/* WolfSSLContext.java * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * wolfSSL 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ package com.wolfssl.provider.jsse; import java.security.KeyManagementException; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import javax.security.auth.x500.X500Principal; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContextSpi; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import com.wolfssl.WolfSSL; import com.wolfssl.WolfSSLDebug; import com.wolfssl.WolfSSL.TLS_VERSION; import com.wolfssl.WolfSSLException; import com.wolfssl.WolfSSLJNIException; /** * wolfSSL implementation of SSLContextSpi * @author wolfSSL */ public class WolfSSLContext extends SSLContextSpi { private TLS_VERSION currentVersion = TLS_VERSION.SSLv23; private WolfSSLAuthStore authStore = null; private com.wolfssl.WolfSSLContext ctx = null; private WolfSSLParameters params = null; /* Created during construction to allow access before init() is called */ private WolfSSLSessionContext serverSessionCtx = null; private WolfSSLSessionContext clientSessionCtx = null; /* Default session cache size of 33 to match native wolfSSL default */ private static final int DEFAULT_CACHE_SIZE = 33; private WolfSSLContext(TLS_VERSION version) { this.currentVersion = version; this.serverSessionCtx = new WolfSSLSessionContext( DEFAULT_CACHE_SIZE, WolfSSL.WOLFSSL_SERVER_END); this.clientSessionCtx = new WolfSSLSessionContext( DEFAULT_CACHE_SIZE, WolfSSL.WOLFSSL_CLIENT_END); } private void createCtx() throws WolfSSLException { long method; String[] ciphersIana = null; /* Enable native wolfSSL debug logging if 'wolfssl.debug' * System property is set. Also attempted in WolfSSLProvider * but System property may not have been set by user yet at that * point. */ WolfSSLDebug.setNativeWolfSSLDebugging(); /* Get available wolfSSL cipher suites in IANA format */ ciphersIana = WolfSSL.getCiphersAvailableIana(this.currentVersion); /* Allow ability for user to hard-code and override version, cipher * suite, and NO_* disable options. Otherwise just sets defaults * into ctxAttr. */ WolfSSLCustomUser ctxAttr = WolfSSLCustomUser.getCtxAttributes (this.currentVersion, ciphersIana); /* Explicitly set SSLContext version if overridden by * WolfSSLCustomUser or specific SSLContext version was created * by user. Otherwise use default of SSLv23. Starts at highest TLS * protocol version supported by native wolfSSL then downgrades to * minimum native downgrade version. */ if(ctxAttr.version == TLS_VERSION.TLSv1 || ctxAttr.version == TLS_VERSION.TLSv1_1 || ctxAttr.version == TLS_VERSION.TLSv1_2 || ctxAttr.version == TLS_VERSION.TLSv1_3 || ctxAttr.version == TLS_VERSION.SSLv23 || ctxAttr.version == TLS_VERSION.DTLSv1_3) { this.currentVersion = ctxAttr.version; } else { throw new IllegalArgumentException( "Invalid SSL/TLS protocol version"); } /* Set SSLContext version. To be compatible with SunJSSE behavior, * the enabled protocols are less than or equal to the version * selected */ switch (this.currentVersion) { case TLSv1: method = WolfSSL.TLSv1_Method(); ctxAttr.noOptions = ctxAttr.noOptions | WolfSSL.SSL_OP_NO_TLSv1_1 | WolfSSL.SSL_OP_NO_TLSv1_2 | WolfSSL.SSL_OP_NO_TLSv1_3; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating WolfSSLContext with TLSv1"); break; case TLSv1_1: method = WolfSSL.TLSv1_1_Method(); ctxAttr.noOptions = ctxAttr.noOptions | WolfSSL.SSL_OP_NO_TLSv1_2 | WolfSSL.SSL_OP_NO_TLSv1_3; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating WolfSSLContext with TLSv1_1"); break; case TLSv1_2: method = WolfSSL.TLSv1_2_Method(); ctxAttr.noOptions = ctxAttr.noOptions | WolfSSL.SSL_OP_NO_TLSv1_3; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating WolfSSLContext with TLSv1_2"); break; case TLSv1_3: method = WolfSSL.TLSv1_3_Method(); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating WolfSSLContext with TLSv1_3"); break; case SSLv23: method = WolfSSL.SSLv23_Method(); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating WolfSSLContext with SSLv23"); break; case DTLSv1_3: method = WolfSSL.DTLSv1_3_Method(); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating WolfSSLContext with DTLSv1_3"); break; default: throw new IllegalArgumentException( "Invalid SSL/TLS protocol version"); } if (method == WolfSSL.NOT_COMPILED_IN) { throw new IllegalArgumentException("Protocol version not " + "compiled into native wolfSSL library"); } ctx = new com.wolfssl.WolfSSLContext(method); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "created new native WOLFSSL_CTX"); if(ctxAttr.list != null && ctxAttr.list.length > 0) { ciphersIana = ctxAttr.list; } else { ciphersIana = WolfSSL.getCiphersIana(); } /* Set minimum allowed RSA/DH/ECC key sizes */ enforceKeySizeLimitations(); /* Set native wolfSSL device ID (devId) */ setGlobalCryptoCallbackDevId(); /* Auto-populate enabled ciphersuites with supported ones. If suites * have been restricted with wolfjsse.enabledCipherSuites system * security property, the suite list will be filtered in * WolfSSLEngineHelper.sanitizeSuites() to adhere to any * set restrictions */ if (WolfSSLUtil.isSecurityPropertyStringSet( "wolfjsse.enabledCipherSuites")) { /* User is overriding cipher suites, set CTX list */ this.setCtxCiphers(WolfSSLUtil.sanitizeSuites(ciphersIana, true)); } params.setCipherSuites(WolfSSLUtil.sanitizeSuites(ciphersIana, true)); /* Auto-populate enabled protocols with supported ones. Protocols * which have been disabled via system property get filtered in * WolfSSLEngineHelper.sanitizeProtocols() */ params.setProtocols(WolfSSLUtil.sanitizeProtocols( this.getProtocolsMask(ctxAttr.noOptions), this.currentVersion)); try { LoadTrustedRootCerts(); } catch (Exception e) { throw new IllegalArgumentException(e); } } /** * Set native wolfSSL crypto callback device ID (devId). * The devId used defaults to WolfSSL.INVALID_DEVID but an app may * have set this globally via WolfSSLProvider.setDevId(). If not set * globally, applications may also set this via * SSLContext.setDevId() and SSLSocket.setDevId() * * @throws IllegalStateException if underlying WOLFSSL has been freed * @throws WolfSSLException if native wolfSSL/JNI error occurs */ private void setGlobalCryptoCallbackDevId() throws WolfSSLException { int ret = 0; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "setting wolfSSL devId: " + WolfSSL.devId); try { ret = this.ctx.setDevId(WolfSSL.devId); if (ret != WolfSSL.SSL_SUCCESS) { throw new WolfSSLException( "Error setting native wolfSSL device ID, ret = " + ret); } } catch (IllegalStateException e) { throw new WolfSSLException(e); } } /** * Set native WOLFSSL_CTX cipher suite list. * Converts String[] to colon-delimited cipher suite array, then * calls native wolfSSL_CTX_set_cipher_list(). */ private void setCtxCiphers(String[] suites) throws IllegalArgumentException { try { String list; StringBuilder sb = new StringBuilder(); if (suites == null || suites.length == 0) { /* use default cipher suites */ return; } for (String s : suites) { sb.append(s); sb.append(":"); } if (sb.length() > 0) { /* remove last : */ sb.deleteCharAt(sb.length() - 1); list = sb.toString(); if (this.ctx.setCipherList(list) != WolfSSL.SSL_SUCCESS) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "error setting WolfSSLContext cipher list: " + list); } } } catch (IllegalStateException e) { throw new IllegalArgumentException(e); } } /** * Set minimum supported key sizes for RSA/DH/ECC based on values * set by user in jdk.tls.disabledAlgorithms security property. * * @throws WolfSSLException Key size limitation fails to set in CTX */ private void enforceKeySizeLimitations() throws WolfSSLException { int minKeySize = 0; int ret = 0; minKeySize = WolfSSLUtil.getDisabledAlgorithmsKeySizeLimit("RSA"); ret = this.ctx.setMinRSAKeySize(minKeySize); if (ret != WolfSSL.SSL_SUCCESS) { throw new WolfSSLException( "Error setting SSLContext min RSA key size"); } minKeySize = WolfSSLUtil.getDisabledAlgorithmsKeySizeLimit("EC"); ret = this.ctx.setMinECCKeySize(minKeySize); if (ret != WolfSSL.SSL_SUCCESS) { throw new WolfSSLException( "Error setting SSLContext min ECC key size"); } minKeySize = WolfSSLUtil.getDisabledAlgorithmsKeySizeLimit("DH"); ret = this.ctx.setMinDHKeySize(minKeySize); if (ret != WolfSSL.SSL_SUCCESS) { throw new WolfSSLException( "Error setting SSLContext min DH key size"); } } private void LoadTrustedRootCerts() { int ret = 0; int loadedCACount = 0; /* extract root certs from X509TrustManager */ X509TrustManager tm = authStore.getX509TrustManager(); if (tm == null) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "internal TrustManager is null, no CAs to load"); return; } WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "Using X509TrustManager: " + tm.getClass().getName()); /* We only need to load trusted CA certificates into our native * WOLFSSL_CTX if we are doing internal verification with wolfSSL * native verify logic. Otherwise, the registered Java TrustManager * being used will handle verification (including CA lookup) when * we call checkClientTrusted()/checkServerTrusted(). * * If tm is not an instance of WolfSSLTrustX509, simply return * here since we do not need to interface with native verification */ if (!(tm instanceof com.wolfssl.provider.jsse.WolfSSLTrustX509)) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "Deferring verification to checkClientTrusted/ServerTrusted()"); return; } /* Get trusted/accepted root certificates from the X509TrustManager */ X509Certificate[] caList = tm.getAcceptedIssuers(); if (caList == null) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "internal TrustManager has no accepted issuers to load"); return; } if (caList.length == 0) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "internal TrustManager has no certs"); return; } WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "Number of certs in X509TrustManager: " + caList.length); /* Load accepted issuer certificates into native WOLFSSL_CTX to be * used in native wolfSSL verify logic */ for (int i = 0; i < caList.length; i++) { try { byte[] derCert = caList[i].getEncoded(); ret = ctx.loadVerifyBuffer(derCert, derCert.length, WolfSSL.SSL_FILETYPE_ASN1); if (ret == WolfSSL.SSL_SUCCESS) { loadedCACount++; } else { /* skip loading on failure, move to next */ continue; } } catch (CertificateEncodingException ce) { /* skip loading if encoding error is encountered */ WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "skipped loading CA, encoding error"); continue; } catch (WolfSSLJNIException we) { /* skip loading if wolfSSL fails to load der encoding */ WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "skipped loading CA, JNI exception"); continue; } final String sigAltName = caList[i].getSigAlgName(); final String subjectName = caList[i].getSubjectX500Principal().getName( X500Principal.RFC1779); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "loaded trusted root cert (" + sigAltName + "): " + subjectName); } if (caList.length > 0 && loadedCACount == 0) { throw new IllegalArgumentException("wolfSSL failed to load " + "any trusted CA certificates from TrustManager"); } } /* Format an array of objects as "[Class1, Class2, ...]" for debug * logging without invoking caller-supplied toString() on user-provided * KeyManager/TrustManager objects. */ private static String classNames(Object[] arr) { if (arr == null) { return "null"; } StringBuilder sb = new StringBuilder("["); for (int i = 0; i < arr.length; i++) { if (i > 0) { sb.append(", "); } sb.append(arr[i] == null ? "null" : arr[i].getClass().getName()); } sb.append("]"); return sb.toString(); } /** * Initializes a SSLContext. * * wolfJSSE currently selects the first KeyManager and TrustManager * in the input arrays to be used during the SSL/TLS context setup * and session. Native wolfSSL gets entropy directly based on how * the wolfSSL library has been compiled. SecureRandom is not used * by wolfJSSE. * * @param km - array of KeyManager objects * @param tm - array of TrustManager objects * @param sr - SecureRandom object * * @throws KeyManagementException if wolfJSSE fails to load and use * input objects. */ @Override protected void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom sr) throws KeyManagementException { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineInit(km=" + classNames(km) + ", tm=" + classNames(tm) + ", sr=" + (sr == null ? "null" : sr.getClass().getName()) + ")"); try { authStore = new WolfSSLAuthStore(km, tm, sr, currentVersion, this.serverSessionCtx, this.clientSessionCtx); params = new WolfSSLParameters(); createCtx(); /* Link authStore to session contexts for session operations */ this.serverSessionCtx.setWolfSSLAuthStore(authStore); this.clientSessionCtx.setWolfSSLAuthStore(authStore); } catch (IllegalArgumentException iae) { throw new KeyManagementException(iae); } catch (WolfSSLException we) { throw new KeyManagementException(we); } } /** * Creates a new wolfJSSE SSLSocketFactory. * * @throws IllegalStateException if SSLContext has not been initialized */ @Override protected SSLSocketFactory engineGetSocketFactory() throws IllegalStateException { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineGetSocketFactory()"); if (this.ctx == null || this.authStore == null) { throw new IllegalStateException("SSLContext must be initialized " + "before use, please call init()"); } return new WolfSSLSocketFactory(this.ctx, this.authStore, this.params); } /** * Creates a new wolfJSSE SSLServerSocketFactory. * * @throws IllegalStateException if SSLContext has not been initialized */ @Override protected SSLServerSocketFactory engineGetServerSocketFactory() throws IllegalStateException { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineGetServerSocketFactory()"); if (this.ctx == null || this.authStore == null) { throw new IllegalStateException("SSLContext must be initialized " + "before use, please call init()"); } return new WolfSSLServerSocketFactory(this.ctx, this.authStore, this.params); } /** * Creates a new wolfJSSE SSLEngine. * * @throws IllegalStateException if SSLContext has not been initialized */ @Override protected SSLEngine engineCreateSSLEngine() throws IllegalStateException { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineCreateSSLEngine()"); if (this.ctx == null || this.authStore == null) { throw new IllegalStateException("SSLContext must be initialized " + "before use, please call init()"); } try { return new WolfSSLEngine(this.ctx, this.authStore, this.params); } catch (WolfSSLException ex) { throw new IllegalStateException("Unable to create engine", ex); } } /** * Creates a new SSLEngine, using peer information as hints. * * @param host - name of the peer host * @param port - peer port * * @throws IllegalStateException if SSLContext has not been initialized */ @Override protected SSLEngine engineCreateSSLEngine(String host, int port) throws IllegalStateException { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineCreateSSLEngine(String host, int port)"); if (this.ctx == null || this.authStore == null) { throw new IllegalStateException("SSLContext must be initialized " + "before use, please call init()"); } try { return new WolfSSLEngine(this.ctx, this.authStore, this.params, host, port); } catch (WolfSSLException ex) { throw new IllegalStateException("Unable to create engine", ex); } } /** * Returns the SSLServerSessionContext associated with this SSLContext. * * Session contexts are created during SSLContext construction to allow * access before init() is called. * * @return SSLSessionContext for server sessions */ @Override protected SSLSessionContext engineGetServerSessionContext() { return this.serverSessionCtx; } /** * Returns the SSLClientSessionContext associated with this SSLContext. * * Session contexts are created during SSLContext construction to allow * access before init() is called. * * @return SSLSessionContext for client sessions */ @Override protected SSLSessionContext engineGetClientSessionContext() { return this.clientSessionCtx; } /** * Returns copy of SSLParameters with default settings for this SSLContext. */ @Override protected SSLParameters engineGetDefaultSSLParameters() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineGetDefaultSSLParameters()"); return WolfSSLParametersHelper.decoupleParams(this.params); } /** * Returns copy of SSLParameters with max supported settings for this * SSLContext. */ @Override protected SSLParameters engineGetSupportedSSLParameters() { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "entered engineGetSupportedSSLParameters()"); return WolfSSLParametersHelper.decoupleParams(this.params); } /** * Get WolfSSLAuthStore for this WolfSSLContext. * Used internally by SSLSocketFactory() * * @return WolfSSLAuthStore for this WolfSSLContext object */ protected WolfSSLAuthStore getInternalAuthStore() { return this.authStore; } /** * Get internal SSLParameters. * Used internally by SSLSocketFactory() * * @return WolfSSLParameters for this WolfSSLContext object */ protected WolfSSLParameters getInternalSSLParams() { return this.params; } /** * Get internal com.wolfssl.WolfSSLContext for this object. * Used internally by SSLSocketFactory() * * @return com.wolfssl.WolfSSLContext for this object */ protected com.wolfssl.WolfSSLContext getInternalWolfSSLContext() { return this.ctx; } @SuppressWarnings({"deprecation", "removal"}) @Override protected void finalize() throws Throwable { if (this.ctx != null) { this.ctx = null; } super.finalize(); } /** * Sets the WolfSSLContext options using specified protocol mask. * Also translates the protocol mask provided to an array of Strings * for the enabled SSL/TLS protocols. * * @param noOpt protocol mask set into native WOLFSSL_CTX * * @return String array of enabled SSL/TLS protocols for this * WolfSSLContext object */ private String[] getProtocolsMask(long noOpt) { if (ctx != null) { ctx.setOptions(noOpt); } return WolfSSL.getProtocolsMask(noOpt); } /** * SSLContext implementation supporting TLS 1.0 */ public static final class TLSV1_Context extends WolfSSLContext { /** * Create new TLSv1_Context, calls parent WolfSSLContext constructor */ public TLSV1_Context() { super(TLS_VERSION.TLSv1); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using TLSV1_Context"); } } /** * SSLContext implementation supporting TLS 1.1 */ public static final class TLSV11_Context extends WolfSSLContext { /** * Create new TLSv11_Context, calls parent WolfSSLContext constructor */ public TLSV11_Context() { super(TLS_VERSION.TLSv1_1); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using TLSV11_Context"); } } /** * SSLContext implementation supporting TLS 1.2 */ public static final class TLSV12_Context extends WolfSSLContext { /** * Create new TLSv12_Context, calls parent WolfSSLContext constructor */ public TLSV12_Context() { super(TLS_VERSION.TLSv1_2); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using TLSV12_Context"); } } /** * SSLContext implementation supporting TLS 1.3 */ public static final class TLSV13_Context extends WolfSSLContext { /** * Create new TLSv13_Context, calls parent WolfSSLContext constructor */ public TLSV13_Context() { super(TLS_VERSION.TLSv1_3); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using TLSV13_Context"); } } /** * TLSv23 SSLContext class. * Created using SSLv23 method, supporting highest protocol enabled * in native wolfSSL. Downgrades to native minimum downgrade level. */ public static final class TLSV23_Context extends WolfSSLContext { /** * Create new TLSv23_Context, calls parent WolfSSLContext constructor */ public TLSV23_Context() { super(TLS_VERSION.SSLv23); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using TLSV23_Context"); } } /** * SSLContext implementation supporting DTLS 1.3 */ public static final class DTLSV13_Context extends WolfSSLContext { /** * Create new DTLSv13_Context, calls parent WolfSSLContext constructor */ public DTLSV13_Context() { super(TLS_VERSION.DTLSv1_3); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using DTLSV13_Context"); } } /** * DEFAULT SSLContext class. * Created using SSLv23 method, supporting highest protocol enabled * in native wolfSSL. Downgrades to native minimum downgrade level. */ public static final class DEFAULT_Context extends WolfSSLContext { /** * Create new DEFAULT_Context, calls parent WolfSSLContext constructor * with TLS_VERSION.SSLv23 * * @throws IllegalStateException when engine init fails */ public DEFAULT_Context() { super(TLS_VERSION.SSLv23); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, () -> "creating new WolfSSLContext using DEFAULT_Context"); try { this.engineInit(null, null, null); } catch (Exception e) { throw new IllegalStateException("wolfSSL engine init failed"); } } } }