/* * Copyright © 2020, 2021, 2022, 2023 Peter Doornbosch * * This file is part of Agent15, an implementation of TLS 1.3 in Java. * * Agent15 is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) * any later version. * * Agent15 is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package net.luminis.tls.handshake; import net.luminis.tls.*; import net.luminis.tls.alert.*; import net.luminis.tls.extension.*; import net.luminis.tls.util.ByteUtils; import net.luminis.tls.util.FieldSetter; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.mockito.internal.util.reflection.FieldReader; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; import java.nio.ByteBuffer; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PSSParameterSpec; import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import static net.luminis.tls.TlsConstants.CipherSuite.*; import static net.luminis.tls.TlsConstants.SignatureScheme.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.*; class TlsClientEngineTest extends EngineTest { private TlsClientEngine engine; private ECPublicKey publicKey; private ClientMessageSender messageSender; @BeforeEach void initObjectUnderTest() { messageSender = Mockito.mock(ClientMessageSender.class); engine = new TlsClientEngine(messageSender, Mockito.mock(TlsStatusEventHandler.class)); engine.setServerName("server"); engine.addSupportedCiphers(List.of(TLS_AES_128_GCM_SHA256)); publicKey = KeyUtils.generatePublicKey(); } @Test void serverHelloShouldContainMandatoryExtensions() { ServerHello serverHello = new ServerHello(TLS_AES_128_CCM_8_SHA256); Assertions.assertThatThrownBy(() -> engine.received(serverHello, ProtectionKeysType.None) ).isInstanceOf(MissingExtensionAlert.class); } @Test void serverHelloShouldContainSupportedVersionExtension() { ServerHello serverHello = new ServerHello(TLS_AES_128_CCM_8_SHA256, List.of(new ServerPreSharedKeyExtension())); Assertions.assertThatThrownBy(() -> engine.received(serverHello, ProtectionKeysType.None) ).isInstanceOf(MissingExtensionAlert.class); } @Test void serverHelloSupportedVersionExtensionShouldContainRightVersion() throws Exception { SupportedVersionsExtension supportedVersionsExtension = new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello); FieldSetter.setField(supportedVersionsExtension, supportedVersionsExtension.getClass().getDeclaredField("tlsVersion"), (short) 0x0303); ServerHello serverHello = new ServerHello(TLS_AES_128_CCM_8_SHA256, List.of(new ServerPreSharedKeyExtension(), supportedVersionsExtension)); Assertions.assertThatThrownBy(() -> engine.received(serverHello, ProtectionKeysType.None) ).isInstanceOf(IllegalParameterAlert.class); } @Test void serverHelloShouldContainPreSharedKeyOrKeyShareExtension() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_CCM_8_SHA256, List.of(new ServerPreSharedKeyExtension())); Assertions.assertThatThrownBy(() -> engine.received(serverHello, ProtectionKeysType.None) ).isInstanceOf(TlsProtocolException.class); } @Test void serverHelloShouldNotContainOtherExtensions() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello), new ServerNameExtension("server"))); Assertions.assertThatThrownBy(() -> engine.received(serverHello, ProtectionKeysType.None) ).isInstanceOf(IllegalParameterAlert.class); } @Test void engineAcceptsCorrectServerHello() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); } @Test void serverHelloShouldContainCipherThatClientOffered() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_256_GCM_SHA384, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new ServerPreSharedKeyExtension())); Assertions.assertThatThrownBy(() -> engine.received(serverHello, ProtectionKeysType.None) ).isInstanceOf(IllegalParameterAlert.class); } @Test void afterProperServerHelloSelectedCipherIsAvailable() throws Exception { // Given engine.startHandshake(); Assertions.assertThatThrownBy(() -> engine.getSelectedCipher() ).isInstanceOf(IllegalStateException.class); // When ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); // Then Assertions.assertThat(engine.getSelectedCipher()).isEqualTo(TLS_AES_128_GCM_SHA256); } @Test void afterProperServerHelloTrafficSecretsAreAvailable() throws Exception { // Given engine.startHandshake(); // When ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); // Then Assertions.assertThat(engine.getClientHandshakeTrafficSecret()) .isNotNull() .hasSizeGreaterThan(12); } @Test void encryptedExtensionsShouldNotBeReceivedBeforeServerHello() throws Exception { // Given engine.startHandshake(); Assertions.assertThatThrownBy(() -> engine.received(new EncryptedExtensions(Collections.emptyList()), ProtectionKeysType.Handshake) ).isInstanceOf(UnexpectedMessageAlert.class); } @Test void encryptedExtensionsShouldNotContainExtensionNotOfferedByClient() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); Assertions.assertThatThrownBy(() -> engine.received(new EncryptedExtensions(List.of(new DummyExtension())), ProtectionKeysType.Handshake) ).isInstanceOf(UnsupportedExtensionAlert.class); } @Test void encryptedExtensionsShouldNotContainDuplicateTypes() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); Assertions.assertThatThrownBy(() -> engine.received(new EncryptedExtensions(List.of( new ServerNameExtension("server"), new ServerNameExtension("server") )), ProtectionKeysType.Handshake) ).isInstanceOf(UnsupportedExtensionAlert.class); } @Test void certificateMessageShouldNotBeReceivedBeforeEncryptedExtensions() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); // When, no Encrypted Extensions Message received // Then Assertions.assertThatThrownBy(() -> engine.received(new CertificateMessage(), ProtectionKeysType.Handshake) ).isInstanceOf(UnexpectedMessageAlert.class); } @Test void serverCertificateMessageRequestContextShouldBeEmpty() throws Exception { handshakeUpToCertificate(); X509Certificate cert = Mockito.mock(X509Certificate.class); Mockito.when(cert.getEncoded()).thenReturn(new byte[300]); CertificateMessage certificateMessage = new CertificateMessage(new byte[4], cert); Assertions.assertThatThrownBy(() -> engine.received(certificateMessage, ProtectionKeysType.Handshake) ).isInstanceOf(IllegalParameterAlert.class); } @Test void serverCertificateMessageShouldAlwaysContainAtLeastOneCertificate() throws Exception { handshakeUpToCertificate(); CertificateMessage certificateMessage = new CertificateMessage(); byte[] emptyCertificateMessageData = ByteUtils.hexToBytes("0b000009" + "00" + "000005" + "0000000000"); certificateMessage.parse(ByteBuffer.wrap(emptyCertificateMessageData)); Assertions.assertThatThrownBy(() -> engine.received(certificateMessage, ProtectionKeysType.Handshake) ).isInstanceOf(IllegalParameterAlert.class); } @Test void certificateVerifyShouldNotBeReceivedBeforeCertificateMessage() throws Exception { // Given handshakeUpToCertificate(); // When, no Certificate Message received // Then Assertions.assertThatThrownBy(() -> engine.received(new CertificateVerifyMessage(), ProtectionKeysType.Handshake) ).isInstanceOf(UnexpectedMessageAlert.class); } @Test void certificateVerifySignatureSchemeShouldMatch() throws Exception { // Given handshakeUpToCertificate(List.of(TlsConstants.SignatureScheme.ecdsa_secp256r1_sha256)); Certificate certificate = CertificateUtils.inflateCertificate(encodedCertificate); engine.received(new CertificateMessage((X509Certificate) certificate), ProtectionKeysType.Handshake); Assertions.assertThatThrownBy(() -> // When engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, new byte[0]), ProtectionKeysType.Handshake)) // Then .isInstanceOf(IllegalParameterAlert.class); } @Test void validSignatureShouldPassValidation() throws Exception { // Given engine.setHostnameVerifier(createNoOpHostnameVerifier()); engine.setTrustManager(createNoOpTrustManager()); byte[] validSignature = createServerSignature(); handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); // When engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, validSignature), ProtectionKeysType.Handshake); // Then // Ok } @Test void whenSignatureVerificationFailsHandshakeShouldBeTerminatedWithDecryptError() throws Exception { // Given handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); Assertions.assertThatThrownBy(() -> // When engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, new byte[256]), ProtectionKeysType.Handshake)) // Then .isInstanceOf(DecryptErrorAlert.class); } @Test void testVerifySignature() throws Exception { byte[] signature = createServerSignature(); Certificate certificate = CertificateUtils.inflateCertificate(encodedCertificate); byte[] hash = new byte[32]; Arrays.fill(hash, (byte) 0x01); boolean verified = engine.verifySignature(signature, rsa_pss_rsae_sha256, certificate, hash); Assertions.assertThat(verified).isTrue(); } @Test void unknownCertificateShouldAbortTls() throws Exception { // Given byte[] validSignature = createServerSignature(); handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); Assertions.assertThatThrownBy(() -> // When engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, validSignature), ProtectionKeysType.Handshake)) // Then .isInstanceOf(BadCertificateAlert.class); } @Test void certificateWithoutMatchingServerNameShouldAbortTls() throws Exception { // Given engine.setHostnameVerifier(createAlwaysRefusingVerifier()); engine.setTrustManager(createNoOpTrustManager()); handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); byte[] validSignature = createServerSignature(); Assertions.assertThatThrownBy(() -> // When engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, validSignature), ProtectionKeysType.Handshake)) // Then .isInstanceOf(CertificateUnknownAlert.class); } @Test void clearingHostnameVerifierDoesNotBypassDefaultVerification() throws Exception { // Given engine.setTrustManager(createNoOpTrustManager()); byte[] validSignature = createServerSignature(); handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); // When engine.setHostnameVerifier(null); Assertions.assertThatThrownBy(() -> engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, validSignature), ProtectionKeysType.Handshake)) // Then .isInstanceOf(CertificateUnknownAlert.class); } @Test void finisedMessageShouldNotBeReceivedBeforeCertificateVerify() throws Exception { // Given handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); // When, no Certificate Verify Message received // Then Assertions.assertThatThrownBy(() -> engine.received(new FinishedMessage(new byte[256]), ProtectionKeysType.Handshake) ).isInstanceOf(UnexpectedMessageAlert.class); } @Test void withPskAcceptedFinisedMessageShouldFollowEncryptedExentions() throws Exception { // Given handshakeUpToCertificate(); FieldSetter.setField(engine, engine.getClass().getDeclaredField("pskAccepted"), true); Assertions.assertThatThrownBy(() -> // When engine.received(new FinishedMessage(new byte[256]), ProtectionKeysType.Handshake)) // Then .isInstanceOf(DecryptErrorAlert.class); // And not UnexpectedMessageAlert } @Test void withPskAcceptedFinisedMessageShouldNotBeReceivedBeforeEncryptedExentions() throws Exception { // Given handshakeUpToEncryptedExtensions(); FieldSetter.setField(engine, engine.getClass().getDeclaredField("pskAccepted"), true); Assertions.assertThatThrownBy(() -> // When engine.received(new FinishedMessage(new byte[256]), ProtectionKeysType.Handshake)) // Then .isInstanceOf(UnexpectedMessageAlert.class); } @Test void incorrectServerFinishedShouldAbortTls() throws Exception { handshakeUpToFinished(); FinishedMessage finishedMessage = new FinishedMessage(new byte[256]); Assertions.assertThatThrownBy(() -> // When engine.received(finishedMessage, ProtectionKeysType.Handshake)) // Then .isInstanceOf(DecryptErrorAlert.class); } @Test void engineShouldSendClientFinishedWhenHandshakeDone() throws Exception { handshakeUpToFinished(); FinishedMessage finishedMessage = new FinishedMessage(new byte[32]); TlsClientEngine stubbedEngine = Mockito.spy(engine); Mockito.doReturn(new byte[32]).when(stubbedEngine).computeFinishedVerifyData(ArgumentMatchers.any(), ArgumentMatchers.any()); stubbedEngine.received(finishedMessage, ProtectionKeysType.Handshake); Mockito.verify(messageSender).send(ArgumentMatchers.any(FinishedMessage.class)); } @Test void certificateRequestMessageShouldNotBeReceivedBeforeEncryptedExtensions() throws Exception { // Given engine.startHandshake(); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); // Then Assertions.assertThatThrownBy(() -> // When engine.received(new CertificateRequestMessage(new SignatureAlgorithmsExtension()), ProtectionKeysType.Handshake) ).isInstanceOf(UnexpectedMessageAlert.class); } @Test void certificateRequestMessageShouldNotBeReceivedAfterCertificate() throws Exception { // Given handshakeUpToCertificate(); engine.received(new CertificateMessage(CertificateUtils.inflateCertificate(encodedCertificate)), ProtectionKeysType.Handshake); // Then Assertions.assertThatThrownBy(() -> engine.received(new CertificateRequestMessage(new SignatureAlgorithmsExtension()), ProtectionKeysType.Handshake) ).isInstanceOf(UnexpectedMessageAlert.class); } @Test void withoutClientCertificateClientAuthLeadsToAdditionalCertificateMessageBeforeFinished() throws Exception { // Given handshakeUpToFinished(List.of(rsa_pss_rsae_sha256), true, null); FinishedMessage finishedMessage = new FinishedMessage(new byte[32]); TlsClientEngine stubbedEngine = Mockito.spy(engine); Mockito.doReturn(new byte[32]).when(stubbedEngine).computeFinishedVerifyData(ArgumentMatchers.any(), ArgumentMatchers.any()); // When stubbedEngine.received(finishedMessage, ProtectionKeysType.Handshake); // Then Mockito.verify(messageSender).send(ArgumentMatchers.any(CertificateMessage.class)); Mockito.verify(messageSender, never()).send(ArgumentMatchers.any(CertificateVerifyMessage.class)); Mockito.verify(messageSender).send(ArgumentMatchers.any(FinishedMessage.class)); } @Test void withClientCertificateClientAuthLeadsToAdditionalCertificateMessageAndVerifyBeforeFinished() throws Exception { // Given X509Certificate clientCertificate = CertificateUtils.getTestCertificate(); PrivateKey privateKey = CertificateUtils.getPrivateKey(); engine.setClientCertificateCallback(arg -> new CertificateWithPrivateKey(clientCertificate, privateKey)); handshakeUpToFinished(List.of(rsa_pss_rsae_sha256), true, null); FinishedMessage finishedMessage = new FinishedMessage(new byte[32]); TlsClientEngine stubbedEngine = Mockito.spy(engine); Mockito.doReturn(new byte[32]).when(stubbedEngine).computeFinishedVerifyData(ArgumentMatchers.any(), ArgumentMatchers.any()); // When stubbedEngine.received(finishedMessage, ProtectionKeysType.Handshake); // Then Mockito.verify(messageSender).send(ArgumentMatchers.any(CertificateMessage.class)); Mockito.verify(messageSender).send(ArgumentMatchers.any(CertificateVerifyMessage.class)); Mockito.verify(messageSender).send(ArgumentMatchers.any(FinishedMessage.class)); } @Test void unsupportedSignatureSchemeLeadsToException() throws Exception { assertThatThrownBy(() -> // When engine.startHandshake(TlsConstants.NamedGroup.secp256r1, List.of(rsa_pss_rsae_sha256, rsa_pkcs1_sha1))) // Then .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("rsa_pkcs1_sha1"); } @Test void certificateRequestMessageShouldContainSignatureAlgorithmsExtension() throws Exception { // Given handshakeUpToCertificate(); assertThatThrownBy(() -> // When engine.received(new CertificateRequestMessage(new CertificateAuthoritiesExtension(new X500Principal("CN=dummy"))), ProtectionKeysType.Handshake)) // Then .isInstanceOf(MissingExtensionAlert.class); } @Test void signatureUsedForClientAuthCertVerifyShouldSelectedFromWhatServerOffers() throws Exception { // Given X509Certificate clientCertificate = CertificateUtils.getTestCertificate(); PrivateKey privateKey = CertificateUtils.getPrivateKey(); engine.setClientCertificateCallback(arg -> new CertificateWithPrivateKey(clientCertificate, privateKey)); handshakeUpToFinished(List.of(rsa_pss_rsae_sha256, rsa_pss_rsae_sha384), true, rsa_pss_rsae_sha384); FinishedMessage finishedMessage = new FinishedMessage(new byte[32]); TlsClientEngine stubbedEngine = Mockito.spy(engine); Mockito.doReturn(new byte[32]).when(stubbedEngine).computeFinishedVerifyData(ArgumentMatchers.any(), ArgumentMatchers.any()); // When stubbedEngine.received(finishedMessage, ProtectionKeysType.Handshake); // Then ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(CertificateVerifyMessage.class); verify(messageSender).send(messageCaptor.capture()); assertThat(messageCaptor.getValue().getSignatureScheme()).isEqualTo(rsa_pss_rsae_sha384); } private void handshakeUpToEncryptedExtensions() throws Exception { handshakeUpToEncryptedExtensions(List.of(rsa_pss_rsae_sha256)); } private void handshakeUpToEncryptedExtensions(List signatureSchemes) throws Exception { engine.startHandshake(TlsConstants.NamedGroup.secp256r1, signatureSchemes); ServerHello serverHello = new ServerHello(TLS_AES_128_GCM_SHA256, List.of( new SupportedVersionsExtension(TlsConstants.HandshakeType.server_hello), new KeyShareExtension(publicKey, TlsConstants.NamedGroup.secp256r1, TlsConstants.HandshakeType.server_hello))); engine.received(serverHello, ProtectionKeysType.None); Mockito.clearInvocations(messageSender); } private void handshakeUpToCertificate() throws Exception { handshakeUpToCertificate(List.of(rsa_pss_rsae_sha256)); } private void handshakeUpToCertificate(List signatureSchemes) throws Exception { handshakeUpToEncryptedExtensions(signatureSchemes); TranscriptHash transcriptHash = (TranscriptHash) Mockito.spy(new FieldReader(engine, engine.getClass().getDeclaredField("transcriptHash")).read()); Mockito.doReturn(ByteUtils.hexToBytes("0101010101010101010101010101010101010101010101010101010101010101")).when(transcriptHash).getServerHash(ArgumentMatchers.argThat(t -> t == TlsConstants.HandshakeType.certificate)); FieldSetter.setField(engine, engine.getClass().getDeclaredField("transcriptHash"), transcriptHash); engine.received(new EncryptedExtensions(), ProtectionKeysType.Handshake); } private void handshakeUpToFinished() throws Exception { handshakeUpToFinished(List.of(rsa_pss_rsae_sha256), false, null); } private void handshakeUpToFinished(List signatureSchemes, boolean requestClientCert, TlsConstants.SignatureScheme clientAuthRequiredSignatureScheme) throws Exception { handshakeUpToCertificate(signatureSchemes); if (requestClientCert) { if (clientAuthRequiredSignatureScheme == null) { clientAuthRequiredSignatureScheme = rsa_pss_rsae_sha256; } engine.received(new CertificateRequestMessage(new SignatureAlgorithmsExtension(clientAuthRequiredSignatureScheme)), ProtectionKeysType.Handshake); } X509Certificate certificate = CertificateUtils.inflateCertificate(encodedCertificate); byte[] validSignature = createServerSignature(); engine.setTrustManager(createNoOpTrustManager()); engine.setHostnameVerifier(createNoOpHostnameVerifier()); engine.received(new CertificateMessage(certificate), ProtectionKeysType.Handshake); engine.received(new CertificateVerifyMessage(rsa_pss_rsae_sha256, validSignature), ProtectionKeysType.Handshake); } private byte[] createServerSignature() throws Exception { // https://tools.ietf.org/html/rfc8446#section-4.4.3 // "For example, if the transcript hash was 32 bytes of 01 (this length would make sense for SHA-256), // the content covered by the digital signature for a server CertificateVerify would be:" String content = "20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020" + "544c5320312e332c2073657276657220436572746966696361746556657269667900" + "0101010101010101010101010101010101010101010101010101010101010101"; byte[] messageBytes = ByteUtils.hexToBytes(content); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(encodedPrivateKey)); PrivateKey privateKey = keyFactory.generatePrivate(keySpecPKCS8); Signature signatureAlgorithm = Signature.getInstance("RSASSA-PSS"); signatureAlgorithm.setParameter(new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 32, 1)); signatureAlgorithm.initSign(privateKey); signatureAlgorithm.update(messageBytes); byte[] digitalSignature = signatureAlgorithm.sign(); return digitalSignature; } private HostnameVerifier createNoOpHostnameVerifier() { return new HostnameVerifier() { @Override public boolean verify(String hostname, X509Certificate serverCertificate) { return true; } }; } private HostnameVerifier createAlwaysRefusingVerifier() { return new HostnameVerifier() { @Override public boolean verify(String hostname, X509Certificate serverCertificate) { return false; } }; } X509TrustManager createNoOpTrustManager() { return new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } }; } static class DummyExtension extends Extension { @Override public byte[] getBytes() { return new byte[0]; } } }