简体   繁体   English

Keytool创建受信任的自签名证书

[英]Keytool create a trusted self signed certificate

I am trying to use the (java) keytool to create a self signed certificate but when I attempt to use it I get the following exception (see bottom for entire exception). 我正在尝试使用(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

I know that I can by-pass this with this code: 我知道我可以用这段代码绕过这个:

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) (资源)

But I am not interested in this solutions because I think that it creates a security hole. 但我对这个解决方案不感兴趣,因为我认为它会造成安全漏洞。 (please correct me if I am wrong). (如果我错了,请纠正我)。

Can anyone point me in the right direction? 谁能指出我正确的方向? I am testing locally at the moment right now so it is pretty easy to change things. 我现在正在本地进行测试,因此很容易改变。 I have access to the server code, client code and to the .keystore file. 我可以访问服务器代码,客户端代码和.keystore文件。

Update 更新

I was attempting to use one .keystore file for both the client and server but in hopes of simplifying my issues I have created server.keystore (see below) and client.truststore (see below). 我试图为客户端和服务器使用一个.keystore文件,但为了简化我的问题,我创建了server.keystore(见下文)和client.truststore(见下文)。 I am reasonably confident that the certicates are correct but if someone could verify I would be grateful. 我有理由相信证书是正确的,但如果有人可以证实我会感激。

server.keystore 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 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


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

Update 更新

I thought it could be useful to include the entire exception: 我认为包含整个异常可能很有用:

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

You would need to "establish trust" between your server and client (I'm assuming you only need to do server-side authentication). 您需要在服务器和客户端之间“建立信任”(我假设您只需要进行服务器端身份验证)。 This is because you use self-signed certs. 这是因为您使用自签名证书。 That involves importing your server's cert into the client trust store: 这涉及将服务器的证书导入客户端信任库:

On the server side: 在服务器端:

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

Copy the .cert file over to the client side and then: 将.cert文件复制到客户端,然后:

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

You can't share the keystore between client and server, because the keystore contains the private key. 您无法在客户端和服务器之间共享密钥库,因为密钥库包含私钥。 When authenticating, the client skips the certificates with private keys. 进行身份验证时,客户端会使用私钥跳过证书。 As said above you need to deploy a truststore on client side. 如上所述,您需要在客户端部署信任库。

The certificates in a keystore don't behave the same way, depending on how you generated or imported them. 密钥库中的证书的行为方式不同,具体取决于您生成或导入它们的方式。

An imported certificate's entry type (seen when verbosely listing the whole keystore with -list -v ) is "trustedCertEntry". 导入证书的条目类型(在使用-list -v详细列出整个密钥库时可以看到)是“trustedCertEntry”。 A generated certificate's entry type is "PrivateKeyEntry". 生成的证书的条目类型是“PrivateKeyEntry”。 When you export a certificate, you only export its public key, and an optional reference to its issuer. 导出证书时,只导出其公钥和对其颁发者的可选引用。

Seems like you need to export the self-signed certificate in your keystore as a trusted certificate in your truststore (names make sense here). 您似乎需要将密钥库中的自签名证书导出为信任库中的可信证书(此处的名称有意义)。

I wouldn't do that, because SSL/TLS implementations probably don't support it. 我不这样做,因为SSL / TLS实现可能不支持它。 From a real world perspective it's like deploying the ultimately secret private key from Verisign on some obscure Web server to sign casual pages, while the only purpose of this private key is to remain in a safe and sign other certificates. 从现实世界的角度来看,就像在Verisign上部署一些不起眼的Web服务器上的最终秘密私钥来签署临时页面,而这个私钥的唯一目的是保持安全并签署其他证书。 SSL/TLS implementors probably won't pollute their code with such a use case, and anyways, the "KeyUsage" certificate extension may restrict a certificate usage to signing, preventing encipherment. SSL / TLS实现者可能不会使用这样的用例污染他们的代码,并且无论如何,“KeyUsage”证书扩展可能将证书使用限制为签名,从而防止加密。

That's why I suggest to rebuild a chain of certificates for your test. 这就是为什么我建议为您的测试重建一系列证书。

The keytool documentation contains an interesting part about creating a chain ( -gencert command) but it's a very skeletal example which doesn't cover the keystore-truststore relationship. keytool文档包含一个关于创建链的有趣部分( -gencert命令),但它是一个非常骨架的例子,它不包括keystore-truststore关系。 I've enhanced it to simulate a third-party certification authority. 我已经增强它来模拟第三方证书颁发机构。

A temporary store their-keystore.jks represents a certificate-emitting authority. 临时存储their-keystore.jks表示证书发出权限。 I feed it with a certificate chain of ca2 -> ca1 -> ca with ca being considered as a root certificate. 我用ca2 -> ca1 -> ca的证书链来喂它, ca被认为是根证书。 The chain appears with each non-root certificate (namely ca1 and ca2 ) referencing their issuer as Certificate[2] . 链出现时,每个非根证书(即ca1ca2 )引用其颁发者作为Certificate[2] Please note that every certificate is "PrivateKeyEntry". 请注意,每个证书都是“PrivateKeyEntry”。

Then I feed the my-keystore.jks with those certificates in order: ca , ca1 , ca2 . 然后我按顺序为my-keystore.jks提供这些证书: caca1ca2 I import ca with the -trustcacerts option which means it becomes a root certificate. 我使用-trustcacerts选项导入ca ,这意味着它成为根证书。 In my-keystore.jks each imported certificate now is "trustedCertEntry" which means there is only the public key. my-keystore.jks每个导入的证书现在都是“trustedCertEntry”,这意味着只有公钥。 The issuing relationship only appears in the "Issuer" field but it's OK because the trust relationship mattered most at the time of the import. 发行关系仅出现在“发行人”字段中,但没有问题,因为信任关系在导入时最为重要。

At this point my-keystore.jks simulates an environment containing some trusted certificates, like a fresh JRE. 此时, my-keystore.jks模拟包含一些可信证书的环境,如新鲜的JRE。 The their-keystore.jks simulates the owners of those certificates, who have the power to sign certificate requests. their-keystore.jks模拟这些证书的所有者,他们有权签署证书请求。

So do I : I create a self-signed certificate e1 in my-keystore.jks , get it signed by ca2 (through their-keystore.jks ) and import the signed result back into my-keystore.jks . 我也是这样:我在my-keystore.jks创建一个自签名证书e1 ,由ca2 (通过their-keystore.jks )签名并将签名结果导回my-keystore.jks e1 is still a "PrivateKeyEntry" (because its private key remains in my-keystore.jks ) but now I've built the following chain : e1 -> ca2 -> ca1 . e1仍然是一个“PrivateKeyEntry”(因为它的私钥保留在my-keystore.jks ),但现在我构建了以下链: e1 -> ca2 -> ca1 It seems that ca1 -> ca is implicit with ca being a certification authority. 似乎ca1 -> ca隐含着ca作为证书颁发机构。

To build the truststore I just import certificates ca , ca1 and ca2 the same way I did for my-keystore.jks . 要构建信任库,我只需导入证书caca1ca2 ,就像我对my-keystore.jks Please note I don't import e1 , as I expect the SSL/TLS client to validate it against ca2 . 请注意我不导入e1 ,因为我希望SSL / TLS客户端针对ca2验证它。

I think this gets rather close of how things work in real world. 我认为这与现实世界中的工作方式非常接近。 What's nice here is you have full control on the certificates, and no dependency on JRE's cacerts. 这里有什么好处是你可以完全控制证书,而不依赖于JRE的cacerts。

Here is the code putting what I say in practice. 这是我在实践中所说的代码。 Seems to work with Jetty (client and server) as long as you disable certificate revocation list (a topic left for another day). 似乎可以使用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

You mustn't do that. 你不能这样做。 A keystore is strictly private. 密钥库是严格私密的。 If you leak it to anybody you have fatally compromised security. 如果你把它泄露给任何人,你的安全性会受到严重损害。 There is no point in doing this kind of thing just to get it working, because it isn't working - it is just a security breach. 做这种事只是为了让它工作是没有意义的 ,因为它不起作用 - 它只是一个安全漏洞。 You have to do it right: export from the server's keystore into the client's truststore, and from the client's keystore if any to the server's keystore. 您必须正确执行:从服务器的密钥库导出到客户端的信任库,并从客户端的密钥库(如果有)导出到服务器的密钥库。

I don't get it. 我不明白。 Are you using the server key store with the client? 您是否在客户端使用服务器密钥库? What is your use case exactly? 你的用例究竟是什么? Are you trying to setup mutual authentication? 您是否尝试设置相互身份验证?

If yes, you're on the wrong path here. 如果是的话,你走错了路。 You'll need a client key store (for the client's self-signed certificate and private key) and a client trust store (for the server's "stand-alone" self-signed certificate ie without its private key). 您需要一个客户端密钥存储区(用于客户端的自签名证书和私钥)和一个客户端信任存储区(用于服务器的“独立”自签名证书,即没有其私钥)。 Both are different from the server key store. 两者都与服务器密钥库不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将自签名证书导入keytool? - How to import a self signed certificate into keytool? 受信任的 SSL 证书显示为自签名 - Trusted SSL certificate showing as self signed 我可以在Java中创建自签名证书吗?这些证书将被Web浏览器自动信任? - Can I create self-signed certificate in Java which will be automatically trusted by web browsers? 使用keytool的密钥对和自签名证书之间的区别? - Difference between key pair and self signed certificate using keytool? lotus notes ssl certificate self signed client没有可信任的证书java - lotus notes ssl certificate self signed client no trusted certificate java 如何使用keytool创建证书? - How to create a certificate with keytool? 使用我的自签名证书创建KeyStore实例 - Create a KeyStore instance with my self signed certificate java keytool自签名证书在firefox中使用,以信任更改的DSL家庭IP地址 - java keytool self signed certificate use in firefox to trust changing DSL home IP-addresses 将外部服务器的自签名证书添加到我的Tomcat的受信任证书中 - Adding a foreign server's self-signed certificate to the trusted certificates of my Tomcat 如何在IE可信根证书颁发机构存储中自动安装自签名证书 - How to automatically install self signed certificate in IE Trusted Root Certification Authorities store
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM