diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java index f9dbd780..6acc3123 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java @@ -1349,6 +1349,11 @@ private void initHandshakeInternal(SSLSocket socket, SSLEngine engine) else { this.session.setSessionContext(authStore.getServerContext()); this.session.setSide(WolfSSL.WOLFSSL_SERVER_END); + /* Track client auth state for getPeerCertificates() */ + boolean clientAuthRequested = + this.params.getNeedClientAuth() || + this.params.getWantClientAuth(); + this.session.setClientAuthRequested(clientAuthRequested); } if (this.sessionCreation == false && !this.session.isFromTable) { diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java b/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java index 419afe29..383cbaa3 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java @@ -73,6 +73,9 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession { byte[] pseudoSessionID = null; /* used with TLS 1.3*/ private int side = 0; + /* Track if client auth was requested, for getPeerCertificates() behavior */ + private volatile boolean clientAuthRequested = false; + /* Cache peer certificates after received. Applications assume that * SSLSocket.getSession().getPeerCertificates() will return the peer * certificate even on a resumed connection where the cert has not been @@ -260,6 +263,7 @@ public WolfSSLImplementSSLSession (WolfSSLImplementSSLSession orig) { this.pseudoSessionID = orig.pseudoSessionID.clone(); } this.side = orig.side; + this.clientAuthRequested = orig.clientAuthRequested; if (orig.peerCerts != null) { this.peerCerts = orig.peerCerts.clone(); } @@ -519,6 +523,15 @@ public synchronized Certificate[] getPeerCertificates() "SSLSocket/Engine closed"); } + /* Throw if server side with no client auth requested */ + if (this.side == WolfSSL.WOLFSSL_SERVER_END && + !this.clientAuthRequested) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + () -> "Server side, no client auth, throwing exception"); + throw new SSLPeerUnverifiedException( + "peer not authenticated (no client auth requested)"); + } + try { x509 = this.ssl.getPeerCertificate(); } catch (IllegalStateException | WolfSSLJNIException ex) { @@ -605,8 +618,8 @@ public Certificate[] getLocalCertificates() { } @Override - public synchronized javax.security.cert.X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { + public synchronized javax.security.cert.X509Certificate[] + getPeerCertificateChain() throws SSLPeerUnverifiedException { long peerX509 = 0; WolfSSLX509X x509; @@ -615,10 +628,17 @@ public synchronized javax.security.cert.X509Certificate[] getPeerCertificateChai throw new SSLPeerUnverifiedException("handshake not done"); } + /* Throw if server side with no client auth requested */ + if (this.side == WolfSSL.WOLFSSL_SERVER_END && + !this.clientAuthRequested) { + throw new SSLPeerUnverifiedException( + "peer not authenticated (no client auth requested)"); + } + try { peerX509 = this.ssl.getPeerCertificate(); if (peerX509 == 0) { - return null; + throw new SSLPeerUnverifiedException("No peer certificate"); } /* wolfSSL starting with 5.3.0 returns a new WOLFSSL_X509 @@ -657,10 +677,17 @@ public synchronized Principal getPeerPrincipal() throw new SSLPeerUnverifiedException("handshake not done"); } + /* Throw if server side with no client auth requested */ + if (this.side == WolfSSL.WOLFSSL_SERVER_END && + !this.clientAuthRequested) { + throw new SSLPeerUnverifiedException( + "peer not authenticated (no client auth requested)"); + } + try { peerX509 = this.ssl.getPeerCertificate(); if (peerX509 == 0) { - return null; + throw new SSLPeerUnverifiedException("No peer certificate"); } /* wolfSSL starting with 5.3.0 returns a new WOLFSSL_X509 @@ -1039,6 +1066,16 @@ protected int getSide() { return this.side; } + /** + * Set whether client auth was requested. + * Used for getPeerCertificates() behavior. + * + * @param requested true if client auth was requested, false otherwise + */ + protected void setClientAuthRequested(boolean requested) { + this.clientAuthRequested = requested; + } + /** * Return the side session is on (server/client) as a String * @return "client" or "server" representing the side of this session diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 5d93631b..1bf5525a 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -54,6 +54,7 @@ import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; import java.util.concurrent.TimeUnit; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; @@ -2494,5 +2495,54 @@ private ByteBuffer enlargeBuffer(ByteBuffer buffer, int size) { bb.put(buffer); return bb; } + + /** + * Verify getPeerCertificateChain() throws SSLPeerUnverifiedException + * when no client auth requested, matching SunJSSE/Netty expectations. + */ + @Test + public void testGetPeerCertificateChainNoClientAuth() throws Exception { + + System.out.print("\tgetPeerCertChain no client auth"); + + String protocol = null; + for (String p : enabledProtocols) { + if (!p.equals("TLS") && !p.contains("DTLS")) { + protocol = p; + break; + } + } + + if (protocol == null) { + pass("\t... skipped"); + return; + } + + SSLContext ctx = tf.createSSLContext(protocol, engineProvider); + + SSLEngine server = ctx.createSSLEngine(); + SSLEngine client = ctx.createSSLEngine("localhost", 11111); + + server.setUseClientMode(false); + server.setNeedClientAuth(false); + server.setWantClientAuth(false); + client.setUseClientMode(true); + + tf.testConnection(server, client, null, null, "No client auth test"); + + SSLSession serverSession = server.getSession(); + + try { + javax.security.cert.X509Certificate[] certs = + serverSession.getPeerCertificateChain(); + error("\t... failed"); + fail("Expected SSLPeerUnverifiedException, got " + + (certs == null ? "null" : "certs")); + } catch (SSLPeerUnverifiedException e) { + /* Expected */ + } + + pass("\t... passed"); + } }