keytoolは、信頼できる自己署名証明書を作成します
-
18-09-2019 - |
質問
(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
他のヒント
キーストアには秘密鍵が含まれているため、クライアントとサーバーの間でキーストアを共有することはできません。認証するとき、クライアントはプライベートキーで証明書をスキップします。上記のように、クライアント側にトラストストアを展開する必要があります。
キーストアの証明書は、それらを生成またはインポートする方法に応じて、同じように動作しません。
インポートされた証明書のエントリタイプ(キーストア全体をverboselyにリストすると見られます -list -v
)は「TrustedCertentry」です。生成された証明書のエントリタイプは「privatekeyentry」です。証明書をエクスポートする場合、公開キーとその発行者へのオプションの参照のみをエクスポートするだけです。
キーストアの自己署名証明書をトラストストアの信頼できる証明書としてエクスポートする必要があるようです(ここでは名前が理にかなっています)。
SSL/TLSの実装はおそらくそれをサポートしていないため、私はそれをしません。現実の世界の観点からは、不明瞭なWebサーバーでVerisignから最終的に秘密の秘密鍵を展開してカジュアルなページに署名するようなものです。一方、この秘密鍵の唯一の目的は、安全なままで他の証明書に署名することです。 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
. 。インポートします ca
とともに -trustcacerts
オプションは、ルート証明書になることを意味します。の my-keystore.jks
現在、輸入された各証明書は「信頼できるCertentry」です。これは、公開キーのみがあることを意味します。発行関係は「発行者」分野にのみ表示されますが、輸入時に信頼関係が最も重要だったため、問題ありません。
この時点で my-keystore.jks
新鮮なJREのような信頼できる証明書を含む環境をシミュレートします。 their-keystore.jks
証明書リクエストに署名する権限を持つ証明書の所有者をシミュレートします。
私も:自己署名証明書を作成します e1
の my-keystore.jks
, 、署名してください 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のカカートに依存していないことです。
これが私が実際に言っていることを置くコードです。証明書の取り消しリスト(別の日に残されたトピック)を無効にしている限り、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
あなたはそれをしてはいけません。キーストアは厳密にプライベートです。誰かに漏れている場合は、セキュリティを致命的に侵害しています。それを機能させるためだけにこの種のことをすることには意味がありません。 そうではありません 動作 - それは単なるセキュリティ侵害です。あなたはそれを正しくしなければなりません:サーバーのキーストアからクライアントのトラストストアに、そしてサーバーのキーストアにある場合はクライアントのキーストアからエクスポートします。
理解できません。クライアントと一緒にサーバーキーストアを使用していますか?あなたのユースケースは正確に何ですか?相互認証をセットアップしようとしていますか?
はいの場合、あなたはここで間違った道を歩んでいます。クライアントキーストア(クライアントの自己署名証明書と秘密キー用)とクライアントトラストストア(サーバーの「スタンドアロン」の自己署名証明書、つまり秘密キーのない)が必要です。どちらもサーバーキーストアとは異なります。