[英]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.