[英]Keytool create a trusted self signed certificate
我正在尝试使用(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);
但我对这个解决方案不感兴趣,因为我认为它会造成安全漏洞。 (如果我错了,请纠正我)。
谁能指出我正确的方向? 我现在正在本地进行测试,因此很容易改变。 我可以访问服务器代码,客户端代码和.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
您需要在服务器和客户端之间“建立信任”(我假设您只需要进行服务器端身份验证)。 这是因为您使用自签名证书。 这涉及将服务器的证书导入客户端信任库:
在服务器端:
keytool -keystore <keystore file> -alias <alias> -export -file <certfilename>.cert
将.cert文件复制到客户端,然后:
keytool -keystore <truststore file> -alias <alias> -import -file <certfilename>.cert
您无法在客户端和服务器之间共享密钥库,因为密钥库包含私钥。 进行身份验证时,客户端会使用私钥跳过证书。 如上所述,您需要在客户端部署信任库。
密钥库中的证书的行为方式不同,具体取决于您生成或导入它们的方式。
导入证书的条目类型(在使用-list -v
详细列出整个密钥库时可以看到)是“trustedCertEntry”。 生成的证书的条目类型是“PrivateKeyEntry”。 导出证书时,只导出其公钥和对其颁发者的可选引用。
您似乎需要将密钥库中的自签名证书导出为信任库中的可信证书(此处的名称有意义)。
我不这样做,因为SSL / TLS实现可能不支持它。 从现实世界的角度来看,就像在Verisign上部署一些不起眼的Web服务器上的最终秘密私钥来签署临时页面,而这个私钥的唯一目的是保持安全并签署其他证书。 SSL / TLS实现者可能不会使用这样的用例污染他们的代码,并且无论如何,“KeyUsage”证书扩展可能将证书使用限制为签名,从而防止加密。
这就是为什么我建议为您的测试重建一系列证书。
keytool文档包含一个关于创建链的有趣部分( -gencert
命令),但它是一个非常骨架的例子,它不包括keystore-truststore关系。 我已经增强它来模拟第三方证书颁发机构。
临时存储their-keystore.jks
表示证书发出权限。 我用ca2 -> ca1 -> ca
的证书链来喂它, ca
被认为是根证书。 链出现时,每个非根证书(即ca1
和ca2
)引用其颁发者作为Certificate[2]
。 请注意,每个证书都是“PrivateKeyEntry”。
然后我按顺序为my-keystore.jks
提供这些证书: ca
, ca1
, ca2
。 我使用-trustcacerts
选项导入ca
,这意味着它成为根证书。 在my-keystore.jks
每个导入的证书现在都是“trustedCertEntry”,这意味着只有公钥。 发行关系仅出现在“发行人”字段中,但没有问题,因为信任关系在导入时最为重要。
此时, my-keystore.jks
模拟包含一些可信证书的环境,如新鲜的JRE。 their-keystore.jks
模拟这些证书的所有者,他们有权签署证书请求。
我也是这样:我在my-keystore.jks
创建一个自签名证书e1
,由ca2
(通过their-keystore.jks
)签名并将签名结果导回my-keystore.jks
。 e1
仍然是一个“PrivateKeyEntry”(因为它的私钥保留在my-keystore.jks
),但现在我构建了以下链: e1 -> ca2 -> ca1
。 似乎ca1 -> ca
隐含着ca
作为证书颁发机构。
要构建信任库,我只需导入证书ca
, ca1
和ca2
,就像我对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
你不能这样做。 密钥库是严格私密的。 如果你把它泄露给任何人,你的安全性会受到严重损害。 做这种事只是为了让它工作是没有意义的 ,因为它不起作用 - 它只是一个安全漏洞。 您必须正确执行:从服务器的密钥库导出到客户端的信任库,并从客户端的密钥库(如果有)导出到服务器的密钥库。
我不明白。 您是否在客户端使用服务器密钥库? 你的用例究竟是什么? 您是否尝试设置相互身份验证?
如果是的话,你走错了路。 您需要一个客户端密钥存储区(用于客户端的自签名证书和私钥)和一个客户端信任存储区(用于服务器的“独立”自签名证书,即没有其私钥)。 两者都与服务器密钥库不同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.