【问题标题】:Keytool create a trusted self signed certificateKeytool 创建受信任的自签名证书
【发布时间】:2011-01-13 02:21:45
【问题描述】:

我正在尝试使用 (java) keytool 创建一个自签名证书,但是当我尝试使用它时,我得到了以下异常(整个异常请参见底部)。

...<5 more exceptions above this>
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:203)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
        ... 22 more

我知道我可以用这段代码绕过这个:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;

HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName, SSLSession session) {
        System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

(source)

但我对这种解决方案不感兴趣,因为我认为它会造成安全漏洞。 (如果我错了,请纠正我)。

谁能指出我正确的方向?我现在正在本地测试,所以很容易改变。我可以访问服务器代码、客户端代码和 .keystore 文件。

更新

我试图为客户端和服务器使用一个 .keystore 文件,但为了简化我的问题,我创建了 server.keystore(见下文)和 client.truststore(见下文)。我有理由相信这些证书是正确的,但如果有人可以验证,我将不胜感激。

server.keystore

hostname[username:/this/is/a/path][711]% keytool -list -keystore server.keystore -v
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: hostname
Creation date: Feb 4, 2010
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Serial number: 4b6b0ea7
Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
Certificate fingerprints:
         MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
         SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

client.truststore

hostname[username:/this/is/a/path][713]% keytool -list -keystore client.truststore -v
Enter keystore password:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: mykey
Creation date: Feb 4, 2010
Entry type: trustedCertEntry

Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
Serial number: 4b6b0ea7
Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
Certificate fingerprints:
         MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
         SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************

更新

我认为包含整个异常可能很有用:

javax.xml.soap.SOAPException: java.io.IOException: Could not transmit message
        at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:115)
        at org.jboss.ws.core.soap.SOAPConnectionImpl.call(SOAPConnectionImpl.java:66)
        at com.alcatel.tpapps.common.utils.SOAPClient.execute(SOAPClient.java:193)
        at com.alcatel.tpapps.common.utils.SOAPClient.main(SOAPClient.java:280)
Caused by: java.io.IOException: Could not transmit message
        at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:192)
        at org.jboss.ws.core.client.SOAPRemotingConnection.invoke(SOAPRemotingConnection.java:77)
        at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:106)
        ... 3 more
Caused by: org.jboss.remoting.CannotConnectException: Can not connect http client invoker. sun.security.validator.ValidatorException: No trusted certificate found.
        at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:368)
        at org.jboss.remoting.transport.http.HTTPClientInvoker.transport(HTTPClientInvoker.java:148)
        at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:141)
        at org.jboss.remoting.Client.invoke(Client.java:1858)
        at org.jboss.remoting.Client.invoke(Client.java:718)
        at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:171)
        ... 5 more
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1584)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:877)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1089)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1116)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1100)
        at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
        at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170)
        at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:857)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
        at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:288)
        ... 10 more
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:203)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
        at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
        ... 22 more

【问题讨论】:

    标签: java web-services certificate ssl-certificate keytool


    【解决方案1】:

    我不明白。您是否在客户端使用服务器密钥库?你的用例到底是什么?您是否尝试设置相互身份验证?

    如果是,那么您在这里走错了路。您将需要一个客户端密钥存储(用于客户端的自签名证书和私钥)和一个客户端信任存储(用于服务器的“独立”自签名证书,即没有其私钥)。两者都不同于服务器密钥存储。

    【讨论】:

    • 我认为我可以将服务器的密钥库与客户端一起使用进行测试。这将确保客户端拥有服务器证书。虽然也许客户端确实需要自己的自签名证书...
    • 正如我所说,这取决于您想做什么(我不清楚)。如果你想实现相互认证,客户端也需要它的证书。在这种情况下,我认为您不能将服务器密钥存储用作客户端信任存储。
    【解决方案2】:

    您需要在服务器和客户端之间“建立信任”(我假设您只需要进行服务器端身份验证)。这是因为您使用自签名证书。 这涉及将服务器的证书导入客户端信任库:

    在服务器端:

    keytool -keystore <keystore file> -alias <alias> -export -file <certfilename>.cert
    

    将 .cert 文件复制到客户端,然后:

    keytool -keystore <truststore file> -alias <alias> -import -file <certfilename>.cert
    

    【讨论】:

    • +1:感谢您的明确回答。我很确定这就是我正在做的除了我将客户端和服务器都设置为使用相同的密钥库文件。即:server.keystore。我相信密钥库可以同时充当密钥库和信任库。我使用我的作为服务器和客户端的密钥库和信任库。这合法吗?
    • 我自己没有尝试过该配置,但它可能有效。您必须为密钥材料和证书使用不同的别名,因为它们用于不同的目的(并且两者都必须存在)。您似乎知道此设置仅适用于测试目的,不适用于生产...
    • 意味着我的一个 .keystore 文件中必须有一个trustedCertEntry 和一个privateKeyEntry?如果我仍然遇到问题,我将尝试为客户端创建一个单独的 .keystore 文件。感谢您的帮助。
    • 不确定这是实际问题,但您的 8443 端口连接器中有 clientAuth="true"。这也需要在另一个方向(客户端->服务器)建立信任。你的 443 端口没有那个。你的要求是什么?
    • 好的。那么您可以发布您的客户端代码以便我看一下吗?另外,你能把你的浏览器指向同一个端口吗?你得到了什么? (您应该能够查询您的 Web 服务的 WSDL)
    【解决方案3】:

    你不能那样做。密钥库是严格私有的。如果您将其泄露给任何人,您将严重损害安全性。仅仅为了让它工作而做这种事情是没有意义的,因为它没有工作 - 这只是一个安全漏洞。您必须做正确的事:从服务器的密钥库导出到客户端的信任库,并从客户端的密钥库(如果有)导出到服务器的密钥库。

    【讨论】:

      【解决方案4】:

      您不能在客户端和服务器之间共享密钥库,因为密钥库包含私钥。进行身份验证时,客户端会跳过带有私钥的证书。如上所述,您需要在客户端部署信任库。

      密钥库中的证书的行为方式不同,具体取决于您生成或导入它们的方式。

      导入证书的条目类型(在详细列出使用 -list -v 的整个密钥库时看到)是“trustedCertEntry”。生成的证书的条目类型是“PrivateKeyEntry”。导出证书时,您只导出其公钥,以及对其颁发者的可选引用。

      您似乎需要将密钥库中的自签名证书导出为信任库中的受信任证书(名称在这里有意义)。

      我不会那样做,因为 SSL/TLS 实现可能不支持它。从现实世界的角度来看,这就像在一些不起眼的 Web 服务器上部署来自 Verisign 的最终秘密私钥来签署临时页面,而此私钥的唯一目的是保存在保险箱中并签署其他证书。 SSL/TLS 实现者可能不会用这样的用例污染他们的代码,而且无论如何,“KeyUsage”证书扩展可能会限制证书的使用以进行签名,从而防止加密。

      这就是为什么我建议为您的测试重建一系列证书。

      keytool 文档包含一个关于创建链的有趣部分(-gencert 命令),但它是一个非常简单的示例,没有涵盖 keystore-truststore 关系。我已经对其进行了增强以模拟第三方证书颁发机构。

      一个临时存储their-keystore.jks 代表一个证书颁发机构。我用ca2 -&gt; ca1 -&gt; ca 的证书链提供它,ca 被视为根证书。该链与每个非根证书(即@9​​87654326@ 和ca2)一起出现,将它们的颁发者引用为Certificate[2]。请注意,每个证书都是“PrivateKeyEntry”。

      然后我将这些证书依次提供给my-keystore.jkscaca1ca2。我使用-trustcacerts 选项导入ca,这意味着它成为根证书。在my-keystore.jks 中,每个导入的证书现在都是“trustedCertEntry”,这意味着只有公钥。发行关系仅出现在“Issuer”字段中,但没关系,因为信任关系在导入时最重要。

      此时my-keystore.jks 模拟了一个包含一些受信任证书的环境,例如一个新的 JRE。 their-keystore.jks 模拟这些证书的所有者,他们有权签署证书请求。

      我也是这样:我在my-keystore.jks 中创建了一个自签名证书e1,由ca2 签名(通过their-keystore.jks)并将签名结果导入回my-keystore.jkse1 仍然是“PrivateKeyEntry”(因为它的私钥保留在 my-keystore.jks 中)但现在我已经构建了以下链:e1 -&gt; ca2 -&gt; ca1ca1 -&gt; ca 似乎是隐含的,ca 是证书颁发机构。

      要构建信任库,我只需导入证书 caca1ca2,就像我为 my-keystore.jks 所做的那样。请注意,我没有导入 e1,因为我希望 SSL/TLS 客户端根据 ca2 对其进行验证。

      我认为这与现实世界中事物的运作方式非常接近。这里的好处是您可以完全控制证书,而不依赖于 JRE 的 cacerts。

      这是将我所说的内容付诸实践的代码。只要您禁用证书吊销列表,似乎就可以与 Jetty(客户端和服务器)一起使用(这个话题留到另一天)。

      #!/bin/bash
      
      rm  their-keystore.jks 2> /dev/null
      rm  my-keystore.jks    2> /dev/null
      rm  my-truststore.jks  2> /dev/null
      
      echo "===================================================="
      echo "Creating fake third-party chain ca2 -> ca1 -> ca ..."
      echo "===================================================="
      
      keytool -genkeypair -alias ca  -dname cn=ca                           \
        -validity 10000 -keyalg RSA -keysize 2048                           \
        -ext BasicConstraints:critical=ca:true,pathlen:10000                \
        -keystore their-keystore.jks -keypass Keypass -storepass Storepass
      
      keytool -genkeypair -alias ca1 -dname cn=ca1                          \
        -validity 10000 -keyalg RSA -keysize 2048                           \
        -keystore their-keystore.jks -keypass Keypass -storepass Storepass
      
      keytool -genkeypair -alias ca2 -dname cn=ca2                          \
        -validity 10000 -keyalg RSA -keysize 2048                           \
        -keystore their-keystore.jks -keypass Keypass -storepass Storepass
      
      
        keytool -certreq -alias ca1                                            \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
      | keytool -gencert -alias ca                                             \
          -ext KeyUsage:critical=keyCertSign                                   \
          -ext SubjectAlternativeName=dns:ca1                                  \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
      | keytool -importcert -alias ca1                                         \
          -keystore   their-keystore.jks -keypass Keypass -storepass Storepass
      
      #echo "Debug exit" ; exit 0
      
        keytool -certreq -alias ca2                                           \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
      | keytool -gencert -alias ca1                                           \
          -ext KeyUsage:critical=keyCertSign                                  \
          -ext SubjectAlternativeName=dns:ca2                                 \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
      | keytool -importcert -alias ca2                                        \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass
      
      keytool -list -v -storepass Storepass -keystore their-keystore.jks
      
      
      echo  "===================================================================="
      echo  "Fake third-party chain generated. Now generating my-keystore.jks ..."
      echo  "===================================================================="
      read -p "Press a key to continue."
      
      # Import authority's certificate chain
      
        keytool -exportcert -alias ca                                         \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
      | keytool -importcert -trustcacerts -noprompt -alias ca                 \
          -keystore  my-keystore.jks -keypass Keypass -storepass Storepass
      
        keytool -exportcert -alias ca1                                        \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
      | keytool -importcert -noprompt -alias ca1                              \
          -keystore  my-keystore.jks -keypass Keypass -storepass Storepass
      
        keytool -exportcert -alias ca2                                        \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
      | keytool -importcert -noprompt -alias ca2                              \
          -keystore  my-keystore.jks -keypass Keypass -storepass Storepass
      
      # Create our own certificate, the authority signs it.
      
      keytool -genkeypair -alias e1  -dname cn=e1                        \
        -validity 10000 -keyalg RSA -keysize 2048                        \
        -keystore my-keystore.jks -keypass Keypass -storepass Storepass
      
        keytool -certreq -alias e1                                            \
          -keystore my-keystore.jks -keypass Keypass -storepass Storepass     \
      | keytool -gencert -alias ca2                                           \
          -ext SubjectAlternativeName=dns:localhost                           \
          -ext KeyUsage:critical=keyEncipherment,digitalSignature             \
          -ext ExtendedKeyUsage=serverAuth,clientAuth                         \
          -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
      | keytool -importcert -alias e1                                         \
          -keystore my-keystore.jks -keypass Keypass -storepass Storepass
      
      keytool -list -v  -storepass Storepass -keystore  my-keystore.jks
      
      echo "================================================="
      echo "Keystore generated. Now generating truststore ..."
      echo "================================================="
      read -p "Press a key to continue."
      
        keytool -exportcert -alias ca                                        \
          -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
      | keytool -importcert -trustcacerts -noprompt -alias ca                \
          -keystore my-truststore.jks -keypass Keypass -storepass Storepass
      
        keytool -exportcert -alias ca1                                       \
          -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
      | keytool -importcert -noprompt -alias ca1                             \
          -keystore my-truststore.jks -keypass Keypass -storepass Storepass
      
        keytool -exportcert -alias ca2                                       \
          -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
      | keytool -importcert -noprompt -alias ca2                             \
          -keystore my-truststore.jks -keypass Keypass -storepass Storepass
      
      keytool -list -v  -storepass Storepass -keystore  my-truststore.jks
      
      rm  their-keystore.jks 2> /dev/null
      

      【讨论】:

      • 整个答案基于自签名证书不起作用和/或不受支持和/或不安全地泄漏私钥的谬论。由于这些都不是真的,答案是毫无意义的,也没有解决所提出的问题。
      • 加油@EJP 我在我的脚本中使用自签名证书,所以我认为它们确实有效。我对 sixtyfootersdude 的建议是尝试用一个易于理解的信任链重现他的问题。
      • 自己动手吧。您在第五段中所说是“SSL/TLS 实现可能不支持它”,随后的评论似乎暗示您认为证书包含私钥。
      • 不,你误解了重点。当我写“这就像在一些不起眼的 Web 服务器上部署来自 Verisign 的最终秘密私钥来签署临时页面”时,并没有涉及到信任库。我清楚地提到了“KeyUsage”扩展,它设置了证书颁发机构和域名身份验证之间的区别。这都是关于证书签名的(如果您愿意,可以在“密钥库方面”)并且不涉及信任库。 [继续...]
      • [...continuing] 在 Java 中运行 HTTPS 客户端时,由于信任库仅导入用于域名身份验证的自签名证书而失败。但是在导入自签名证书后,对域名认证进行签名就成功了。
      【解决方案5】:
      Springboot 2.1.5 , java 1.8, keytool(it is part of JDK 8) 
      
      these are the steps to follow to generate the self signed ssl certifcate in spring boot
      
      
      1. Generate self signed ssl certificate
      
      keytool -genkeypair -alias tomcat -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650
      
      ex: D:\example> <here run the above command>
      
      if it is not working then make sure that your java bin path is set at environment variables to the PATH variable as
      
      C:\Program Files\Java\jdk1.8.0_191\bin
      
      2. after key generation has done then copy that file in to the src/main/resources folder in your project
      
      3. add key store properties in applicaiton.properties 
      
      server.port: 8443
      server.ssl.key-store:classpath:keystore.p12
      server.ssl.key-store-password: test123     # change the pwd
      server.ssl.keyStoreType: PKCS12
      server.ssl.keyAlias: tomcat
      
      3. change your postman ssl verification settings to turn OFF
          go to settings and select the  ssl verification  to turn off
      
      now verify the url ( your applicaiton url)
      https://localhost:8443/test/hello
      

      【讨论】:

        猜你喜欢
        • 2011-09-28
        • 2016-12-30
        • 2013-02-08
        • 2018-04-16
        • 2012-04-23
        • 2017-04-21
        • 2015-10-28
        • 1970-01-01
        • 2015-11-02
        相关资源
        最近更新 更多