-
Notifications
You must be signed in to change notification settings - Fork 1.8k
KB: TLS with OPTIONAL mutualAuthentication
What happens when a member has a TLS mutual authentication configured to OPTIONAL
and a client configures an untrusted certificate?
Server:
<ssl enabled="true">
<properties>
<property name="protocol">TLSv1.3</property>
<property name="keyStore">server.keystore</property>
<property name="keyStorePassword">123456</property>
<property name="trustStore">server.truststore</property>
<property name="trustStorePassword">123456</property>
<property name="mutualAuthentication">OPTIONAL</property>
</properties>
</ssl>
Client
<ssl enabled="true">
<properties>
<property name="protocol">TLSv1.3</property>
<property name="keyStore">untrusted.keystore</property>
<property name="keyStorePassword">123456</property>
<property name="trustStore">client.truststore</property>
<property name="trustStorePassword">123456</property>
</properties>
</ssl>
The client is still able to connect to the server.
You can check what's happening during the TLS handshake by enabling the debug output:
Member:
export JAVA_OPTS="-Djavax.net.debug=ssl:handshake"
bin/hz start
Client:
export JAVA_OPTS="-Djavax.net.debug=ssl:handshake"
bin/hz-cli sql -v
You can find the following info in the member output.
Client starts the TLS handshake with the ClientHello
message:
javax.net.ssl|DEBUG|23|hz.flamboyant_cannon.cached.thread-1|2023-07-19 09:40:37.776 CEST|ClientHello.java:798|Consuming ClientHello handshake message (
"ClientHello": {
"client version" : "TLSv1.2",
"random" : "84 81 56 60 FF 50 54 DF A2 B8 31 DD 84 9C 59 6E 40 42 91 92 63 5D C4 E9 77 F4 78 80 FE CC FE 6A",
"session id" : "EE 9C 8D FA 8A 6A 58 23 28 81 EC E1 00 FE EF E9 52 DA B6 8D 9C F1 1B 06 6E 0F 3F 11 4C C7 14 8F",
"cipher suites" : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_CHACHA20_POLY1305_SHA256(0x1303)]",
"compression methods" : "00",
"extensions" : [
"status_request (5)": {
"certificate status type": ocsp
"OCSP status request": {
"responder_id": <empty>
"request extensions": {
<empty>
}
}
},
"supported_groups (10)": {
"versions": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
},
"signature_algorithms (13)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha1, rsa_pkcs1_sha1]
},
"signature_algorithms_cert (50)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha1, rsa_pkcs1_sha1]
},
"supported_versions (43)": {
"versions": [TLSv1.3]
},
"psk_key_exchange_modes (45)": {
"ke_modes": [psk_dhe_ke]
},
"key_share (51)": {
"client_shares": [
{
"named group": x25519
"key_exchange": {
0000: 30 35 F0 3A 98 AC 96 8F 0A 7E B0 42 D2 FD A6 0E 05.:.......B....
0010: 13 A2 DE 90 03 32 C4 63 22 2E 1E C2 B5 12 CD 2B .....2.c"......+
}
},
]
}
]
}
)
Server responds with the ServerHello
followed by EncryptedExtensions
and the CertificateRequest
:
javax.net.ssl|DEBUG|23|hz.flamboyant_cannon.cached.thread-1|2023-07-19 09:40:37.800 CEST|ServerHello.java:572|Produced ServerHello handshake message (
"ServerHello": {
"server version" : "TLSv1.2",
"random" : "E5 CC F7 97 65 08 DE 78 A9 A2 F1 7F 83 E4 F4 1A 33 E3 FC 45 67 98 55 DB 61 9B 5E 89 65 EE C3 12",
"session id" : "EE 9C 8D FA 8A 6A 58 23 28 81 EC E1 00 FE EF E9 52 DA B6 8D 9C F1 1B 06 6E 0F 3F 11 4C C7 14 8F",
"cipher suite" : "TLS_AES_256_GCM_SHA384(0x1302)",
"compression methods" : "00",
"extensions" : [
"supported_versions (43)": {
"selected version": [TLSv1.3]
},
"key_share (51)": {
"server_share": {
"named group": x25519
"key_exchange": {
0000: 2F 97 67 66 63 D3 EC 0E 01 7E 6D 5F BC B1 DD A9 /.gfc.....m_....
0010: DC AB 71 5A E0 4D 24 1D F0 02 8C BD 5B 11 9B 66 ..qZ.M$.....[..f
}
},
}
]
}
)
...
javax.net.ssl|DEBUG|23|hz.flamboyant_cannon.cached.thread-1|2023-07-19 09:40:37.818 CEST|EncryptedExtensions.java:137|Produced EncryptedExtensions message (
"EncryptedExtensions": [
"supported_groups (10)": {
"versions": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
}
]
)
javax.net.ssl|DEBUG|23|hz.flamboyant_cannon.cached.thread-1|2023-07-19 09:40:37.824 CEST|CertificateRequest.java:927|Produced CertificateRequest message (
"CertificateRequest": {
"certificate_request_context": "",
"extensions": [
"signature_algorithms (13)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha1, rsa_pkcs1_sha1]
},
"signature_algorithms_cert (50)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha1, rsa_pkcs1_sha1]
},
"certificate_authorities (47)": {
"certificate authorities": [
CN=server
CN=client]
}
]
}
)
The CertificateRequest
message contains the certificate_authorities
extension, which serves as a base for client when selecting which key to send to the server.
Then the server sends other messages - its Certificate
, CertificateVerify
, and Finished
.
As the certificate was requested by the client, it responds with CertificateRequest
, but there were no matching certificate authority, so the message content is empty:
javax.net.ssl|DEBUG|23|hz.flamboyant_cannon.cached.thread-1|2023-07-19 09:40:37.975 CEST|CertificateMessage.java:1178|Consuming client Certificate handshake message (
"Certificate": {
"certificate_request_context": "",
"certificate_list": [
]
}
)
Server knows the mutual authentication is optional, so empty certificate list is accepted.
When the client has a proper certificate matching (issued by or equal to) any of the certificate authorities supported by server, it's send in its Certificate
message followed by CertificateVerify
.
javax.net.ssl|DEBUG|25|hz.competent_burnell.cached.thread-3|2023-07-19 09:55:12.783 CEST|CertificateMessage.java:1178|Consuming client Certificate handshake message (
"Certificate": {
"certificate_request_context": "",
"certificate_list": [
{
"certificate" : {
"version" : "v3",
"serial number" : "2D 3E 11 C5",
"signature algorithm": "SHA256withRSA",
"issuer" : "CN=client",
"not before" : "2023-05-17 08:24:47.000 CEST",
"not after" : "2043-05-12 08:24:47.000 CEST",
"subject" : "CN=client",
"subject public key" : "RSA",
"extensions" : [
{
ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 7D 05 E2 FD CA 69 1D 75 98 CA C5 5B 61 2E 29 7A .....i.u...[a.)z
0010: 2C B7 CF 07 ,...
]
]
}
]}
"extensions": {
<no extension>
}
},
]
}
)
javax.net.ssl|DEBUG|25|hz.competent_burnell.cached.thread-3|2023-07-19 09:55:12.795 CEST|CertificateVerify.java:1163|Consuming CertificateVerify handshake message (
"CertificateVerify": {
"signature algorithm": rsa_pss_rsae_sha256
"signature": {
0000: 18 A5 94 64 52 12 84 7C 21 F7 5E D8 53 90 BF BA ...dR...!.^.S...
...
After the server receives the empty Certificate
message from the client, it responds with the bad_certificate
alert message - meaning the TLS handshake failed:
javax.net.ssl|DEBUG|25|hz.elegant_proskuriakova.cached.thread-3|2023-07-19 10:00:10.549 CEST|CertificateMessage.java:1178|Consuming client Certificate handshake message (
"Certificate": {
"certificate_request_context": "",
"certificate_list": [
]
}
)
javax.net.ssl|ERROR|25|hz.elegant_proskuriakova.cached.thread-3|2023-07-19 10:00:10.550 CEST|TransportContext.java:345|Fatal (BAD_CERTIFICATE): Empty client certificate chain (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Empty client certificate chain
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:340)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:296)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:287)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1194)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1181)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008)
at com.hazelcast.internal.nio.ssl.TLSExecutor$HandshakeTask.run(TLSExecutor.java:73)
at com.hazelcast.internal.util.executor.CachedExecutorServiceDelegate$Worker.run(CachedExecutorServiceDelegate.java:217)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
at com.hazelcast.internal.util.executor.HazelcastManagedThread.executeRun(HazelcastManagedThread.java:76)
at com.hazelcast.internal.util.executor.HazelcastManagedThread.run(HazelcastManagedThread.java:111)}
)