F-6307: reject presented but untrusted client cert under wantClientAuth
parent
05fbad13f9
commit
09fbfdff88
|
|
@ -638,18 +638,17 @@ public class WolfSSLInternalVerifyCb implements WolfSSLVerifyCallback {
|
|||
else if ((!this.clientMode) && (this.params != null) &&
|
||||
this.params.getWantClientAuth() &&
|
||||
(!this.params.getNeedClientAuth())) {
|
||||
/* wantClientAuth is set and client sent a certificate.
|
||||
* Try to verify via TrustManager, but don't fail the
|
||||
* handshake if verification fails — matches SunJSSE
|
||||
* behavior where wantClientAuth is non-fatal. */
|
||||
if (VerifyCertChainWithTrustManager(x509certs, authType)) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
() -> "wantClientAuth: client cert verified successfully");
|
||||
} else {
|
||||
/* wantClientAuth only makes an absent client cert non-fatal.
|
||||
* A cert that is presented must still validate, matching SunJSSE,
|
||||
* so abort the handshake if the TrustManager rejects it. */
|
||||
if (VerifyCertChainWithTrustManager(x509certs, authType) == false) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
() -> "wantClientAuth: client cert verification failed, " +
|
||||
"continuing handshake (non-fatal)");
|
||||
"aborting handshake");
|
||||
return 0;
|
||||
}
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
() -> "wantClientAuth: client cert verified successfully");
|
||||
}
|
||||
else {
|
||||
/* Poll X509TrustManager / X509ExtendedTrustManager for certificate
|
||||
|
|
|
|||
|
|
@ -1006,22 +1006,27 @@ public class WolfSSLEngineTest {
|
|||
};
|
||||
|
||||
/* All combinations with external X509ExtendedTrustManager registered
|
||||
* which will trust NO client certs and ALL server certs */
|
||||
* which will trust NO client certs and ALL server certs. The client
|
||||
* presents a cert, so when the server requests one (want or need) it
|
||||
* fails validation and the handshake aborts. wantClientAuth only makes
|
||||
* an absent client cert non-fatal, matching SunJSSE, so serverWant with
|
||||
* a presented-but-rejected cert is expected to fail. Only the cases
|
||||
* where the server requests no client cert succeed. */
|
||||
PeerAuthConfig[] configsDefaultManagers = new PeerAuthConfig[] {
|
||||
new PeerAuthConfig(true, true, true, true, false),
|
||||
new PeerAuthConfig(true, true, true, false, true),
|
||||
new PeerAuthConfig(true, true, true, false, false),
|
||||
new PeerAuthConfig(true, true, false, true, false),
|
||||
new PeerAuthConfig(true, true, false, false, true),
|
||||
new PeerAuthConfig(true, false, true, true, false),
|
||||
new PeerAuthConfig(true, false, true, false, true),
|
||||
new PeerAuthConfig(true, false, true, false, false),
|
||||
new PeerAuthConfig(true, false, false, true, false),
|
||||
new PeerAuthConfig(true, false, false, false, true),
|
||||
new PeerAuthConfig(false, true, true, true, false),
|
||||
new PeerAuthConfig(false, true, true, false, true),
|
||||
new PeerAuthConfig(false, true, true, false, false),
|
||||
new PeerAuthConfig(false, true, false, true, false),
|
||||
new PeerAuthConfig(false, true, false, false, true),
|
||||
new PeerAuthConfig(false, false, true, true, false),
|
||||
new PeerAuthConfig(false, false, true, false, true),
|
||||
new PeerAuthConfig(false, false, true, false, false),
|
||||
new PeerAuthConfig(false, false, false, true, false),
|
||||
new PeerAuthConfig(false, false, false, false, true)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2173,6 +2173,116 @@ public class WolfSSLSocketTest {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* With wantClientAuth(true) and needClientAuth(false), a client that
|
||||
* presents a certificate the server cannot validate must abort the
|
||||
* handshake, matching SunJSSE. wantClientAuth only makes an absent
|
||||
* client cert non-fatal, not an invalid one.
|
||||
*/
|
||||
@Test
|
||||
public void testWantClientAuthRejectsUntrustedClientCert()
|
||||
throws Exception {
|
||||
|
||||
/* Negative case: server does not trust the presented client cert.
|
||||
* Server truststore (caServerJKS) does not contain the CA that signed
|
||||
* the client cert, so a presented client cert cannot be validated. */
|
||||
SSLContext srvCtx = tf.createSSLContext("TLSv1.2", ctxProvider,
|
||||
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
|
||||
tf.createKeyManager("SunX509", tf.serverJKS, ctxProvider));
|
||||
|
||||
/* Client trusts the server and has a client cert (clientJKS) to
|
||||
* present when the server sends a CertificateRequest. */
|
||||
SSLContext cliCtx = tf.createSSLContext("TLSv1.2", ctxProvider,
|
||||
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
|
||||
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
|
||||
|
||||
SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
SSLSocket cs = (SSLSocket)cliCtx.getSocketFactory().createSocket();
|
||||
cs.connect(new InetSocketAddress(ss.getLocalPort()));
|
||||
|
||||
final SSLSocket server = (SSLSocket)ss.accept();
|
||||
server.setWantClientAuth(true);
|
||||
server.setNeedClientAuth(false);
|
||||
|
||||
ExecutorService es = Executors.newSingleThreadExecutor();
|
||||
Future<Void> serverFuture = es.submit(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
try {
|
||||
server.startHandshake();
|
||||
fail("Server accepted an untrusted client cert under " +
|
||||
"wantClientAuth");
|
||||
|
||||
} catch (SSLException e) {
|
||||
/* expected: presented client cert failed validation */
|
||||
}
|
||||
server.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
cs.startHandshake();
|
||||
fail("Client handshake succeeded despite server rejecting cert");
|
||||
|
||||
} catch (SSLException e) {
|
||||
/* expected: server sent a fatal alert */
|
||||
}
|
||||
cs.close();
|
||||
|
||||
es.shutdown();
|
||||
serverFuture.get();
|
||||
ss.close();
|
||||
|
||||
/* Server truststore (caClientJKS) trusts the client cert, so a valid
|
||||
* presented client cert must let the handshake complete. */
|
||||
SSLContext srvCtx2 = tf.createSSLContext("TLSv1.2", ctxProvider,
|
||||
tf.createTrustManager("SunX509", tf.caClientJKS, ctxProvider),
|
||||
tf.createKeyManager("SunX509", tf.serverJKS, ctxProvider));
|
||||
|
||||
SSLContext cliCtx2 = tf.createSSLContext("TLSv1.2", ctxProvider,
|
||||
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
|
||||
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
|
||||
|
||||
ss = (SSLServerSocket)srvCtx2.getServerSocketFactory()
|
||||
.createServerSocket(0);
|
||||
cs = (SSLSocket)cliCtx2.getSocketFactory().createSocket();
|
||||
cs.connect(new InetSocketAddress(ss.getLocalPort()));
|
||||
|
||||
final SSLSocket server2 = (SSLSocket)ss.accept();
|
||||
server2.setWantClientAuth(true);
|
||||
server2.setNeedClientAuth(false);
|
||||
|
||||
es = Executors.newSingleThreadExecutor();
|
||||
serverFuture = es.submit(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
try {
|
||||
server2.startHandshake();
|
||||
|
||||
} catch (SSLException e) {
|
||||
fail("Server rejected a valid client cert under " +
|
||||
"wantClientAuth");
|
||||
}
|
||||
server2.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
cs.startHandshake();
|
||||
|
||||
} catch (SSLException e) {
|
||||
fail("Client handshake failed with a valid client cert");
|
||||
}
|
||||
cs.close();
|
||||
|
||||
es.shutdown();
|
||||
serverFuture.get();
|
||||
ss.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtocolTLSv10() throws Exception {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue