AWSでホストしており、すべてのエンドポイントは、1.2未満のTLSを許可しない事前定義されたセキュリティポリシーを使用しています(つまり、ELBの場合は TLS-1-2-2017-01 または TLSv1)。 2_2018 CloudFrontの場合)。
お客様の1人が、Javaクライアントからエンドポイントへの接続がSSLHandshakeException
で失敗することを訴え、_TLSv1.2
_がクライアントで有効になっていることを確認しました。
さらなる調査により、クライアントはレガシーの理由で_SSLv2Hello
_も有効になっていることが示されました。無効にすると問題は解決しましたが、そもそもなぜ問題だったのかよくわかりません。
実験によると、この問題はAWSエンドポイントにのみ発生します。AWSでホストされているさまざまなウェブサイトをELBとCloudfrontで試したところ、このエラーが発生しました。 AWSでホストされているが、他のSSLソリューション(LetsEncryptなど)を使用しているサイトにはこの問題はなく、他の場所でホストされているWebサイトもありません。
これは、実験に使用した単純なJavaスニペットです。
_import Java.net.HttpURLConnection;
import Java.net.URL;
import Java.util.HashMap;
public class TestConnect {
public static void main(String[] args) {
String url="https://alertlogic.com"; // hosted on AWS behind ELB - failure
// String url="https://myx.ostankin.net"; // hosted on AWS, but using LetsEncrypt - ok
// String url="https://google.com"; // Not hosted on AWS - ok
System.setProperty("https.protocols", "TLSv1.2,SSLv2Hello");
// System.setProperty("https.protocols", "TLSv1.2");
try {
URL restURL = new URL(url);
HttpURLConnection conn = (HttpURLConnection) restURL.openConnection();
conn.setRequestMethod("GET");
int responseCode = conn.getResponseCode();
String responseMessage = conn.getResponseMessage();
System.out.println("Response:");
System.out.println(responseMessage);
} catch(Exception e) {
e.printStackTrace();
}
}
}
_
指定された構成で実行すると、次の例外を除いて失敗します。
_javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at Sun.security.ssl.Alerts.getSSLException(Alerts.Java:192)
at Sun.security.ssl.Alerts.getSSLException(Alerts.Java:154)
at Sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.Java:2023)
at Sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.Java:1125)
at Sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.Java:1375)
at Sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.Java:1403)
at Sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.Java:1387)
at Sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.Java:559)
at Sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.Java:185)
at Sun.net.www.protocol.http.HttpURLConnection.followRedirect0(HttpURLConnection.Java:2701)
at Sun.net.www.protocol.http.HttpURLConnection.followRedirect(HttpURLConnection.Java:2623)
at Sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.Java:1806)
at Sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.Java:1474)
at Java.net.HttpURLConnection.getResponseCode(HttpURLConnection.Java:480)
at Sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.Java:338)
at TestConnect.main(TestConnect.Java:17)
_
_-Djavax.net.debug=ssl:handshake
_で実行すると、かなり長い出力が得られますが、すべての初期化をカットすると、次のように終了します。
_main, READ: TLSv1.2 Handshake, length = 333
*** ECDH ServerKeyExchange
Signature Algorithm SHA1withRSA
Server key: Sun EC public key, 256 bits
public x coord: 21436757520046799441414055942148383722667165679440737888092161490006597177581
public y coord: 15404188333633930557065698234035214201299701177286160881126576519412706199661
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
main, READ: TLSv1.2 Handshake, length = 4
*** ServerHelloDone
*** ECDHClientKeyExchange
ECDH Public value: { 4, 244, 123, 66, 147, 148, 50, 164, 198, 11, 116, 147, 132, 129, 249, 46, 144, 69, 1, 121, 111, 104, 31, 233, 10, 114, 250, 76, 140, 98, 224, 1, 117, 35, 0, 227, 49, 251, 226, 190, 74, 117, 251, 22, 229, 1, 140, 173, 234, 126, 79, 206, 67, 169, 150, 47, 135, 212, 84, 98, 225, 148, 102, 145, 220 }
main, WRITE: TLSv1.2 Handshake, length = 70
SESSION KEYGEN:
PreMaster Secret:
0000: FB 1D 4C 57 26 1E 10 D9 57 2A 37 71 D8 B0 7A 4A ..LW&...W*7q..zJ
0010: 1D 99 75 85 24 90 28 19 E4 B3 58 31 6E 02 B8 4A ..u.$.(...X1n..J
CONNECTION KEYGEN:
Client Nonce:
0000: 5C 05 06 08 F6 7C 3F 5C 73 57 F0 E4 D0 A8 90 11 \.....?\sW......
0010: 3C D1 AB 9A 69 B6 4D A0 F3 13 6C 27 F5 09 80 9B <...i.M...l'....
Server Nonce:
0000: E7 47 9B 66 69 81 2F 03 93 AE 49 BC 3C A7 97 6C .G.fi./...I.<..l
0010: 3D D2 2E 08 5A C6 74 65 AB 97 87 DE 5F 2C E3 E1 =...Z.te...._,..
Master Secret:
0000: 99 E8 51 8A 8D A5 56 56 09 42 31 3B EA A6 25 B0 ..Q...VV.B1;..%.
0010: 69 73 EB 33 56 64 57 D1 10 86 55 1C 51 86 B2 23 is.3VdW...U.Q..#
0020: 89 51 FF 42 41 BB D5 50 FD BC EF 9B E0 10 38 3F .Q.BA..P......8?
... no MAC keys used for this cipher
Client write key:
0000: 4C 86 23 06 D6 FA 06 BD 3C EE A2 CE 28 1F 77 9A L.#.....<...(.w.
Server write key:
0000: 1D 93 C4 5B 2A 6D 74 F8 1C 51 23 7E 5C BA DA A9 ...[*mt..Q#.\...
Client write IV:
0000: DF 88 94 CB ....
Server write IV:
0000: 5C 43 CA 31 \C.1
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data: { 7, 235, 79, 105, 125, 222, 241, 252, 104, 122, 143, 122 }
***
main, WRITE: TLSv1.2 Handshake, length = 40
main, READ: TLSv1.2 Change Cipher Spec, length = 1
main, READ: TLSv1.2 Handshake, length = 40
*** Finished
verify_data: { 177, 236, 151, 88, 195, 59, 82, 200, 25, 135, 145, 190 }
***
%% Cached client session: [Session-1, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
main, WRITE: TLSv1.2 Application Data, length = 178
main, READ: TLSv1.2 Application Data, length = 540
main, called close()
main, called closeInternal(true)
main, SEND TLSv1.2 ALERT: warning, description = close_notify
main, WRITE: TLSv1.2 Alert, length = 26
main, called closeSocket(true)
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
main, setSoTimeout(0) called
%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie: GMT: 1543833097 bytes = { 240, 7, 191, 159, 152, 93, 95, 120, 98, 108, 183, 39, 146, 177, 15, 17, 250, 215, 164, 123, 155, 232, 211, 112, 149, 107, 76, 139 }
Session ID: {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, secp384r1, secp521r1, sect283k1, sect283r1, sect409k1, sect409r1, sect571k1, sect571r1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Extension server_name, server_name: [type=Host_name (0), value=www.alertlogic.com]
***
main, WRITE: TLSv1.2 Handshake, length = 194
main, WRITE: SSLv2 client hello message, length = 140
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1.2 ALERT: fatal, handshake_failure
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
_
したがって、基本的にハンドシェイクは通常TLSv1.2を使用して開始されますが、その後、説明なしで突然失敗します。
次のいずれかにより、スニペットが失敗するのを防ぎます。
url
をAWS以外のエンドポイントに置き換えます。SSLv2Hello
_を削除します。System.setProperty("https.protocols", ...)
関数を完全にコメントアウトします。_SSLv2Hello
_の存在がハンドシェイクを壊す理由、AWS固有である理由、セキュリティを損なうことなくこの問題を解決する方法はありますか?
この動作はJava 7およびおそらく他のSSL/TLSライブラリの意図的な機能)のようです。
この投稿はあなたにもう少し洞察を与えるでしょう: Java7とSSLv2Helloの死 。基本的に、Oracleは、SSLv2Helloが完全に強制終了され、他のユーザー(AWS)が廃止されたハンドシェイクの処理に追随することを確認したいと考えています。
お役に立てば幸いです:)