diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c index ba8bb1df..5ed693cd 100644 --- a/native/com_wolfssl_WolfSSLSession.c +++ b/native/com_wolfssl_WolfSSLSession.c @@ -2358,6 +2358,33 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useCertificateChainBuffer return ret; } +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useCertificateChainBufferFormat + (JNIEnv* jenv, jobject jcl, jlong sslPtr, jbyteArray in, jlong sz, jint format) +{ + int ret = WOLFSSL_FAILURE; + byte* buff = NULL; + word32 buffSz = 0; + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; + (void)jcl; + (void)sz; + + if (jenv == NULL || ssl == NULL || in == NULL) { + return (jint)BAD_FUNC_ARG; + } + + buff = (byte*)(*jenv)->GetByteArrayElements(jenv, in, NULL); + buffSz = (*jenv)->GetArrayLength(jenv, in); + + if (buff != NULL && buffSz > 0) { + ret = wolfSSL_use_certificate_chain_buffer_format( + ssl, buff, buffSz, format); + } + + (*jenv)->ReleaseByteArrayElements(jenv, in, (jbyte*)buff, JNI_ABORT); + + return (jint)ret; +} + JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setGroupMessages (JNIEnv* jenv, jobject jcl, jlong sslPtr) { diff --git a/native/com_wolfssl_WolfSSLSession.h b/native/com_wolfssl_WolfSSLSession.h index 47fb578f..4368d994 100644 --- a/native/com_wolfssl_WolfSSLSession.h +++ b/native/com_wolfssl_WolfSSLSession.h @@ -367,6 +367,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_usePrivateKeyBuffer JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useCertificateChainBuffer (JNIEnv *, jobject, jlong, jbyteArray, jlong); +/* + * Class: com_wolfssl_WolfSSLSession + * Method: useCertificateChainBufferFormat + * Signature: (J[BJI)I + */ +JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useCertificateChainBufferFormat + (JNIEnv *, jobject, jlong, jbyteArray, jlong, jint); + /* * Class: com_wolfssl_WolfSSLSession * Method: setGroupMessages diff --git a/src/java/com/wolfssl/WolfSSLContext.java b/src/java/com/wolfssl/WolfSSLContext.java index 5448e3c4..81f65fb0 100644 --- a/src/java/com/wolfssl/WolfSSLContext.java +++ b/src/java/com/wolfssl/WolfSSLContext.java @@ -1052,7 +1052,7 @@ public int useCertificateChainBuffer(byte[] in, long sz) * and start with the subject's certificate, ending with the root * certificate. * - * @param in the input buffer containing the PEM-formatted + * @param in the input buffer containing the PEM or DER formatted * certificate chain to be loaded. * @param sz the size of the input buffer, in * @param format format of the certificate buffer being loaded - either diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java index e41f209c..7e6fa80b 100644 --- a/src/java/com/wolfssl/WolfSSLSession.java +++ b/src/java/com/wolfssl/WolfSSLSession.java @@ -297,6 +297,8 @@ private native int usePrivateKeyBuffer(long ssl, byte[] in, long sz, int format); private native int useCertificateChainBuffer(long ssl, byte[] in, long sz); + private native int useCertificateChainBufferFormat( + long ssl, byte[] in, long sz, int format); private native int setGroupMessages(long ssl); private native int enableCRL(long ssl, int options); private native int disableCRL(long ssl); @@ -2008,6 +2010,52 @@ public int useCertificateChainBuffer(byte[] in, long sz) } } + /** + * Loads a certificate chain buffer into the SSL object in specific format. + * This method behaves like the non-buffered version, only differing + * in its ability to be called with a buffer as input instead of a file. + * This function is similar to useCertificateChainBuffer(), but allows + * the input format to be specified. The format must be either DER or PEM, + * and start with the subject's certificate, ending with the root + * certificate. + * + * @param in the input buffer containing the PEM or DER formatted + * certificate chain to be loaded. + * @param sz the size of the input buffer, in + * @param format format of the certificate buffer being loaded - either + * SSL_FILETYPE_PEM or SSL_FILETYPE_ASN1 + * @return SSL_SUCCESS upon success, + * SSL_FAILURE upon general failure, + * SSL_BAD_FILETYPE if the file is + * in the wrong format, SSL_BAD_FILE + * if the file doesn't exist, can't be read, or is + * corrupted. MEMORY_E if an out of + * memory condition occurs, ASN_INPUT_E + * if Base16 decoding fails on the file, + * BUFFER_E if a chain buffer is + * bigger than the receiving buffer, and + * BAD_FUNC_ARG if invalid input arguments + * are provided. + * @throws IllegalStateException WolfSSLContext has been freed + * @throws WolfSSLJNIException Internal JNI error + * @see #useCertificateBuffer(byte[], long, int) + * @see #usePrivateKeyBuffer(byte[], long, int) + * @see WolfSSLSession#useCertificateBuffer(byte[], long, int) + * @see WolfSSLSession#usePrivateKeyBuffer(byte[], long, int) + * @see WolfSSLSession#useCertificateChainBuffer(byte[], long) + */ + public int useCertificateChainBufferFormat(byte[] in, long sz, int format) + throws IllegalStateException, WolfSSLJNIException { + + confirmObjectIsActive(); + + synchronized (sslLock) { + return useCertificateChainBufferFormat( + getSessionPtr(), in, sz, format); + } + } + + /** * Turns on grouping of the handshake messages where possible using the * SSL session. diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java index 1ed146ba..5dd164ac 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLContext.java @@ -178,7 +178,6 @@ private void createCtx() throws WolfSSLException { try { LoadTrustedRootCerts(); - LoadClientKeyAndCertChain(); } catch (Exception e) { throw new IllegalArgumentException(e); @@ -369,101 +368,6 @@ private void LoadTrustedRootCerts() { } } - private void LoadClientKeyAndCertChain() throws Exception { - - int ret; - int offset; - X509KeyManager km = authStore.getX509KeyManager(); - String javaVersion = System.getProperty("java.version"); - - if (km == null) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "internal KeyManager is null, no cert/key to load"); - return; - } - - /* We only load keys from algorithms enabled in native wolfSSL, - * and in the priority order of ECC first, then RSA. JDK 1.7.0_201 - * and 1.7.0_171 have a bug that causes PrivateKey.getEncoded() to - * fail for EC keys. This has been fixed in later JDK versions, - * but skip adding EC here if we're running on those versions . */ - ArrayList keyAlgos = new ArrayList(); - if (WolfSSL.EccEnabled() && - (!javaVersion.equals("1.7.0_201") && - !javaVersion.equals("1.7.0_171"))) { - keyAlgos.add("EC"); - } - if (WolfSSL.RsaEnabled()) { - keyAlgos.add("RSA"); - } - - String[] keyStrings = new String[keyAlgos.size()]; - keyStrings = keyAlgos.toArray(keyStrings); - - String alias = km.chooseClientAlias(keyStrings, null, null); - authStore.setCertAlias(alias); - - /* client private key */ - PrivateKey privKey = km.getPrivateKey(alias); - - if (privKey != null) { - byte[] privKeyEncoded = privKey.getEncoded(); - if (!privKey.getFormat().equals("PKCS#8")) { - throw new Exception("Private key is not in PKCS#8 format"); - } - - /* skip past PKCS#8 offset */ - offset = WolfSSL.getPkcs8TraditionalOffset(privKeyEncoded, 0, - privKeyEncoded.length); - - byte[] privKeyTraditional = Arrays.copyOfRange(privKeyEncoded, - offset, privKeyEncoded.length); - - ret = ctx.usePrivateKeyBuffer(privKeyTraditional, - privKeyTraditional.length, WolfSSL.SSL_FILETYPE_ASN1); - - if (ret != WolfSSL.SSL_SUCCESS) { - throw new WolfSSLJNIException("Failed to load private key " + - "buffer, err = " + ret); - } - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "loaded private key from KeyManager (alias: " + alias + - ")"); - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "no private key found, skipped loading"); - } - - /* client certificate chain */ - X509Certificate[] cert = km.getCertificateChain(alias); - - if (cert != null) { - ByteArrayOutputStream certStream = new ByteArrayOutputStream(); - int chainLength = 0; - for (int i = 0; i < cert.length; i++) { - /* concatenate certs into single byte array */ - certStream.write(cert[i].getEncoded()); - chainLength++; - } - byte[] certChain = certStream.toByteArray(); - certStream.close(); - - ret = ctx.useCertificateChainBufferFormat(certChain, - certChain.length, WolfSSL.SSL_FILETYPE_ASN1); - - if (ret != WolfSSL.SSL_SUCCESS) { - throw new WolfSSLJNIException("Failed to load certificate " + - "chain buffer, err = " + ret); - } - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "loaded certificate chain from KeyManager (length: " + - chainLength + ")"); - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "no certificate or chain found, skipped loading"); - } - } - /** * Initializes a SSLContext. * diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java index 512412ab..f0a3f065 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java @@ -28,6 +28,7 @@ import com.wolfssl.WolfSSLJNIException; import com.wolfssl.WolfSSLSession; import com.wolfssl.WolfSSLALPNSelectCallback; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.util.function.BiFunction; @@ -36,6 +37,7 @@ import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; +import java.security.cert.CertificateEncodingException; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -164,6 +166,14 @@ protected WolfSSLEngine(com.wolfssl.WolfSSLContext ctx, } EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params); + + try { + EngineHelper.LoadKeyAndCertChain(null, this); + } catch (CertificateEncodingException | IOException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "failed to load private key and/or cert chain"); + throw new WolfSSLException(e); + } } /** @@ -192,6 +202,14 @@ protected WolfSSLEngine(com.wolfssl.WolfSSLContext ctx, } EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, port, host); + + try { + EngineHelper.LoadKeyAndCertChain(null, this); + } catch (CertificateEncodingException | IOException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "failed to load private key and/or cert chain"); + throw new WolfSSLException(e); + } } /** @@ -350,6 +368,13 @@ private synchronized int ClosingConnection() throws SocketException { /* send/recv close_notify as needed */ synchronized (ioLock) { ret = ssl.shutdownSSL(); + if (ssl.getError(ret) == WolfSSL.SSL_ERROR_ZERO_RETURN) { + /* got close_notify alert, reset ret to 0 to continue + * and let corresponding close_notify to be sent */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "ClosingConnection(), ssl.getError() is ZERO_RETURN"); + ret = 0; + } } UpdateCloseNotifyStatus(); @@ -757,6 +782,8 @@ private synchronized int RecvAppData(ByteBuffer[] out, int ofst, int length) synchronized (ioLock) { if (ssl.getShutdown() == WolfSSL.SSL_RECEIVED_SHUTDOWN) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "RecvAppData(), received shutdown message"); try { ret = ClosingConnection(); if (ret > 0) { @@ -1381,7 +1408,7 @@ public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() { @Override public synchronized void setUseClientMode(boolean mode) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "entered setUseClientMode()"); + "entered setUseClientMode(" + mode + ")"); EngineHelper.setUseClientMode(mode); this.clientModeSet = true; } @@ -1396,7 +1423,7 @@ public synchronized boolean getUseClientMode() { @Override public synchronized void setNeedClientAuth(boolean need) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "entered setNeedClientAuth()"); + "entered setNeedClientAuth(" + need + ")"); EngineHelper.setNeedClientAuth(need); } @@ -1410,7 +1437,7 @@ public synchronized boolean getNeedClientAuth() { @Override public synchronized void setWantClientAuth(boolean want) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "entered setWantClientAuth()"); + "entered setWantClientAuth(" + want + ")"); EngineHelper.setWantClientAuth(want); } @@ -1424,7 +1451,7 @@ public synchronized boolean getWantClientAuth() { @Override public synchronized void setEnableSessionCreation(boolean flag) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "entered setEnableSessionCreation()"); + "entered setEnableSessionCreation(" + flag + ")"); EngineHelper.setEnableSessionCreation(flag); } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java index 09bb63e2..95dc5faa 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java @@ -20,12 +20,12 @@ */ package com.wolfssl.provider.jsse; -import com.wolfssl.WolfSSL; -import com.wolfssl.WolfSSLException; -import com.wolfssl.WolfSSLSession; import java.util.Arrays; import java.util.ArrayList; import java.util.List; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.Socket; import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; @@ -33,8 +33,18 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.X509TrustManager; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.SSLHandshakeException; import java.security.Security; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateEncodingException; + +import com.wolfssl.WolfSSL; +import com.wolfssl.WolfSSLSession; +import com.wolfssl.WolfSSLException; +import com.wolfssl.WolfSSLJNIException; /** * This is a helper class to account for similar methods between SSLSocket @@ -168,6 +178,206 @@ protected WolfSSLEngineHelper(WolfSSLSession ssl, WolfSSLAuthStore store, ", peer IP: " + peerAddr.getHostAddress() + ")"); } + /** + * Get the alias from the X509KeyManager to use for finding and loading + * the private key and certificate chain for this endpoint. + * + * @param km X509KeyManager or X509ExtendedKeyManager to poll for + * client/server alias name + * @param socket Socket or SSLSocket from which this peer is being + * created, may be null if engine is being used instead + * @param engine SSLEngine from which this peer is being created, may be + * null if socket is being used instead + * + * @return alias String, or null if none found + */ + private String GetKeyAndCertChainAlias(X509KeyManager km, Socket sock, + SSLEngine engine) { + + String alias = null; + String javaVersion = System.getProperty("java.version"); + + if (sock == null && engine == null) { + return null; + } + + /* We only load keys from algorithms enabled in native wolfSSL, + * and in the priority order of ECC first, then RSA. JDK 1.7.0_201 + * and 1.7.0_171 have a bug that causes PrivateKey.getEncoded() to + * fail for EC keys. This has been fixed in later JDK versions, + * but skip adding EC here if we're running on those versions . */ + ArrayList keyAlgos = new ArrayList(); + if (WolfSSL.EccEnabled() && + (!javaVersion.equals("1.7.0_201") && + !javaVersion.equals("1.7.0_171"))) { + keyAlgos.add("EC"); + } + if (WolfSSL.RsaEnabled()) { + keyAlgos.add("RSA"); + } + + String[] keyTypes = new String[keyAlgos.size()]; + keyTypes = keyAlgos.toArray(keyTypes); + + if (clientMode) { + if (sock != null) { + alias = km.chooseClientAlias(keyTypes, null, sock); + } + else if (engine != null) { + if (km instanceof X509ExtendedKeyManager) { + alias = ((X509ExtendedKeyManager)km). + chooseEngineClientAlias(keyTypes, null, engine); + } + else { + alias = km.chooseClientAlias(keyTypes, null, null); + } + } + } + else { + /* Loop through available key types until we find an alias + * that works, or none that do and return null */ + for (String type : keyTypes) { + if (sock != null) { + alias = km.chooseServerAlias(type, null, sock); + } + else if (engine != null) { + if (km instanceof X509ExtendedKeyManager) { + alias = ((X509ExtendedKeyManager)km). + chooseEngineServerAlias(type, null, engine); + } + else { + alias = km.chooseServerAlias(type, null, null); + } + } + + if (alias != null) { + break; + } + } + } + + return alias; + } + + /** + * Loads the private key and certificate chain for this + * SSLSocket/SSLEngine to be used for performing authentication of + * this peer during the handshake. + * + * If there is no X509KeyManager in our WolfSSLAuthStore, skips loading + * private key and certificate. This means SSLContext.init() was + * initialized with a null KeyManager. + * + * @param sock Socket or SSLSocket associated with this connection, may + * be null if engine is used instead + * @param engine SSLEngine associated with this connection, may be null + * if sock used instead + * + * @throws WolfSSLException if private key is not correct format, + * WolfSSLAuthStore is null, or native error when loading + * private key or certificate. + * @throws CertificateEncodingException on error getting Certificate + * encoding before loading into native WOLFSSL + * @throws IOException on error concatenating certificate chain into + * single byte array + */ + protected void LoadKeyAndCertChain(Socket sock, SSLEngine engine) + throws WolfSSLException, CertificateEncodingException, IOException { + + int ret; + int offset; + String alias = null; /* KeyStore alias holding private key */ + X509KeyManager km = null; /* X509KeyManager from KeyStore */ + + if (this.authStore == null) { + throw new WolfSSLException("WolfSSLAuthStore is null"); + } + + km = this.authStore.getX509KeyManager(); + if (km == null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "internal KeyManager is null, no cert/key to load"); + return; + } + + /* Ask X509KeyManager to choose correct client alias from which + * to load private key / cert chain */ + alias = GetKeyAndCertChainAlias(km, sock, engine); + authStore.setCertAlias(alias); + + /* Load private key into WOLFSSL session */ + PrivateKey privKey = km.getPrivateKey(alias); + + if (privKey != null) { + byte[] privKeyEncoded = privKey.getEncoded(); + if (!privKey.getFormat().equals("PKCS#8")) { + throw new WolfSSLException( + "Private key is not in PKCS#8 format"); + } + + /* Skip past PKCS#8 offset */ + offset = WolfSSL.getPkcs8TraditionalOffset(privKeyEncoded, 0, + privKeyEncoded.length); + + byte[] privKeyTraditional = Arrays.copyOfRange(privKeyEncoded, + offset, privKeyEncoded.length); + + try { + ret = this.ssl.usePrivateKeyBuffer(privKeyTraditional, + privKeyTraditional.length, WolfSSL.SSL_FILETYPE_ASN1); + } catch (WolfSSLJNIException e) { + throw new WolfSSLException(e); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException("Failed to load private key " + + "buffer into WOLFSSL, err = " + ret); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "loaded private key from X509KeyManager " + + "(alias: " + alias + ")"); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "no private key found in X509KeyManager " + + "(alias: " + alias + "), skipped loading"); + } + + /* Load certificate chain */ + X509Certificate[] cert = km.getCertificateChain(alias); + + if (cert != null) { + ByteArrayOutputStream certStream = new ByteArrayOutputStream(); + int chainLength = 0; + for (int i = 0; i < cert.length; i++) { + /* concatenate certs into single byte array */ + certStream.write(cert[i].getEncoded()); + chainLength++; + } + byte[] certChain = certStream.toByteArray(); + certStream.close(); + + try { + ret = this.ssl.useCertificateChainBufferFormat(certChain, + certChain.length, WolfSSL.SSL_FILETYPE_ASN1); + } catch (WolfSSLJNIException e) { + throw new WolfSSLException(e); + } + + if (ret != WolfSSL.SSL_SUCCESS) { + throw new WolfSSLException("Failed to load certificate " + + "chain buffer into WOLFSSL, err = " + ret); + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "loaded certificate chain from KeyManager (alias: " + + alias + ", length: " + + chainLength + ")"); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "no certificate or chain found " + + "(alias: " + alias + "), skipped loading"); + } + } + /** * Set hostname and port * Used internally by SSLSocket.connect(SocketAddress) @@ -580,7 +790,8 @@ private void setLocalAuth(SSLSocket socket, SSLEngine engine) { X509TrustManager tm = authStore.getX509TrustManager(); wicb = new WolfSSLInternalVerifyCb(authStore.getX509TrustManager(), - this.clientMode, socket, engine); + this.clientMode, socket, engine, + this.params); if (tm instanceof com.wolfssl.provider.jsse.WolfSSLTrustX509) { /* use internal peer verification logic */ diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLInternalVerifyCb.java b/src/java/com/wolfssl/provider/jsse/WolfSSLInternalVerifyCb.java index dd54a36d..b394af45 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLInternalVerifyCb.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLInternalVerifyCb.java @@ -48,6 +48,7 @@ public class WolfSSLInternalVerifyCb implements WolfSSLVerifyCallback { private boolean clientMode; private SSLSocket callingSocket = null; private SSLEngine callingEngine = null; + private WolfSSLParameters params = null; /** * Create new WolfSSLInternalVerifyCb @@ -56,13 +57,15 @@ public class WolfSSLInternalVerifyCb implements WolfSSLVerifyCallback { * @param client boolean representing if this is client side * @param socket SSLSocket associated with this callback, or null * @param engine SSLEngine associated with this callback, or null + * @param params WolfSSLParameters associated with this callback */ public WolfSSLInternalVerifyCb(X509TrustManager xtm, boolean client, - SSLSocket socket, SSLEngine engine) { + SSLSocket socket, SSLEngine engine, WolfSSLParameters params) { this.tm = xtm; this.clientMode = client; this.callingSocket = socket; this.callingEngine = engine; + this.params = params; } /** @@ -114,6 +117,97 @@ else if (this.callingEngine != null) { return 1; } + /** + * Calls registered X509TrustManager / X509ExtendedTrustManager to + * verify certificate chain. + * + * @param certs Peer certificate chain to validate + * @param authType Authentication type + * + * @return true on successful validation, otherwise false on failure + */ + private boolean VerifyCertChainWithTrustManager(X509Certificate[] certs, + String authType) { + + try { + /* Call TrustManager to do cert verification, should throw + * CertificateException if verification fails */ + if (this.clientMode) { + if (this.tm instanceof X509ExtendedTrustManager) { + X509ExtendedTrustManager xtm = + (X509ExtendedTrustManager)this.tm; + if (this.callingSocket != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Calling TrustManager.checkServerTrusted(SSLSocket)"); + xtm.checkServerTrusted(certs, authType, + this.callingSocket); + } + else if (this.callingEngine != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Calling TrustManager.checkServerTrusted(SSLEngine)"); + xtm.checkServerTrusted(certs, authType, + this.callingEngine); + } + else { + /* If we do have access to X509ExtendedTrustManager, + * but don't have SSLSocket/Engine, error out instead + * of falling back to verify without hostname. */ + throw new Exception( + "SSLSocket/SSLEngine null during server peer " + + "verification, failed to verify"); + } + } + else { + /* Basic X509TrustManager does not support HTTPS + * hostname verification, no SSLSocket/Engine needed */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Calling TrustManager.checkServerTrusted()"); + this.tm.checkServerTrusted(certs, authType); + } + + } else { + if (this.tm instanceof X509ExtendedTrustManager) { + X509ExtendedTrustManager xtm = + (X509ExtendedTrustManager)this.tm; + if (this.callingSocket != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Calling TrustManager.checkClientTrusted(SSLSocket)"); + xtm.checkClientTrusted(certs, authType, + this.callingSocket); + } + else if (this.callingEngine != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Calling TrustManager.checkClientTrusted(SSLEngine)"); + xtm.checkClientTrusted(certs, authType, + this.callingEngine); + } + else { + /* If we do have access to X509ExtendedTrustManager, + * but don't have SSLSocket/Engine, error out instead + * of falling back to verify without hostname. */ + throw new Exception( + "SSLSocket/SSLEngine null during client peer " + + "verification, failed to verify"); + } + } + else { + /* Basic X509TrustManager does not support HTTPS + * hostname verification, no SSLSocket/Engine needed */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Calling TrustManager.checkClientTrusted()"); + this.tm.checkClientTrusted(certs, authType); + } + } + } catch (Exception e) { + /* TrustManager rejected certificate, not valid */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "TrustManager rejected certificates, verification failed"); + return false; + } + + return true; + } + /** * Native wolfSSL verify callback. * @@ -128,8 +222,8 @@ else if (this.callingEngine != null) { public int verifyCallback(int preverify_ok, long x509StorePtr) { WolfSSLCertificate[] certs = null; - X509Certificate[] x509certs = null; String authType = null; + X509Certificate[] x509certs = new X509Certificate[0]; if (preverify_ok == 1) { /* When using WolfSSLTrustX509 implementation of @@ -141,10 +235,12 @@ public int verifyCallback(int preverify_ok, long x509StorePtr) { * passed if preverify_ok == 1, so we skip doing it again here * later on for this case */ WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Native wolfSSL peer verification passed"); + "Native wolfSSL peer verification passed (clientMode: " + + this.clientMode + ")"); } else { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "NOTE: Native wolfSSL peer verification failed"); + "NOTE: Native wolfSSL peer verification failed " + + "(clientMode: " + this.clientMode + ")"); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, " Continuing with X509TrustManager verification"); } @@ -173,10 +269,11 @@ public int verifyCallback(int preverify_ok, long x509StorePtr) { } } catch (CertificateException | IOException | WolfSSLJNIException ce) { - /* failed to get cert array, give app null array */ + /* failed to get cert array, give app empty array */ WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Failed to get X509Certificate[] array, set to null"); - x509certs = null; + "Failed to get X509Certificate[] array, set to " + + "empty array"); + x509certs = new X509Certificate[0]; } /* get authType, use first cert */ @@ -205,87 +302,51 @@ public int verifyCallback(int preverify_ok, long x509StorePtr) { * We do that here and return before going on to additional * checkServerTrusted/checkClientTrusted() so that we do not * duplicate verification. */ - if (preverify_ok == 1 && (tm instanceof WolfSSLTrustX509)) { + if ((preverify_ok == 1) && (x509certs.length > 0) && + (tm instanceof WolfSSLTrustX509)) { return verifyHostnameOnly(x509certs[0]); } - try { - /* poll TrustManager for cert verification, should throw - * CertificateException if verification fails */ - if (clientMode) { - if (tm instanceof X509ExtendedTrustManager) { - X509ExtendedTrustManager xtm = (X509ExtendedTrustManager)tm; - if (this.callingSocket != null) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Calling TrustManager.checkServerTrusted(SSLSocket)"); - xtm.checkServerTrusted(x509certs, authType, - this.callingSocket); - } - else if (this.callingEngine != null) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Calling TrustManager.checkServerTrusted(SSLEngine)"); - xtm.checkServerTrusted(x509certs, authType, - this.callingEngine); - } - else { - /* If we do have access to X509ExtendedTrustManager, - * but don't have SSLSocket/Engine, error out instead - * of falling back to verify without hostname. */ - throw new Exception( - "SSLSocket/SSLEngine null during server peer " + - "verification, failed to verify"); - } - } - else { - /* Basic X509TrustManager does not support HTTPS - * hostname verification, no SSLSocket/Engine needed */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Calling TrustManager.checkServerTrusted()"); - tm.checkServerTrusted(x509certs, authType); - } - - } else { - if (tm instanceof X509ExtendedTrustManager) { - X509ExtendedTrustManager xtm = (X509ExtendedTrustManager)tm; - if (this.callingSocket != null) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Calling TrustManager.checkClientTrusted(SSLSocket)"); - xtm.checkClientTrusted(x509certs, authType, - this.callingSocket); - } - else if (this.callingEngine != null) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Calling TrustManager.checkClientTrusted(SSLEngine)"); - xtm.checkClientTrusted(x509certs, authType, - this.callingEngine); - } - else { - /* If we do have access to X509ExtendedTrustManager, - * but don't have SSLSocket/Engine, error out instead - * of falling back to verify without hostname. */ - throw new Exception( - "SSLSocket/SSLEngine null during client peer " + - "verification, failed to verify"); - } - } - else { - /* Basic X509TrustManager does not support HTTPS - * hostname verification, no SSLSocket/Engine needed */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Calling TrustManager.checkClientTrusted()"); - tm.checkClientTrusted(x509certs, authType); - } - } - } catch (Exception e) { - /* TrustManager rejected certificate, not valid */ + /* If server-side application has explicitly disabled client + * authentication, return as success and skip X509TrustManager + * verification */ + if ((!this.clientMode) && (this.params != null) && + (!this.params.getNeedClientAuth())) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "TrustManager rejected certificates, verification failed"); - return 0; + "Application has disabled client verification with " + + "setNeedClientAuth(false), skipping verification"); + return 1; + } + else if ((preverify_ok == 1) && (x509certs.length == 0) && + (!this.clientMode) && (this.params != null) && + this.params.getWantClientAuth() && + (!this.params.getNeedClientAuth())) { + /* If native wolfSSL verification has passed, and we have no peer + * certificates, if application has set client authentication be + * requested (wantClientAuth == true), but not fatal if no + * certificate was sent (needClientAuth == false), don't call + * TrustManager with empty certificate chain just consider + * verification successful at this point */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "No client cert sent and client auth marked optional, not " + + "calling TrustManager for hostname verification"); + } + else { + /* Poll X509TrustManager / X509ExtendedTrustManager for certificate + * verification status. Returns 0 if certificates are rejected, + * otherwise 1 on successful verification */ + if (VerifyCertChainWithTrustManager(x509certs, authType) == false) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "TrustManager verification failed"); + /* Abort handshake, verification failed */ + return 0; + } } WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "TrustManager verification successful"); - /* continue handshake, verification succeeded */ + + /* Continue handshake, verification succeeded */ return 1; } } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java index a9a96dc2..5fb3eb06 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java @@ -106,9 +106,11 @@ boolean getWantClientAuth() { } void setWantClientAuth(boolean wantClientAuth) { - /* wantClientAuth OR needClientAuth can be set, not both */ + /* wantClientAuth OR needClientAuth can be set true, not both */ this.wantClientAuth = wantClientAuth; - this.needClientAuth = false; + if (this.wantClientAuth) { + this.needClientAuth = false; + } } boolean getNeedClientAuth() { @@ -116,9 +118,11 @@ boolean getNeedClientAuth() { } void setNeedClientAuth(boolean needClientAuth) { - /* wantClientAuth OR needClientAuth can be set, not both */ + /* wantClientAuth OR needClientAuth can be set true, not both */ this.needClientAuth = needClientAuth; - this.wantClientAuth = false; + if (this.needClientAuth) { + this.wantClientAuth = false; + } } String getEndpointIdentificationAlgorithm() { diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java index f1925267..13ada725 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Arrays; import java.nio.channels.SocketChannel; +import java.security.cert.CertificateEncodingException; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; @@ -131,8 +132,10 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(this, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException | + IOException e) { throw new IOException(e); } } @@ -173,8 +176,10 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, port, host); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(this, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException | + IOException e) { throw new IOException(e); } } @@ -218,8 +223,10 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, port, address); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(this, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException | + IOException e) { throw new IOException(e); } } @@ -260,8 +267,10 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, port, host); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(this, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException | + IOException e) { throw new IOException(e); } } @@ -305,8 +314,10 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, port, host); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(this, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException | + IOException e) { throw new IOException(e); } } @@ -364,8 +375,10 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, port, host); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(this.socket, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException | + IOException e) { throw new IOException(e); } } @@ -799,8 +812,9 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, s.getPort(), s.getInetAddress()); EngineHelper.setUseClientMode(clientMode); + EngineHelper.LoadKeyAndCertChain(s, null); - } catch (WolfSSLException e) { + } catch (WolfSSLException | CertificateEncodingException e) { throw new IOException(e); } } @@ -850,6 +864,7 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore, this.params, s.getPort(), s.getInetAddress()); EngineHelper.setUseClientMode(false); + EngineHelper.LoadKeyAndCertChain(s, null); /* register custom receive callback to read consumed first */ if (consumed != null) { @@ -859,10 +874,9 @@ public WolfSSLSocket(com.wolfssl.WolfSSLContext context, this.ssl.setIOReadCtx(recvCtx); } - } catch (WolfSSLException e) { + } catch (WolfSSLException | WolfSSLJNIException | + CertificateEncodingException e) { throw new IOException(e); - } catch (WolfSSLJNIException jnie) { - throw new IOException(jnie); } } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java index 4717fbe5..638fa3ec 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java @@ -34,11 +34,13 @@ import java.security.Security; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; import java.security.KeyStore; import java.util.Random; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; +import java.net.Socket; import java.net.InetSocketAddress; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; @@ -46,6 +48,8 @@ import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -122,7 +126,7 @@ public void testSSLEngine() SSLEngine e; /* create new SSLEngine */ - System.out.print("\tTesting creation"); + System.out.print("\tSSLEngine creation"); for (int i = 0; i < enabledProtocols.size(); i++) { this.ctx = tf.createSSLContext(enabledProtocols.get(i), @@ -143,7 +147,7 @@ public void testSSLEngineSetCipher() String sup[]; boolean ok = false; - System.out.print("\tTesting setting cipher"); + System.out.print("\tSetting ciphersuite"); if (!WolfSSL.TLSv12Enabled()) { pass("\t\t... skipped"); @@ -203,7 +207,7 @@ public void testCipherConnection() Certificate[] certs; /* create new SSLEngine */ - System.out.print("\tTesting cipher connection"); + System.out.print("\tBasic ciphersuiet connection"); this.ctx = tf.createSSLContext("TLS", engineProvider); server = this.ctx.createSSLEngine(); @@ -262,24 +266,24 @@ public void testCipherConnection() } pass("\t... passed"); - System.out.print("\tTesting close connection"); + System.out.print("\tclose connection"); try { /* test close connection */ tf.CloseConnection(server, client, false); } catch (SSLException ex) { - error("\t... failed"); + error("\t\t... failed"); fail("failed to create engine"); } /* check if inbound is still open */ if (!server.isInboundDone() || !client.isInboundDone()) { - error("\t... failed"); + error("\t\t... failed"); fail("inbound is not done"); } /* check if outbound is still open */ if (!server.isOutboundDone() || !client.isOutboundDone()) { - error("\t... failed"); + error("\t\t... failed"); fail("outbound is not done"); } @@ -287,11 +291,11 @@ public void testCipherConnection() try { server.closeInbound(); } catch (SSLException ex) { - error("\t... failed"); + error("\t\t... failed"); fail("close inbound failure"); } - pass("\t... passed"); + pass("\t\t... passed"); } @Test @@ -302,7 +306,7 @@ public void testBeginHandshake() int ret; /* create new SSLEngine */ - System.out.print("\tTesting begin handshake"); + System.out.print("\tbeginHandshake()"); this.ctx = tf.createSSLContext("TLS", engineProvider); server = this.ctx.createSSLEngine(); @@ -336,7 +340,7 @@ public void testConnectionOutIn() int ret; /* create new SSLEngine */ - System.out.print("\tTesting out/in bound"); + System.out.print("\tisIn/OutboundDone()"); this.ctx = tf.createSSLContext("TLS", engineProvider); server = this.ctx.createSSLEngine(); @@ -383,7 +387,7 @@ public void testSetUseClientMode() SSLEngine client; SSLEngine server; - System.out.print("\tTesting setUseClientMode()"); + System.out.print("\tsetUseClientMode()"); /* expected to fail, not calling setUseClientMode() */ this.ctx = tf.createSSLContext("TLS", engineProvider); @@ -393,7 +397,7 @@ public void testSetUseClientMode() server.setNeedClientAuth(false); try { ret = tf.testConnection(server, client, null, null, "Testing"); - error("\t... failed"); + error("\t\t... failed"); fail("did not fail without setUseClientMode()"); } catch (IllegalStateException e) { /* expected */ @@ -407,7 +411,7 @@ public void testSetUseClientMode() client.setUseClientMode(true); try { ret = tf.testConnection(server, client, null, null, "Testing"); - error("\t... failed"); + error("\t\t... failed"); fail("did not fail without server.setUseClientMode()"); } catch (IllegalStateException e) { /* expected */ @@ -421,7 +425,7 @@ public void testSetUseClientMode() server.setUseClientMode(false); try { ret = tf.testConnection(server, client, null, null, "Testing"); - error("\t... failed"); + error("\t\t... failed"); fail("did not fail without client.setUseClientMode()"); } catch (IllegalStateException e) { /* expected */ @@ -438,11 +442,11 @@ public void testSetUseClientMode() ret = tf.testConnection(server, client, null, null, "Testing"); } catch (IllegalStateException e) { e.printStackTrace(); - error("\t... failed"); + error("\t\t... failed"); fail("failed with setUseClientMode(), should succeed"); } - pass("\t... passed"); + pass("\t\t... passed"); } @Test @@ -453,7 +457,7 @@ public void testMutualAuth() int ret; /* create new SSLEngine */ - System.out.print("\tTesting mutual auth"); + System.out.print("\tMutual authentication"); /* success case */ this.ctx = tf.createSSLContext("TLS", engineProvider); @@ -496,6 +500,437 @@ public void testMutualAuth() pass("\t\t... passed"); } + /** + * Helper class used with below setWant/NeedClientAuth() test methods. + * + * Note that setWantClientAuth() and setNeedClientAuth() are only + * applicable when called on the server side. But including testing + * then when called on the client side here as well, for sanity. + */ + private static class PeerAuthConfig { + boolean clientWantClientAuth; + boolean clientNeedClientAuth; + boolean serverWantClientAuth; + boolean serverNeedClientAuth; + boolean expectSuccess; + + public PeerAuthConfig(boolean cwca, boolean cnca, boolean swca, + boolean snca, boolean ex) { + this.clientWantClientAuth = cwca; + this.clientNeedClientAuth = cnca; + this.serverWantClientAuth = swca; + this.serverNeedClientAuth = snca; + this.expectSuccess = ex; + } + } + + @Test + public void testSetWantNeedClientAuth_ClientServerDefaultKeyManager() + throws NoSuchProviderException, NoSuchAlgorithmException { + + int ret = 0; + SSLContext cCtx = null; + SSLContext sCtx = null; + SSLEngine client = null; + SSLEngine server = null; + + /* All combinations using DEFAULT X509TrustManager/X509KeyManager + * from WolfSSLTestFactory. All expected to pass since each since has + * certs/keys loaded no matter if verify is being done */ + PeerAuthConfig[] configsDefaultManagers = new PeerAuthConfig[] { + new PeerAuthConfig(true, true, true, true, true), + new PeerAuthConfig(true, true, true, false, true), + new PeerAuthConfig(true, true, false, true, true), + new PeerAuthConfig(true, true, false, false, true), + new PeerAuthConfig(true, false, true, true, true), + new PeerAuthConfig(true, false, true, false, true), + new PeerAuthConfig(true, false, false, true, true), + new PeerAuthConfig(true, false, false, false, true), + new PeerAuthConfig(false, true, true, true, true), + new PeerAuthConfig(false, true, true, false, true), + new PeerAuthConfig(false, true, false, true, true), + new PeerAuthConfig(false, true, false, false, true), + new PeerAuthConfig(false, false, true, true, true), + new PeerAuthConfig(false, false, true, false, true), + new PeerAuthConfig(false, false, false, true, true), + new PeerAuthConfig(false, false, false, false, true) + }; + + System.out.print("\tsetWantClientAuth(default KM)"); + + for (PeerAuthConfig c : configsDefaultManagers) { + + sCtx = tf.createSSLContext("TLS", engineProvider); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContext("TLS", engineProvider); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret); + } + } + + pass("\t... passed"); + } + + @Test + public void testSetWantNeedClientAuth_ClientNoKeyManager() + throws NoSuchProviderException, NoSuchAlgorithmException { + + int ret = 0; + SSLContext cCtx = null; + SSLContext sCtx = null; + SSLEngine client = null; + SSLEngine server = null; + + /* All combinations using 'null' as client KeyManager, so client + * will not have cert or private key loaded, but server will */ + PeerAuthConfig[] configs = new PeerAuthConfig[] { + new PeerAuthConfig(true, true, true, true, false), + new PeerAuthConfig(true, true, true, false, true), + 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, 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, 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, false, true, false), + new PeerAuthConfig(false, false, false, false, true) + }; + + System.out.print("\tsetWantClientAuth(no client KM)"); + + for (PeerAuthConfig c : configs) { + + sCtx = tf.createSSLContext("TLS", engineProvider); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, + tf.createTrustManager("SunX509", tf.clientJKS, engineProvider), + null); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret); + } + } + + pass("\t... passed"); + } + + @Test + public void testSetWantNeedClientAuth_ServerNoKeyManager() + throws NoSuchProviderException, NoSuchAlgorithmException { + + int ret = 0; + SSLContext cCtx = null; + SSLContext sCtx = null; + SSLEngine client = null; + SSLEngine server = null; + + /* All combinations using 'null' as server KeyManager, so server + * will not have cert or private key loaded, but client will. + * All these should fail, since the server requires a private key + * be loaded. */ + PeerAuthConfig[] configs = new PeerAuthConfig[] { + new PeerAuthConfig(true, true, true, true, false), + new PeerAuthConfig(true, true, true, false, false), + new PeerAuthConfig(true, true, false, true, false), + new PeerAuthConfig(true, true, false, false, false), + new PeerAuthConfig(true, false, true, true, false), + new PeerAuthConfig(true, false, true, false, false), + new PeerAuthConfig(true, false, false, true, false), + new PeerAuthConfig(true, false, false, false, false), + new PeerAuthConfig(false, true, true, true, false), + new PeerAuthConfig(false, true, true, false, false), + new PeerAuthConfig(false, true, false, true, false), + new PeerAuthConfig(false, true, false, false, false), + new PeerAuthConfig(false, false, true, true, false), + new PeerAuthConfig(false, false, true, false, false), + new PeerAuthConfig(false, false, false, true, false), + new PeerAuthConfig(false, false, false, false, false) + }; + + System.out.print("\tsetWantClientAuth(no server KM)"); + + for (PeerAuthConfig c : configs) { + + sCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, + tf.createTrustManager("SunX509", tf.clientJKS, engineProvider), + null); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContext("TLS", engineProvider); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret); + } + } + + pass("\t... passed"); + } + + @Test + public void testSetWantNeedClientAuth_ClientServerExternalTrustAllCerts() + throws NoSuchProviderException, NoSuchAlgorithmException { + + int ret = 0; + SSLContext cCtx = null; + SSLContext sCtx = null; + SSLEngine client = null; + SSLEngine server = null; + + /* TrustManager that trusts all certificates */ + TrustManager[] trustAllCerts = { + new X509ExtendedTrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + public void checkClientTrusted(X509Certificate[] chain, + String authType) { + } + public void checkClientTrusted(X509Certificate[] chain, + String authType, Socket socket) { + } + public void checkClientTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) { + } + public void checkServerTrusted(X509Certificate[] chain, + String authType) { + } + public void checkServerTrusted(X509Certificate[] chain, + String authType, Socket socket) { + } + public void checkServerTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) { + } + } + }; + + /* All combinations using DEFAULT X509TrustManager/X509KeyManager + * from WolfSSLTestFactory. All expected to pass since each since has + * certs/keys loaded no matter if verify is being done */ + PeerAuthConfig[] configsDefaultManagers = new PeerAuthConfig[] { + new PeerAuthConfig(true, true, true, true, true), + new PeerAuthConfig(true, true, true, false, true), + new PeerAuthConfig(true, true, false, true, true), + new PeerAuthConfig(true, true, false, false, true), + new PeerAuthConfig(true, false, true, true, true), + new PeerAuthConfig(true, false, true, false, true), + new PeerAuthConfig(true, false, false, true, true), + new PeerAuthConfig(true, false, false, false, true), + new PeerAuthConfig(false, true, true, true, true), + new PeerAuthConfig(false, true, true, false, true), + new PeerAuthConfig(false, true, false, true, true), + new PeerAuthConfig(false, true, false, false, true), + new PeerAuthConfig(false, false, true, true, true), + new PeerAuthConfig(false, false, true, false, true), + new PeerAuthConfig(false, false, false, true, true), + new PeerAuthConfig(false, false, false, false, true) + }; + + System.out.print("\tsetWantClientAuth(ext KM all)"); + + for (PeerAuthConfig c : configsDefaultManagers) { + + sCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, + trustAllCerts, + tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, + trustAllCerts, + tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret); + } + } + + pass("\t... passed"); + } + + @Test + public void testSetWantNeedClientAuth_ExternalTrustNoClientCerts() + throws NoSuchProviderException, NoSuchAlgorithmException { + + int ret = 0; + SSLContext cCtx = null; + SSLContext sCtx = null; + SSLEngine client = null; + SSLEngine server = null; + + /* TrustManager that trusts no certificates */ + TrustManager[] trustNoClientCerts = { + new X509ExtendedTrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + throw new CertificateException( + "fail on purpose / bad cert"); + } + public void checkClientTrusted(X509Certificate[] chain, + String authType, Socket socket) + throws CertificateException { + throw new CertificateException( + "fail on purpose / bad cert"); + } + public void checkClientTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) + throws CertificateException { + throw new CertificateException( + "fail on purpose / bad cert"); + } + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + /* Accept all server certs, not in scope of + * setWant/NeedClientAuth() */ + } + public void checkServerTrusted(X509Certificate[] chain, + String authType, Socket socket) + throws CertificateException { + /* Accept all server certs, not in scope of + * setWant/NeedClientAuth() */ + } + public void checkServerTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) + throws CertificateException { + /* Accept all server certs, not in scope of + * setWant/NeedClientAuth() */ + } + } + }; + + /* All combinations with external X509ExtendedTrustManager registered + * which will trust NO client certs and ALL server certs */ + PeerAuthConfig[] configsDefaultManagers = new PeerAuthConfig[] { + new PeerAuthConfig(true, true, true, true, false), + new PeerAuthConfig(true, true, true, false, true), + 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, 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, 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, false, true, false), + new PeerAuthConfig(false, false, false, false, true) + }; + + System.out.print("\tsetWantClientAuth(ext KM no)"); + + for (PeerAuthConfig c : configsDefaultManagers) { + + sCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, + trustNoClientCerts, + tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); + server = sCtx.createSSLEngine(); + server.setUseClientMode(false); + server.setWantClientAuth(c.serverWantClientAuth); + server.setNeedClientAuth(c.serverNeedClientAuth); + + cCtx = tf.createSSLContextNoDefaults("TLS", engineProvider, + trustNoClientCerts, + tf.createKeyManager("SunX509", tf.clientJKS, engineProvider)); + client = cCtx.createSSLEngine("wolfSSL test case", 11111); + client.setUseClientMode(true); + client.setWantClientAuth(c.clientWantClientAuth); + client.setNeedClientAuth(c.clientNeedClientAuth); + + ret = tf.testConnection(server, client, null, null, "Test"); + if ((c.expectSuccess && ret != 0) || + (!c.expectSuccess && ret == 0)) { + error("\t... failed"); + fail("SSLEngine want/needClientAuth failed: \n" + + "\n cWantClientAuth = " + c.clientWantClientAuth + + "\n cNeedClientAuth = " + c.clientNeedClientAuth + + "\n sWantClientAuth = " + c.serverWantClientAuth + + "\n sNeedClientAuth = " + c.serverNeedClientAuth + + "\n expectSuccess = " + c.expectSuccess + + "\n got ret = " + ret); + } + } + + pass("\t... passed"); + } + + @Test public void testReuseSession() throws NoSuchProviderException, NoSuchAlgorithmException { @@ -504,7 +939,7 @@ public void testReuseSession() int ret; /* create new SSLEngine */ - System.out.print("\tTesting reuse of session"); + System.out.print("\tSession reuse"); this.ctx = tf.createSSLContext("TLS", engineProvider); server = this.ctx.createSSLEngine(); @@ -515,7 +950,7 @@ public void testReuseSession() client.setUseClientMode(true); ret = tf.testConnection(server, client, null, null, "Test reuse"); if (ret != 0) { - error("\t... failed"); + error("\t\t\t... failed"); fail("failed to create engine"); } @@ -523,7 +958,7 @@ public void testReuseSession() /* test close connection */ tf.CloseConnection(server, client, false); } catch (SSLException ex) { - error("\t... failed"); + error("\t\t\t... failed"); fail("failed to create engine"); } @@ -535,22 +970,23 @@ public void testReuseSession() client.setUseClientMode(true); ret = tf.testConnection(server, client, null, null, "Test reuse"); if (ret != 0) { - error("\t... failed"); + error("\t\t\t... failed"); fail("failed to create engine"); } try { /* test close connection */ tf.CloseConnection(server, client, false); } catch (SSLException ex) { - error("\t... failed"); + error("\t\t\t... failed"); fail("failed to create engine"); } - if (client.getEnableSessionCreation() || !server.getEnableSessionCreation()) { - error("\t... failed"); + if (client.getEnableSessionCreation() || + !server.getEnableSessionCreation()) { + error("\t\t\t... failed"); fail("bad enabled session creation"); } - pass("\t... passed"); + pass("\t\t\t... passed"); } /** @@ -591,7 +1027,7 @@ public void testExtendedThreadingUse() failures.set(0, 0); success.set(0, 0); - System.out.print("\tTesting ExtendedThreadingUse"); + System.out.print("\tExtended threading use"); /* Start up simple TLS test server */ CountDownLatch serverOpenLatch = new CountDownLatch(1); @@ -627,14 +1063,16 @@ public void testExtendedThreadingUse() /* check failure count and success count against thread count */ if (failures.get(0) == 0 && success.get(0) == numThreads) { - pass("\t... passed"); + pass("\t\t... passed"); } else { if (returnWithoutTimeout == true) { + error("\t\t... failed"); fail("SSLEngine threading error: " + failures.get(0) + " failures, " + success.get(0) + " success, " + numThreads + " num threads total"); } else { + error("\t\t... failed"); fail("SSLEngine threading error, threads timed out"); } } @@ -776,7 +1214,8 @@ public void connect() throws Exception { case NOT_HANDSHAKING: break; default: - throw new Exception("Invalid HandshakeStatus"); + throw new Exception("Invalid HandshakeStatus: " + + hsStatus); } hsStatus = engine.getHandshakeStatus(); } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java index 2d8b75f1..d8513415 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java @@ -390,7 +390,9 @@ protected SSLContext createSSLContext(String protocol, String provider) { } /** - * Creates a new context using provider passed in and km/tm + * Creates a new context using provider passed in and km/tm. Falls back + * and creates default TrustManager/KeyManager if those arguments are + * null. * * @param protocol to be used when creating context * @param provider to be used when creating context (can be null) @@ -403,6 +405,42 @@ protected SSLContext createSSLContext(String protocol, String provider, return internalCreateSSLContext(protocol, provider, tm, km); } + /** + * Creates a new context using provider passed in and km/tm, does not + * fallback and create default TrustManager/KeyManager if thoes arguments + * are null. + * + * @param protocol to be used when creating context + * @param provider to be used when creating context (can be null) + * @param tm trust manager to use (can be null) + * @param km key manager to use (can be null) + * @return new SSLContext on success and null on failure + */ + protected SSLContext createSSLContextNoDefaults(String protocol, + String provider, TrustManager[] tm, KeyManager[] km) { + + SSLContext ctx = null; + + try { + if (provider != null) { + ctx = SSLContext.getInstance(protocol, provider); + } else { + ctx = SSLContext.getInstance(protocol); + } + + ctx.init(km, tm, null); + return ctx; + + } catch (NoSuchAlgorithmException ex) { + ex.printStackTrace(); + } catch (KeyManagementException ex) { + ex.printStackTrace(); + } catch (NoSuchProviderException ex) { + ex.printStackTrace(); + } + return null; + } + /** * Red coloring to fail message * @param msg