Skip to content

KB: TLS with OPTIONAL mutualAuthentication

Josef Cacek edited this page Jul 19, 2023 · 2 revisions

Question

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>

Answer

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.

What's different when the client certificate is trusted?

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...
...

What if mutualAuthentication is REQUIRED and the client doesn't have trusted certificate?

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)}

)

Resources