From 1e65f4c215a8c2c748fbf8a900258c34c38bae6a Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 14 Mar 2024 15:34:44 -0600 Subject: [PATCH 1/5] JSSE: add -sysca option to example clientJSSE, load system CA certs ignoring any passed in --- examples/provider/ClientJSSE.java | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/examples/provider/ClientJSSE.java b/examples/provider/ClientJSSE.java index 0e48e02a..0a509f98 100644 --- a/examples/provider/ClientJSSE.java +++ b/examples/provider/ClientJSSE.java @@ -81,8 +81,12 @@ public void run(String[] args) throws Exception { String cipherList = null; /* default ciphersuite list */ int sslVersion = 3; /* default to TLS 1.2 */ boolean verifyPeer = true; /* verify peer by default */ - boolean useEnvVar = false; /* load cert/key from enviornment variable */ - boolean listSuites = false; /* list all supported cipher suites */ + boolean useSysRoots = false; /* skip CA KeyStore load, + use system default roots */ + boolean useEnvVar = false; /* load cert/key from enviornment + variable */ + boolean listSuites = false; /* list all supported cipher + suites */ boolean listEnabledProtocols = false; /* show enabled protocols */ boolean putEnabledProtocols = false; /* set enabled protocols */ boolean sendGET = false; /* send HTTP GET */ @@ -187,6 +191,9 @@ public void run(String[] args) throws Exception { } else if (arg.equals("-profile")) { profileSleep = true; + } else if (arg.equals("-sysca")) { + useSysRoots = true; + } else { printUsage(); } @@ -230,10 +237,16 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { /* trust manager (certificates) */ if (verifyPeer) { - cert = KeyStore.getInstance("JKS"); - cert.load(new FileInputStream(caJKS), caPswd.toCharArray()); tm = TrustManagerFactory.getInstance("SunX509", provider); - tm.init(cert); + if (useSysRoots) { + /* Let wolfJSSE try to find/load default system CA certs */ + tm.init((KeyStore)null); + } + else { + cert = KeyStore.getInstance("JKS"); + cert.load(new FileInputStream(caJKS), caPswd.toCharArray()); + tm.init(cert); + } } /* load private key */ @@ -409,6 +422,8 @@ private void printUsage() { System.out.println("-d\t\tDisable peer checks"); System.out.println("-g\t\tSend server HTTP GET"); System.out.println("-e\t\tGet all supported cipher suites"); + System.out.println("-r\t\tResume session"); + System.out.println("-sysca\t\tLoad system CA certs, ignore any passed in"); System.out.println("-getp\t\tGet enabled protocols"); System.out.println("-setp \tSet enabled protocols " + "e.g \"TLSv1.1 TLSv1.2\""); @@ -416,7 +431,6 @@ private void printUsage() { "../provider/client.jks:wolfSSL test"); System.out.println("-A :\tCertificate/key CA JKS file,\tdefault " + "../provider/ca-server.jks:wolfSSL test"); - System.out.println("-r Resume session"); System.out.println("-profile\tSleep for 10 sec before/after running " + "to allow profilers to attach"); System.exit(1); From b80a989b4eb9ddf290fb2c74637589fc7bc4f28d Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 14 Mar 2024 15:36:05 -0600 Subject: [PATCH 2/5] JSSE: refactor KeyManager and TrustManager loading of KeyStore in engineInit(), add support for wolfJCE WKS KeyStore type --- .../provider/jsse/WolfSSLKeyManager.java | 185 ++-- .../provider/jsse/WolfSSLTrustManager.java | 817 ++++++++++++------ .../wolfssl/provider/jsse/WolfSSLUtil.java | 107 +++ 3 files changed, 764 insertions(+), 345 deletions(-) diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java b/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java index 4e75f189..dc1bdad2 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java @@ -40,13 +40,109 @@ * WolfSSL KeyManagerFactory implementation */ public class WolfSSLKeyManager extends KeyManagerFactorySpi { - private char[] pswd; - private KeyStore store; + private char[] pswd = null; + private KeyStore store = null; private boolean initialized = false; /** Default WolfSSLKeyManager constructor */ public WolfSSLKeyManager() { } + /** + * Try to load KeyStore from System properties if set. + * + * If a KeyStore file has been specified in the javax.net.ssl.keyStore + * System property, then we try to load it in the following ways: + * + * 1. Using type specified in javax.net.ssl.keyStoreType. If not given: + * 2. Using wolfJCE WKS type, if available + * 3. Using BKS type if on Android + * 4. Using JKS type if above all fail + * + * @return new KeyStore object that has been created and loaded using + * details specified in System properties. + * + * @throws KeyStoreException if javax.net.ssl.keyStore property is + * set but KeyStore fails to load + */ + private KeyStore LoadKeyStoreFromSystemProperties() + throws KeyStoreException { + + KeyStore sysStore = null; + InputStream stream = null; + String pass = System.getProperty("javax.net.ssl.keyStorePassword"); + String file = System.getProperty("javax.net.ssl.keyStore"); + String type = System.getProperty("javax.net.ssl.keyStoreType"); + boolean wksAvailable = false; + + if (file != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loading certs from: " + file); + + /* Check if wolfJCE WKS KeyStore is registered and available */ + wksAvailable = WolfSSLUtil.WKSAvailable(); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "wolfJCE WKS KeyStore type available: " + wksAvailable); + + /* Set KeyStore password if javax.net.ssl.keyStorePassword set */ + if (pass != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.keyStorePassword system property " + + "set, using password"); + this.pswd = pass.toCharArray(); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.keyStorePassword system property " + + "not set"); + } + + /* Keystore type given in property, try loading using it */ + if (type != null && !type.trim().isEmpty()) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.keyStoreType set: " + type); + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + file, this.pswd, type); + } + else { + /* Try with wolfJCE WKS type first, in case wolfCrypt + * FIPS is being used */ + if (wksAvailable) { + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + file, this.pswd, "WKS"); + } + + /* Try with BKS, if we're running on Android */ + if ((sysStore == null) && WolfSSLUtil.isAndroid()) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Detected Android VM, trying BKS KeyStore type"); + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + file, this.pswd, "BKS"); + } + + /* Try falling back to JKS */ + if (sysStore == null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.keyStoreType system property not set, " + + "trying type: JKS"); + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + file, this.pswd, "JKS"); + } + } + + if (sysStore == null) { + throw new KeyStoreException( + "Failed to load KeyStore from System properties, " + + "please double check settings"); + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loaded certs from KeyStore via System properties"); + } + } + + return sysStore; + } + @Override protected void engineInit(KeyStore store, char[] password) throws KeyStoreException, NoSuchAlgorithmException, @@ -58,84 +154,21 @@ protected void engineInit(KeyStore store, char[] password) WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "entering engineInit(KeyStore store, char[] password)"); - /* If no KeyStore passed in, try to load from system property values */ + /* If no KeyStore passed in, try to load from system property values + * if they have been set */ if (store == null) { - String pass = System.getProperty("javax.net.ssl.keyStorePassword"); - String file = System.getProperty("javax.net.ssl.keyStore"); - String type = System.getProperty("javax.net.ssl.keyStoreType"); - String vmVendor = System.getProperty("java.vm.vendor"); - InputStream stream = null; - - try { - if (file != null) { - if (pass != null) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.keyStorePassword system property " + - "set, using password"); - this.pswd = pass.toCharArray(); - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.keyStorePassword system property " + - "not set"); - } - - /* We default to use a JKS KeyStore type if not set at the - * system level, except on Android we use BKS */ - try { - if (type != null && !type.trim().isEmpty()) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.keyStoreType system property " + - "set: " + type); - certs = KeyStore.getInstance(type); - } else { - if (vmVendor != null && - vmVendor.equals("The Android Project")) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Detected Android VM, " + - "using BKS KeyStore type"); - certs = KeyStore.getInstance("BKS"); - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.keyStoreType system property " + - "not set, using type: JKS"); - certs = KeyStore.getInstance("JKS"); - } - } - } catch (KeyStoreException kse) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Unsupported KeyStore type: " + type); - throw kse; - } - - try { - /* initialize KeyStore, loading certs below will - * overwrite if needed, otherwise Android needs - * this to be initialized here */ - certs.load(null, null); - - } catch (Exception e) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Error initializing KeyStore with load(null, null)"); - throw e; - } - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Loading certs from " + file); - stream = new FileInputStream(file); - certs.load(stream, this.pswd); - stream.close(); - } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "input KeyStore null, trying to load KeyStore from " + + "system properties"); - } catch (FileNotFoundException ex) { - throw new KeyStoreException(ex); - } catch (IOException ex) { - throw new KeyStoreException(ex); - } catch (NoSuchAlgorithmException ex) { - throw new KeyStoreException(ex); - } catch (CertificateException ex) { - throw new KeyStoreException(ex); - } + certs = LoadKeyStoreFromSystemProperties(); } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "input KeyStore provided, using inside KeyManager"); + } + this.store = certs; this.initialized = true; } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java index 81209795..f20f631d 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java @@ -27,10 +27,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.ByteArrayInputStream; -import java.security.InvalidAlgorithmParameterException; +import java.security.Security; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.InvalidAlgorithmParameterException; import java.security.cert.CertificateException; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; @@ -54,304 +55,582 @@ public class WolfSSLTrustManager extends TrustManagerFactorySpi { /** Default WolfSSLTrustManager constructor */ public WolfSSLTrustManager() { } - /* Initialize TrustManager. Attempts to load CA certifciates as trusted - * roots into wolfSSL from user-provided KeyStore. If KeyStore is null, - * we attempt to load default system CA certificates in the following - * priority order: - * 1. javax.net.ssl.trustStore location, if set. Using password - * in javax.net.ssl.trustStorePassword. - * 2. $JAVA_HOME/lib/security/jssecacerts (JDK 9+) - * 3. $JAVA_HOME/jre/lib/security/jssecacerts (JDK <= 8) - * 4. $JAVA_HOME/lib/security/cacerts (JDK 9+) - * 5. $JAVA_HOME/jre/lib/security/cacerts (JDK <= 8) - * 6. /etc/ssl/certs/java/cacerts - * 7. Android: AndroidCAStore system KeyStore - * 8. Android: $ANDROID_ROOT/etc/security/cacerts + /** + * Try to load KeyStore from System properties if set. + * + * If a KeyStore file has been specified in the javax.net.ssl.keyStore + * System property, then we try to load it in the following ways: + * + * 1. Using type specified in javax.net.ssl.keyStoreType. If not given: + * 2. Using wolfJCE WKS type, if available + * 3. Using BKS type if on Android + * 4. Using JKS type if above all fail + * + * @param wksAvailable Boolean indicating if wolfJCE WKS KeyStore type + * is available, true if so, false if not + * @param tsPass javax.net.ssl.trustStorePassword system property + * value, or null + * @param tsFile javax.net.ssl.trustStore system property value, or null + * @param tsType javax.net.ssl.trustStoreType system property value, + * or null + * + * @return new KeyStore object that has been created and loaded using + * details specified in System properties. + * + * @throws KeyStoreException if javax.net.ssl.keyStore property is + * set but KeyStore fails to load */ - @Override - protected void engineInit(KeyStore in) throws KeyStoreException { - KeyStore certs = in; - - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "entered engineInit()"); + private KeyStore LoadKeyStoreFromSystemProperties(boolean wksAvailable, + String tsPass, String tsFile, String tsType) + throws KeyStoreException { + + char[] passArr = null; + KeyStore sysStore = null; + + if (tsFile != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loading certs from: " + tsFile); + + /* Set KeyStore password if javax.net.ssl.keyStorePassword set */ + if (tsPass != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.trustStorePassword system property " + + "set, using password"); + passArr = tsPass.toCharArray(); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.trustStorePassword system property " + + "not set"); + } - if (in == null) { - String pass = System.getProperty("javax.net.ssl.trustStorePassword"); - String file = System.getProperty("javax.net.ssl.trustStore"); - String type = System.getProperty("javax.net.ssl.trustStoreType"); - String vmVendor = System.getProperty("java.vm.vendor"); - String javaHome = System.getenv("JAVA_HOME"); - String androidRoot = System.getenv("ANDROID_ROOT"); - char[] passAr = null; - InputStream stream = null; - boolean systemCertsFound = false; - int aliasCnt = 0; + /* System keystore type set, try loading using it first */ + if (tsType != null && !tsType.trim().isEmpty()) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "javax.net.ssl.trustStoreType set: " + tsType); + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + tsFile, passArr, tsType); + } + else { + /* Try with wolfJCE WKS type first, in case wolfCrypt + * FIPS is being used */ + if (wksAvailable) { + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + tsFile, passArr, "WKS"); + } - try { - if (pass != null) { + /* Try with BKS, if we're running on Android */ + if (sysStore == null && WolfSSLUtil.isAndroid()) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.trustStorePassword system property " + - "set, using password"); - passAr = pass.toCharArray(); - } else { + "Detected Android VM, trying BKS KeyStore type"); + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + tsFile, passArr, "BKS"); + } + + /* Try falling back to JKS */ + if (sysStore == null) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.trustStorePassword system property " + - "not set"); + "javax.net.ssl.trustStoreType system property not set, " + + "trying type: JKS"); + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( + tsFile, passArr, "JKS"); } + } + + if (sysStore == null) { + throw new KeyStoreException( + "Failed to load KeyStore from System properties, " + + "please double check settings"); + } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loaded certs from KeyStore via System properties"); + } + } + + return sysStore; + } + + /** + * Try to load system CA certs from jssecacerts or cacerts KeyStore. + * + * Java 9+ has cacerts and/or jssecacerts under: + * $JAVA_HOME/lib/security/[jssecacerts | cacerts] + * Java 8 and earlier use: + * $JAVA_HOME/jre/lib/security/[jssecacerts | cacerts ] + * + * If wolfJCE WKS KeyStore type is available (ie: wolfJCE has been + * registered on this system), we first try to load jssecacerts.wks + * or cacerts.wks as WKS type KeyStore before falling back to trying to + * load KeyStore type specified in java.security by 'keystore.type' + * Security property. This is "JKS" type by default on my platforms. + * + * @param jh String value of $JAVA_HOME with trailing slash + * @param wksAvailable Boolean if wolfJCE WKS KeyStore typs is available + * @param tsPass javax.net.ssl.trustStorePassword, or null if not set + * @param certBundleName Name of system certificate bundle, either + * "jssecacerts" or "cacerts" + * + * @return KeyStore object loaded with CA certs from jssecacerts, or + * null if not able to find KeyStore or load certs + */ + private KeyStore LoadJavaSystemCerts(String jh, boolean wksAvailable, + String tsPass, String certBundleName) { + + char[] passArr = null; + KeyStore sysStore = null; + File f = null; + FileInputStream stream = null; + + /* Get default KeyStore type, set in java.security and normally JKS */ + String storeType = Security.getProperty("keystore.type"); + if (storeType != null) { + storeType = storeType.toUpperCase(); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "keystore.type Security property set: " + storeType); + } + + if (wksAvailable) { + /* First try wolfJCE WKS converted version for Java 9+ */ + f = new File(jh.concat("lib/security/") + .concat(certBundleName).concat(".wks")); + + /* Second try wolfJCE WKS converted version for Java <= 8 */ + if (!f.exists()) { + f = new File(jh.concat("jre/lib/security/") + .concat(certBundleName).concat(".wks")); + } + + if (f.exists()) { + storeType = "WKS"; + } + } + + /* Third try normal Java 9+ location */ + if ((f == null) || !f.exists()) { + f = new File(jh.concat("lib/security/").concat(certBundleName)); + } + + /* Fourth try normal Java <= 8 location */ + if (!f.exists()) { + f = new File(jh.concat("jre/lib/security/").concat(certBundleName)); + } + + if (f.exists()) { + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loading certs from " + f.getAbsolutePath()); + + try { + sysStore = KeyStore.getInstance(storeType); + } catch (KeyStoreException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to get KeyStore of type: " + storeType); + return null; + } + try { + sysStore.load(null, null); + } catch (IOException | NoSuchAlgorithmException | + CertificateException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to load empty KeyStore(" + storeType + ")"); + return null; + } + + if (tsPass != null) { + passArr = tsPass.toCharArray(); + } - /* default to JKS KeyStore type if not set at system level */ - /* We default to use a JKS KeyStore type if not set at the - * system level, except on Android we use BKS */ + try { + stream = new FileInputStream(f); + } catch (FileNotFoundException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to open KeyStore file for reading: " + + f.getAbsolutePath()); + } + + try { + sysStore.load(stream, passArr); + + } catch (IOException | NoSuchAlgorithmException | + CertificateException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to load KeyStore with file stream: " + + f.getAbsolutePath()); + + } finally { try { - if (type != null && !type.trim().isEmpty()) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.trustStoreType system property " + - "set: " + type); - certs = KeyStore.getInstance(type); - } else { - if (vmVendor != null && - vmVendor.equals("The Android Project")) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Detected Android VM, using BKS KeyStore type"); - certs = KeyStore.getInstance("BKS"); - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.trustStoreType system property " + - "not set, using type: JKS"); - certs = KeyStore.getInstance("JKS"); - } + if (stream != null) { + stream.close(); } - } catch (KeyStoreException kse) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Unsupported KeyStore type: " + type); - throw kse; + } catch (IOException e) { } + } + + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "$JAVA_HOME/(jre/)lib/security/" + + certBundleName + ": not found"); + } + + return sysStore; + } + + private KeyStore LoadSystemJsseCaCerts(String jh, boolean wksAvailable, + String tsPass) { + + return LoadJavaSystemCerts(jh, wksAvailable, tsPass, "jssecacerts"); + } + + private KeyStore LoadSystemCaCerts(String jh, boolean wksAvailable, + String tsPass) { + + return LoadJavaSystemCerts(jh, wksAvailable, tsPass, "cacerts"); + } + + /** + * Try to load system CA certs from common KeyStore locations. + * + * Currently includes: + * 1. /etc/ssl/certs/java/cacerts + * + */ + private KeyStore LoadCommonSystemCerts(boolean wksAvailable, + String tsPass) { + char[] passArr = null; + File f = null; + FileInputStream stream = null; + KeyStore sysStore = null; + + f = new File("/etc/ssl/certs/java/cacerts"); + + if (f.exists()) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Loading certs from " + f.getAbsolutePath()); + + if (tsPass != null) { + passArr = tsPass.toCharArray(); + } + + try { + stream = new FileInputStream(f); + } catch (FileNotFoundException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to open KeyStore file for reading: " + + f.getAbsolutePath()); + } + + try { + sysStore.load(stream, passArr); + + } catch (IOException | NoSuchAlgorithmException | + CertificateException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to load KeyStore with file stream: " + + f.getAbsolutePath()); + + } finally { try { - /* initialize KeyStore, loading certs below will - * overwrite if needed, otherwise Android needs - * this to be initialized here */ - certs.load(null, null); + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + } + } - } catch (Exception e) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Error initializing KeyStore with load(null, null)"); - throw new KeyStoreException(e); + } else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "/etc/ssl/certs/java/cacerts: not found"); + } + + return sysStore; + } + + /** + * Try to load Android system CA certs from AndroidCAStore KeyStore. + * + * The AndroidCAStore KeyStore is pre-loaded with Android system CA + * certs. We try to load this first before going on to load root certs + * manually, since it's already pre-imported and set up. + * + * @return KeyStore object referencing AndroidCAStore, or null if not + * found or not able to be loaded + */ + private KeyStore LoadAndroidCAStore() { + + KeyStore sysStore = null; + + try { + sysStore = KeyStore.getInstance("AndroidCAStore"); + + } catch (KeyStoreException e) { + /* Error finding AndroidCAStore */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "AndroidCAStore KeyStore not found, not loading"); + return null; + } + + try { + sysStore.load(null, null); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Using AndroidCAStore KeyStore for default system certs"); + + } catch (IOException | NoSuchAlgorithmException | + CertificateException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to load AndroidCAStore with null args"); + return null; + } + + return sysStore; + } + + /** + * Try to load Android system root certificates manually by reading + * all PEM certificates in [android_root]/etc/security/cacerts directory. + * + * @return KeyStore object containing Android system CA certificates, or + * null if none found or error loading any certs + */ + private KeyStore LoadAndroidSystemCertsManually() { + + int aliasCnt = 0; + byte[] derArray = null; + KeyStore sysStore = null; + CertificateFactory cfactory = null; + ByteArrayInputStream bis = null; + Certificate tmpCert = null; + String androidRoot = System.getenv("ANDROID_ROOT"); + + if (androidRoot != null) { + + /* Android default KeyStore type is BKS */ + try { + sysStore = KeyStore.getInstance("BKS"); + } catch (KeyStoreException e) { + /* Unable to get or load empty BKS KeyStore type */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Unable to get or load BKS KeyStore instance"); + return null; + } + + try { + sysStore.load(null, null); + + } catch (IOException | NoSuchAlgorithmException | + CertificateException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to load BKS KeyStore with null args"); + return null; + } + + /* Add trailing slash if not there already */ + if (!androidRoot.endsWith("/") && + !androidRoot.endsWith("\\")) { + androidRoot = androidRoot.concat("/"); + } + + String caStoreDir = androidRoot.concat("etc/security/cacerts"); + File cadir = new File(caStoreDir); + String[] cafiles = null; + try { + cafiles = cadir.list(); + } catch (Exception e) { + /* Denied access reading cacerts directory */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Permission error when trying to read system " + + "CA certificates"); + return null; + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Found " + cafiles.length + " CA files to load into KeyStore"); + + /* Get factory for cert creation */ + try { + cfactory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Not able to get X.509 CertificateFactory instance"); + return null; + } + + /* Loop over all PEM certs */ + for (String cafile : cafiles) { + + WolfSSLCertificate certPem = null; + String fullCertPath = caStoreDir.concat("/"); + fullCertPath = fullCertPath.concat(cafile); + + try { + certPem = new WolfSSLCertificate( + fullCertPath, WolfSSL.SSL_FILETYPE_PEM); + } catch (WolfSSLException we) { + /* skip, error parsing PEM */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipped loading cert: " + fullCertPath); + if (certPem != null) { + certPem.free(); + } + continue; } - if (file == null) { + try { + derArray = certPem.getDer(); + } catch (WolfSSLJNIException e) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.trustStore system property not set, " + - "trying to load system certs"); - - /* try to load trusted system certs if possible */ - if (javaHome != null) { - if (!javaHome.endsWith("/") && - !javaHome.endsWith("\\")) { - /* add trailing slash if not there already */ - javaHome = javaHome.concat("/"); - } + "Error getting DER from PEM cert, skipping: " + + fullCertPath); + } finally { + certPem.free(); + } - /* Java 9+ have cacerts under: - * $JAVA_HOME/lib/security/cacerts - * Java 8 and earlier use: - * $JAVA_HOME/jre/lib/security/cacerts - */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "$JAVA_HOME = " + javaHome); - - /* trying: "lib/security/jssecacerts" */ - File f = new File(javaHome.concat( - "lib/security/jssecacerts")); - if (!f.exists()) { - f = new File(javaHome.concat( - "jre/lib/security/jssecacerts")); - } - if (f.exists()) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Loading certs from " + f.getAbsolutePath()); - stream = new FileInputStream(f); - certs.load(stream, passAr); - stream.close(); - systemCertsFound = true; - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "$JAVA_HOME/(jre/)lib/security/jssecacerts: " + - "not found"); - } + bis = new ByteArrayInputStream(derArray); - /* trying: "lib/security/cacerts" */ - f = new File(javaHome.concat("lib/security/cacerts")); - if (!f.exists()) { - f = new File(javaHome.concat( - "jre/lib/security/cacerts")); - } - if (f.exists()) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Loading certs from " + f.getAbsolutePath()); - stream = new FileInputStream(f); - certs.load(stream, passAr); - stream.close(); - systemCertsFound = true; - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "$JAVA_HOME/(jre/)lib/security/cacerts: " + - "not found"); - } - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "$JAVA_HOME not set, unable to load system certs"); - } + try { + tmpCert = cfactory.generateCertificate(bis); - if (systemCertsFound == false) { - /* try loading from common system paths */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Trying to load system certs from common " + - "system paths"); - - /* trying: "/etc/ssl/certs/java/cacerts" */ - File f = new File("/etc/ssl/certs/java/cacerts"); - if (f.exists()) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Loading certs from " + f.getAbsolutePath()); - stream = new FileInputStream(f); - certs.load(stream, passAr); - stream.close(); - systemCertsFound = true; - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "/etc/ssl/certs/java/cacerts: not found"); + } catch (CertificateException ce) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Error generating certificate from " + + "ByteArrayInputStream, skipped loading cert: " + + fullCertPath); + continue; + + } finally { + try { + if (bis != null) { + bis.close(); } + } catch (IOException e) { } + } - if (androidRoot != null) { - - /* first try to use AndroidCAStore KeyStore, this is - * pre-loaded with Android system CA certs */ - try { - certs = KeyStore.getInstance("AndroidCAStore"); - certs.load(null, null); - systemCertsFound = true; - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Using AndroidCAStore KeyStore for default " + - "system certs"); - } catch (KeyStoreException e) { - /* error finding AndroidCAStore */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "AndroidCAStore KeyStore not found, trying " + - "to manually load system certs"); - systemCertsFound = false; - } + String aliasString = "alias" + aliasCnt; + try { + sysStore.setCertificateEntry(aliasString, tmpCert); + } catch (KeyStoreException kse) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, + "Error setting certificate entry in " + + "KeyStore, skipping loading cert: " + + fullCertPath); + continue; + } - /* Otherwise, try to manually load system certs */ - if (systemCertsFound == false) { - if (!androidRoot.endsWith("/") && - !androidRoot.endsWith("\\")) { - /* add trailing slash if not there already */ - androidRoot = androidRoot.concat("/"); - } - - String caStoreDir = androidRoot.concat( - "etc/security/cacerts"); - File cadir = new File(caStoreDir); - String[] cafiles = null; - try { - cafiles = cadir.list(); - } catch (Exception e) { - /* denied access reading cacerts directory */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Permission error when trying to read " + - "system CA certificates"); - throw new KeyStoreException(e); - } - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Found " + cafiles.length + " CA files to load " + - "into KeyStore"); - - /* get factory for cert creation */ - CertificateFactory cfactory = - CertificateFactory.getInstance("X.509"); - - /* loop over all PEM certs */ - for (String cafile : cafiles) { - - WolfSSLCertificate certPem = null; - String fullCertPath = caStoreDir.concat("/"); - fullCertPath = fullCertPath.concat(cafile); - - try { - certPem = new WolfSSLCertificate( - fullCertPath, WolfSSL.SSL_FILETYPE_PEM); - } catch (WolfSSLException we) { - /* skip, error parsing PEM */ - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Skipped loading cert: " + fullCertPath); - if (certPem != null) { - certPem.free(); - } - continue; - } - - byte[] derArray = certPem.getDer(); - certPem.free(); - ByteArrayInputStream bis = - new ByteArrayInputStream(derArray); - Certificate tmpCert = null; - - try { - tmpCert = cfactory.generateCertificate(bis); - bis.close(); - } catch (CertificateException ce) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Error generating certificate from " + - "ByteArrayInputStream"); - bis.close(); - throw new KeyStoreException(ce); - } - - String aliasString = "alias" + aliasCnt; - try { - certs.setCertificateEntry(aliasString, tmpCert); - } catch (KeyStoreException kse) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR, - "Error setting certificate entry in " + - "KeyStore, skipping loading cert"); - continue; - } - - /* increment alias counter for unique aliases */ - aliasCnt++; - } - systemCertsFound = true; - - } /* end Android manual load */ - } + /* increment alias counter for unique aliases */ + aliasCnt++; + } - if (systemCertsFound == false) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "No trusted system certs found, none " + - "loaded by default"); - } + if (aliasCnt == 0) { + /* No certs loaded, don't return empty KeyStore */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "No root certificates loaded from etc/security/cacerts"); + return null; + } + } + + return sysStore; + } + + /** + * Initialize TrustManager, loading root/CA certs. + * + * Attempts to load CA certifciates as trusted roots into wolfSSL from + * user-provided KeyStore. If KeyStore is null, we attempt to load default + * system CA certificates. Certs are loaded in the following priority order: + * + * 1. User-provided KeyStore passed in + * 2. javax.net.ssl.trustStore location, if set. Using password + * in javax.net.ssl.trustStorePassword. + * 3. Java installation 'jssecacerts' bundle: + * a. $JAVA_HOME/lib/security/jssecacerts (JDK 9+) + * b. $JAVA_HOME/jre/lib/security/jssecacerts (JDK less than 9) + * 4. Java installation 'cacerts' bundle: + * a. $JAVA_HOME/lib/security/cacerts (JDK 9+) + * b. $JAVA_HOME/jre/lib/security/cacerts (JDK less than 9) + * 5. Common system CA certs locations: + * a. /etc/ssl/certs/java/cacerts + * 6. Android: AndroidCAStore system KeyStore + * 7. Android: $ANDROID_ROOT/etc/security/cacerts + * + * If none of the locations above work for finding/loading CA certs, + * none are loaded into this TrustManager. + * + * @param in KeyStore from which to load trusted root/CA certificates, may + * be null + */ + @Override + protected void engineInit(KeyStore in) throws KeyStoreException { + + KeyStore certs = in; + String javaHome = null; + boolean wksAvailable = false; + String pass = System.getProperty("javax.net.ssl.trustStorePassword"); + String file = System.getProperty("javax.net.ssl.trustStore"); + String type = System.getProperty("javax.net.ssl.trustStoreType"); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "entered engineInit(KeyStore in)"); + + /* [1] Just use KeyStore passed in by user if available */ + if (in == null) { + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "input KeyStore null, trying to load system CA certs"); + + /* Check if wolfJCE WKS KeyStore is registered and available */ + wksAvailable = WolfSSLUtil.WKSAvailable(); + + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "wolfJCE WKS KeyStore type available: " + wksAvailable); + + /* [2] Try to load from system property details */ + certs = LoadKeyStoreFromSystemProperties( + wksAvailable, pass, file, type); + + /* Get JAVA_HOME for trying to load system certs next */ + if (certs == null) { + javaHome = WolfSSLUtil.GetJavaHome(); + if (javaHome == null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "$JAVA_HOME not set, unable to load system CA certs"); } else { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Loading certs from " + file); - stream = new FileInputStream(file); - certs.load(stream, passAr); - stream.close(); + "$JAVA_HOME = " + javaHome); } - } catch (FileNotFoundException ex) { - throw new KeyStoreException(ex); - } catch (IOException ex) { - throw new KeyStoreException(ex); - } catch (NoSuchAlgorithmException ex) { - throw new KeyStoreException(ex); - } catch (CertificateException ex) { - throw new KeyStoreException(ex); - } catch (WolfSSLJNIException ex) { - throw new KeyStoreException(ex); + } + + /* [3] Try to load system jssecacerts */ + if ((certs == null) && (javaHome != null)) { + certs = LoadSystemJsseCaCerts(javaHome, wksAvailable, pass); + } + + /* [4] Try to load system cacerts */ + if ((certs == null) && (javaHome != null)) { + certs = LoadSystemCaCerts(javaHome, wksAvailable, pass); + } + + /* [5] Try to load common CA cert locations */ + if (certs == null) { + certs = LoadCommonSystemCerts(wksAvailable, pass); + } + + /* [6] Try to load system certs if on Android */ + if ((certs == null) && WolfSSLUtil.isAndroid()) { + certs = LoadAndroidCAStore(); + } + + /* [7] Try to load Android system root certs manually */ + if ((certs == null) && WolfSSLUtil.isAndroid()) { + certs = LoadAndroidSystemCertsManually(); } } + else { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "input KeyStore provided, using for trusted certs"); + } + this.store = certs; this.initialized = true; } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java index 9999c365..63a289b9 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java @@ -25,7 +25,13 @@ import java.util.ArrayList; import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.io.FileInputStream; +import java.io.IOException; import java.security.Security; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import com.wolfssl.WolfSSLException; @@ -274,5 +280,106 @@ protected static int getDisabledAlgorithmsKeySizeLimit(String algo) return ret; } + + /** + * Get path that JAVA_HOME is set, append trailing slash if needed. + * + * @return String that JAVA_HOME is set to, otherwise null if not set + */ + protected static String GetJavaHome() { + + String javaHome = System.getenv("JAVA_HOME"); + + if (javaHome != null) { + if (!javaHome.endsWith("/") && + !javaHome.endsWith("\\")) { + /* add trailing slash if not there already */ + javaHome = javaHome.concat("/"); + } + } + + return javaHome; + } + + /** + * Detect if we are running on Android or not. + * + * @return true if we are running on an Android VM, otherwise false + */ + protected static boolean isAndroid() { + + String vmVendor = System.getProperty("java.vm.vendor"); + + if ((vmVendor != null) && + vmVendor.equals("The Android Project")) { + return true; + } + + return false; + } + + /** + * Check if wolfJCE WKS KeyStore is available for use. + * + * @return true if WKS KeyStore type available, otherwise false + */ + protected static boolean WKSAvailable() { + + boolean wksAvailable = false; + + try { + KeyStore.getInstance("WKS"); + wksAvailable = true; + } catch (KeyStoreException e) { + /* wolfJCE WKS not available, may be that wolfJCE is not being + * used or hasn't bee installed in system */ + } + + return wksAvailable; + } + + /** + * Try to get KeyStore instance of type specified and load from + * given file using provided password. + * + * @param file KeyStore file to load into new KeyStore object + * @param pass KeyStore password used to verify KeyStore integrity + * @param type KeyStore type of file to load + * + * @return new KeyStore object loaded with KeyStore file, or null + * if unable to load KeyStore + */ + protected static KeyStore LoadKeyStoreFileByType(String file, char[] pass, + String type) { + + KeyStore ks = null; + FileInputStream stream = null; + + try { + ks = KeyStore.getInstance(type); + + try { + /* Initialize KeyStore, loading certs below will overwrite if + * needed, but Android needs this to be initialized here */ + ks.load(null, null); + + } catch (Exception e) { + WolfSSLDebug.log(WolfSSLUtil.class, WolfSSLDebug.ERROR, + "Error initializing KeyStore with load(null, null)"); + return null; + } + + stream = new FileInputStream(file); + ks.load(stream, pass); + stream.close(); + + } catch (KeyStoreException | IOException | NoSuchAlgorithmException | + CertificateException e) { + return null; + } + + return ks; + } + } From 49a781bbd070fe3315dcf6ee1c2da417b254010b Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Fri, 15 Mar 2024 10:54:49 -0600 Subject: [PATCH 3/5] JSSE: add -ksformat option to example ClientJSSE and ServerJSSE to specify KeyStore type --- examples/provider/ClientJSSE.java | 13 +++++++++---- examples/provider/ServerJSSE.java | 9 +++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/provider/ClientJSSE.java b/examples/provider/ClientJSSE.java index 0a509f98..c741216d 100644 --- a/examples/provider/ClientJSSE.java +++ b/examples/provider/ClientJSSE.java @@ -104,6 +104,7 @@ public void run(String[] args) throws Exception { String caJKS = "../provider/ca-server.jks"; String clientPswd = "wolfSSL test"; String caPswd = "wolfSSL test"; + String keyStoreFormat = "JKS"; /* server (peer) info */ String host = "localhost"; @@ -194,6 +195,9 @@ public void run(String[] args) throws Exception { } else if (arg.equals("-sysca")) { useSysRoots = true; + } else if (arg.equals("-ksformat")) { + keyStoreFormat = args[++i]; + } else { printUsage(); } @@ -243,14 +247,14 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { tm.init((KeyStore)null); } else { - cert = KeyStore.getInstance("JKS"); + cert = KeyStore.getInstance(keyStoreFormat); cert.load(new FileInputStream(caJKS), caPswd.toCharArray()); tm.init(cert); } } /* load private key */ - pKey = KeyStore.getInstance("JKS"); + pKey = KeyStore.getInstance(keyStoreFormat); pKey.load(new FileInputStream(clientJKS), clientPswd.toCharArray()); km = KeyManagerFactory.getInstance("SunX509", provider); km.init(pKey, clientPswd.toCharArray()); @@ -428,11 +432,12 @@ private void printUsage() { System.out.println("-setp \tSet enabled protocols " + "e.g \"TLSv1.1 TLSv1.2\""); System.out.println("-c :\tCertificate/key JKS,\t\tdefault " + - "../provider/client.jks:wolfSSL test"); + "../provider/client.jks:\"wolfSSL test\""); System.out.println("-A :\tCertificate/key CA JKS file,\tdefault " + - "../provider/ca-server.jks:wolfSSL test"); + "../provider/ca-server.jks:\"wolfSSL test\""); System.out.println("-profile\tSleep for 10 sec before/after running " + "to allow profilers to attach"); + System.out.println("-ksformat \tKeyStore format (default: JKS)"); System.exit(1); } } diff --git a/examples/provider/ServerJSSE.java b/examples/provider/ServerJSSE.java index b621db53..b6a1e5ef 100644 --- a/examples/provider/ServerJSSE.java +++ b/examples/provider/ServerJSSE.java @@ -69,6 +69,7 @@ public void run(String[] args) { String caJKS = "../provider/ca-client.jks"; String serverPswd = "wolfSSL test"; String caPswd = "wolfSSL test"; + String keyStoreFormat = "JKS"; /* server (peer) info */ String host = "localhost"; @@ -164,6 +165,9 @@ public void run(String[] args) { } else if (arg.equals("-profile")) { profileSleep = true; + } else if (arg.equals("-ksformat")) { + keyStoreFormat = args[++i]; + } else { printUsage(); } @@ -201,10 +205,10 @@ public void run(String[] args) { } /* set up keystore and truststore */ - KeyStore keystore = KeyStore.getInstance("JKS"); + KeyStore keystore = KeyStore.getInstance(keyStoreFormat); keystore.load(new FileInputStream(serverJKS), serverPswd.toCharArray()); - KeyStore truststore = KeyStore.getInstance("JKS"); + KeyStore truststore = KeyStore.getInstance(keyStoreFormat); truststore.load(new FileInputStream(caJKS), caPswd.toCharArray()); TrustManagerFactory tm = TrustManagerFactory.getInstance("SunX509", tmfProvider); @@ -345,6 +349,7 @@ private void printUsage() { "../provider/ca-client.jks:\"wolfSSL test\""); System.out.println("-profile\tSleep for 10 sec before/after running " + "to allow profilers to attach"); + System.out.println("-ksformat \tKeyStore format (default: JKS)"); System.exit(1); } From 4de186eae13603e256db384b2f78396c923229d2 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Fri, 15 Mar 2024 11:26:27 -0600 Subject: [PATCH 4/5] JSSE: add wolfjsse.keystore.type.required Security property support to enforce KeyStore type used in KeyManager and TrustManager objects --- README.md | 100 ++++++++++++ .../provider/jsse/WolfSSLKeyManager.java | 40 ++++- .../provider/jsse/WolfSSLTrustManager.java | 142 +++++++++++++++--- .../wolfssl/provider/jsse/WolfSSLUtil.java | 51 +++++++ 4 files changed, 305 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 50b859fe..5a5fde62 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,106 @@ result in undefined behavior when the file descriptor number is larger than `FD_SETSIZE` (defaults to 1024 on most systems). For this reason, `poll()` is used as the default descriptor monitoring function. +### Security Property Support + +wolfJSSE allows for some customization through the `java.security` file +and use of Security properties. + +Support is included for the following pre-existing Java Security properties. + +**keystore.type (String)** - Specifies the default KeyStore type. This defaults +to JKS, but could be set to something else if desired. + +**jdk.tls.disabledAlgorithms (String)** - Can be used to disable algorithms, +TLS protocol versions, and key lengths, among other things. This should be a +comma-delimited String. wolfJSSE includes partial support for this property, +with supported items including disabling SSL/TLS protocol versions and setting +minimum RSA/ECC/DH key sizes. An example of potential use: + +``` +jdk.tls.disabledAlgorithms=SSLv3, TLSv1.1, DH keySize < 1024, EC keySize < 224, RSA keySize < 1024 +``` + +The following custom wolfJSSE-specific Security property settings are supported. +These can be placed into the `java.security` file and will be parsed and used +by wolfJSSE. + +**wolfjsse.enabledCipherSuites (String)** - Allows restriction of the enabled +cipher suiets to those listed in this Security property. When set, applications +wil not be able to override or add additional suites at runtime without +changing this property. This should be a comma-delimited String. Example use: + +``` +wolfjsse.enabledCipherSuites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +``` + +**wolfjsse.enabledSupportedCurves (String)** - Allows setting of specific ECC +curves to be enabled for SSL/TLS connections. This propogates down to the native +wolfSSL API `wolfSSL_UseSupportedCurve()`. If invalid/bad values are found +when processing this property, connection establishment will fail with an +SSLException. This should be a comma-delimited String. Example use: + +``` +wolfjsse.enabledSupportedCurves=secp256r1, secp521r1 +``` + +**wolfjsse.enabledSignatureAlgorithms (String)** - Allows restriction of the +signature algorithms sent in the TLS ClientHello Signature Algorithms +Extension. By using/setting this property, native wolfSSL will not populate +the extension with default values, which are based on what algorithms have been +compiled into the native wolfSSL library. This should be a comma-delimited +String of signature algorithm + MAC combinations. Example use: + +``` +wolfjsse.enabledSignatureAlgorithms=RSA+SHA256:ECDSA+SHA256 +``` + +**wolfjsse.keystore.type.required (String)** - Can be used to specify a KeyStore +type that is required to be used. If this is set, wolfJSSE will not allow use +of any KeyStore instances that are not of this type. One use of this option +is when using wolfCrypt FIPS 140-2/3 with wolfJCE registered as a JCE provider. +This option can be used to restrict use of the wolfJCE "WKS" KeyStore type +to help ensure conformance to using FIPS-validated cryptography. Other +non-wolfJCE KeyStore implementations may not use/consume FIPS validated crypto. + +If there are other Security properties you would like to use with wolfJSSE, +please contact support@wolfssl.com. + +### System Property Support + +wolfJSSE allows some customization through the use of System properties. Since +these are **System** properties and not **Security** properties, they will not +get picked up if placed in the `java.security` file. That file is only used +with/for Security properties (see section above). + +**javax.net.ssl.keyStore (String)** - Can be used to specify the KeyStore file +to use for KeyManager objects. An alternative to passing in the KeyStore file +programatically at runtime. + +**javax.net.ssl.keyStoreType (String)** - Can be used to specify the KeyStore +type to use when getting KeyStore instances inside KeyManager objects. + +**javax.net.ssl.keyStorePassword (String)** - Can be used to specify the +KeyStore password to use for initializing KeyManager instances. + +**javax.net.ssl.trustStore (String)** - Can be used to specify the KeyStore +file to use with TrustManager objects. An alternative to passing in the +KeyStore file programatically at runtime. + +**javax.net.ssl.trustStoreType (String)** - Can be used to specify the KeyStore +type to use when loading KeyStore inside TrustManager objects. + +**javax.net.ssl.trustStorePassword (String)** - Can be used to specify the +KeyStore password to use when loading KeyStore inside TrustManager objects. + +**jdk.tls.client.enableSessionTicketExtension (boolean)** - Session tickets +are enabled in different ways depending on the JDK implementation. For +Oracle/OpenJDK and variants, this System property enables session tickets and +was added in Java 13. Should be set to "true" to enable. + +If there are other System properties you would like to use with wolfJSSE, +please contact support@wolfssl.com. + ## Release Notes Release notes can be found in [ChangeLog.md](./ChangeLog.md). diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java b/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java index dc1bdad2..b9f28015 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java @@ -58,13 +58,17 @@ public WolfSSLKeyManager() { } * 3. Using BKS type if on Android * 4. Using JKS type if above all fail * + * @param requiredType KeyStore type required by user through + * java.security if wolfjsse.keystore.type.required property + * has been set. + * * @return new KeyStore object that has been created and loaded using * details specified in System properties. * * @throws KeyStoreException if javax.net.ssl.keyStore property is * set but KeyStore fails to load */ - private KeyStore LoadKeyStoreFromSystemProperties() + private KeyStore LoadKeyStoreFromSystemProperties(String requiredType) throws KeyStoreException { KeyStore sysStore = null; @@ -100,19 +104,28 @@ private KeyStore LoadKeyStoreFromSystemProperties() if (type != null && !type.trim().isEmpty()) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "javax.net.ssl.keyStoreType set: " + type); + + if (requiredType != null && !requiredType.equals(type)) { + throw new KeyStoreException( + "javax.net.ssl.keyStoreType conflicts with required " + + "KeyStore type from wolfjsse.keystore.type.required"); + } + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( file, this.pswd, type); } else { /* Try with wolfJCE WKS type first, in case wolfCrypt * FIPS is being used */ - if (wksAvailable) { + if (wksAvailable && + (requiredType == null || requiredType.equals("WKS"))) { sysStore = WolfSSLUtil.LoadKeyStoreFileByType( file, this.pswd, "WKS"); } /* Try with BKS, if we're running on Android */ - if ((sysStore == null) && WolfSSLUtil.isAndroid()) { + if ((sysStore == null) && WolfSSLUtil.isAndroid() && + (requiredType == null || requiredType.equals("BKS"))) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "Detected Android VM, trying BKS KeyStore type"); sysStore = WolfSSLUtil.LoadKeyStoreFileByType( @@ -120,7 +133,8 @@ private KeyStore LoadKeyStoreFromSystemProperties() } /* Try falling back to JKS */ - if (sysStore == null) { + if (sysStore == null && + (requiredType == null || requiredType.equals("JKS"))) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "javax.net.ssl.keyStoreType system property not set, " + "trying type: JKS"); @@ -150,10 +164,19 @@ protected void engineInit(KeyStore store, char[] password) this.pswd = password; KeyStore certs = store; + String requiredType = null; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "entering engineInit(KeyStore store, char[] password)"); + requiredType = WolfSSLUtil.getRequiredKeyStoreType(); + if (requiredType != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "java.security has restricted KeyStore type"); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "wolfjsse.keystore.type.required = " + requiredType); + } + /* If no KeyStore passed in, try to load from system property values * if they have been set */ if (store == null) { @@ -162,13 +185,20 @@ protected void engineInit(KeyStore store, char[] password) "input KeyStore null, trying to load KeyStore from " + "system properties"); - certs = LoadKeyStoreFromSystemProperties(); + certs = LoadKeyStoreFromSystemProperties(requiredType); } else { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "input KeyStore provided, using inside KeyManager"); } + /* Verify KeyStore we got matches our requirements, for example + * type may be restricted by users trying to conform to FIPS + * requirements */ + if (certs != null) { + WolfSSLUtil.checkKeyStoreRequirements(certs); + } + this.store = certs; this.initialized = true; } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java index f20f631d..d817587f 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLTrustManager.java @@ -73,6 +73,9 @@ public WolfSSLTrustManager() { } * @param tsFile javax.net.ssl.trustStore system property value, or null * @param tsType javax.net.ssl.trustStoreType system property value, * or null + * @param requiredType KeyStore type required by user through + * java.security if wolfjsse.keystore.type.required property + * has been set. * * @return new KeyStore object that has been created and loaded using * details specified in System properties. @@ -81,7 +84,7 @@ public WolfSSLTrustManager() { } * set but KeyStore fails to load */ private KeyStore LoadKeyStoreFromSystemProperties(boolean wksAvailable, - String tsPass, String tsFile, String tsType) + String tsPass, String tsFile, String tsType, String requiredType) throws KeyStoreException { char[] passArr = null; @@ -107,19 +110,29 @@ private KeyStore LoadKeyStoreFromSystemProperties(boolean wksAvailable, if (tsType != null && !tsType.trim().isEmpty()) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "javax.net.ssl.trustStoreType set: " + tsType); + + if (requiredType != null && !requiredType.equals(tsType)) { + throw new KeyStoreException( + "javax.net.ssl.trustStoreType conflicts with " + + "required KeyStore type from " + + "wolfjsse.keystore.type.required"); + } + sysStore = WolfSSLUtil.LoadKeyStoreFileByType( tsFile, passArr, tsType); } else { /* Try with wolfJCE WKS type first, in case wolfCrypt * FIPS is being used */ - if (wksAvailable) { + if (wksAvailable && + (requiredType == null || requiredType.equals("WKS"))) { sysStore = WolfSSLUtil.LoadKeyStoreFileByType( tsFile, passArr, "WKS"); } /* Try with BKS, if we're running on Android */ - if (sysStore == null && WolfSSLUtil.isAndroid()) { + if (sysStore == null && WolfSSLUtil.isAndroid() && + (requiredType == null || requiredType.equals("BKS"))) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "Detected Android VM, trying BKS KeyStore type"); sysStore = WolfSSLUtil.LoadKeyStoreFileByType( @@ -127,7 +140,8 @@ private KeyStore LoadKeyStoreFromSystemProperties(boolean wksAvailable, } /* Try falling back to JKS */ - if (sysStore == null) { + if (sysStore == null && + (requiredType == null || requiredType.equals("JKS"))) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "javax.net.ssl.trustStoreType system property not set, " + "trying type: JKS"); @@ -169,12 +183,15 @@ private KeyStore LoadKeyStoreFromSystemProperties(boolean wksAvailable, * @param tsPass javax.net.ssl.trustStorePassword, or null if not set * @param certBundleName Name of system certificate bundle, either * "jssecacerts" or "cacerts" + * @param requiredType KeyStore type required by user through + * java.security if wolfjsse.keystore.type.required property + * has been set. * * @return KeyStore object loaded with CA certs from jssecacerts, or * null if not able to find KeyStore or load certs */ private KeyStore LoadJavaSystemCerts(String jh, boolean wksAvailable, - String tsPass, String certBundleName) { + String tsPass, String certBundleName, String requiredType) { char[] passArr = null; KeyStore sysStore = null; @@ -217,6 +234,15 @@ private KeyStore LoadJavaSystemCerts(String jh, boolean wksAvailable, if (f.exists()) { + if (requiredType != null && !requiredType.equals(storeType)) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipping loading of system KeyStore, required type " + + "does not match wolfjsse.keystore.type.required"); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipped loading: " + f.getAbsolutePath()); + return null; + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "Loading certs from " + f.getAbsolutePath()); @@ -276,15 +302,17 @@ private KeyStore LoadJavaSystemCerts(String jh, boolean wksAvailable, } private KeyStore LoadSystemJsseCaCerts(String jh, boolean wksAvailable, - String tsPass) { + String tsPass, String requiredType) { - return LoadJavaSystemCerts(jh, wksAvailable, tsPass, "jssecacerts"); + return LoadJavaSystemCerts(jh, wksAvailable, tsPass, "jssecacerts", + requiredType); } private KeyStore LoadSystemCaCerts(String jh, boolean wksAvailable, - String tsPass) { + String tsPass, String requiredType) { - return LoadJavaSystemCerts(jh, wksAvailable, tsPass, "cacerts"); + return LoadJavaSystemCerts(jh, wksAvailable, tsPass, "cacerts", + requiredType); } /** @@ -293,18 +321,42 @@ private KeyStore LoadSystemCaCerts(String jh, boolean wksAvailable, * Currently includes: * 1. /etc/ssl/certs/java/cacerts * + * @param wksAvailable Boolean if wolfJCE WKS KeyStore typs is available + * @param tsPass javax.net.ssl.trustStorePassword, or null if not set + * @param requiredType KeyStore type required by user through + * java.security if wolfjsse.keystore.type.required property + * has been set. + * */ private KeyStore LoadCommonSystemCerts(boolean wksAvailable, - String tsPass) { + String tsPass, String requiredType) { char[] passArr = null; File f = null; FileInputStream stream = null; KeyStore sysStore = null; + /* Get default KeyStore type, set in java.security and normally JKS */ + String storeType = Security.getProperty("keystore.type"); + if (storeType != null) { + storeType = storeType.toUpperCase(); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "keystore.type Security property set: " + storeType); + } + f = new File("/etc/ssl/certs/java/cacerts"); if (f.exists()) { + + if (requiredType != null && !requiredType.equals(storeType)) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipping loading of system KeyStore, required type " + + "does not match wolfjsse.keystore.type.required"); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipped loading: " + f.getAbsolutePath()); + return null; + } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "Loading certs from " + f.getAbsolutePath()); @@ -321,13 +373,15 @@ private KeyStore LoadCommonSystemCerts(boolean wksAvailable, } try { + sysStore = KeyStore.getInstance(storeType); sysStore.load(stream, passArr); } catch (IOException | NoSuchAlgorithmException | - CertificateException e) { + CertificateException | KeyStoreException e) { WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Not able to load KeyStore with file stream: " + + "Not able to get or load KeyStore with file stream: " + f.getAbsolutePath()); + sysStore = null; } finally { try { @@ -353,13 +407,24 @@ private KeyStore LoadCommonSystemCerts(boolean wksAvailable, * certs. We try to load this first before going on to load root certs * manually, since it's already pre-imported and set up. * + * @param requiredType KeyStore type required by user through + * java.security if wolfjsse.keystore.type.required property + * has been set. + * * @return KeyStore object referencing AndroidCAStore, or null if not * found or not able to be loaded */ - private KeyStore LoadAndroidCAStore() { + private KeyStore LoadAndroidCAStore(String requiredType) { KeyStore sysStore = null; + if (requiredType != null && !requiredType.equals("AndroidCAStore")) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Skipping loading of AndroidCAStore, required type " + + "does not match wolfjsse.keystore.type.required"); + return null; + } + try { sysStore = KeyStore.getInstance("AndroidCAStore"); @@ -390,10 +455,14 @@ private KeyStore LoadAndroidCAStore() { * Try to load Android system root certificates manually by reading * all PEM certificates in [android_root]/etc/security/cacerts directory. * + * @param requiredType KeyStore type required by user through + * java.security if wolfjsse.keystore.type.required property + * has been set. + * * @return KeyStore object containing Android system CA certificates, or * null if none found or error loading any certs */ - private KeyStore LoadAndroidSystemCertsManually() { + private KeyStore LoadAndroidSystemCertsManually(String requiredType) { int aliasCnt = 0; byte[] derArray = null; @@ -401,17 +470,25 @@ private KeyStore LoadAndroidSystemCertsManually() { CertificateFactory cfactory = null; ByteArrayInputStream bis = null; Certificate tmpCert = null; + String storeType = null; String androidRoot = System.getenv("ANDROID_ROOT"); if (androidRoot != null) { /* Android default KeyStore type is BKS */ + if (requiredType != null) { + storeType = requiredType; + } else { + storeType = "BKS"; + } + try { - sysStore = KeyStore.getInstance("BKS"); + sysStore = KeyStore.getInstance(storeType); } catch (KeyStoreException e) { - /* Unable to get or load empty BKS KeyStore type */ + /* Unable to get or load empty KeyStore type */ WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "Unable to get or load BKS KeyStore instance"); + "Unable to get or load KeyStore instance, type: " + + storeType); return null; } @@ -568,10 +645,19 @@ protected void engineInit(KeyStore in) throws KeyStoreException { String pass = System.getProperty("javax.net.ssl.trustStorePassword"); String file = System.getProperty("javax.net.ssl.trustStore"); String type = System.getProperty("javax.net.ssl.trustStoreType"); + String requiredType = null; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, "entered engineInit(KeyStore in)"); + requiredType = WolfSSLUtil.getRequiredKeyStoreType(); + if (requiredType != null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "java.security has restricted KeyStore type"); + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "wolfjsse.keystore.type.required = " + requiredType); + } + /* [1] Just use KeyStore passed in by user if available */ if (in == null) { @@ -586,7 +672,7 @@ protected void engineInit(KeyStore in) throws KeyStoreException { /* [2] Try to load from system property details */ certs = LoadKeyStoreFromSystemProperties( - wksAvailable, pass, file, type); + wksAvailable, pass, file, type, requiredType); /* Get JAVA_HOME for trying to load system certs next */ if (certs == null) { @@ -603,27 +689,30 @@ protected void engineInit(KeyStore in) throws KeyStoreException { /* [3] Try to load system jssecacerts */ if ((certs == null) && (javaHome != null)) { - certs = LoadSystemJsseCaCerts(javaHome, wksAvailable, pass); + certs = LoadSystemJsseCaCerts(javaHome, wksAvailable, pass, + requiredType); } /* [4] Try to load system cacerts */ if ((certs == null) && (javaHome != null)) { - certs = LoadSystemCaCerts(javaHome, wksAvailable, pass); + certs = LoadSystemCaCerts(javaHome, wksAvailable, pass, + requiredType); } /* [5] Try to load common CA cert locations */ if (certs == null) { - certs = LoadCommonSystemCerts(wksAvailable, pass); + certs = LoadCommonSystemCerts(wksAvailable, pass, + requiredType); } /* [6] Try to load system certs if on Android */ if ((certs == null) && WolfSSLUtil.isAndroid()) { - certs = LoadAndroidCAStore(); + certs = LoadAndroidCAStore(requiredType); } /* [7] Try to load Android system root certs manually */ if ((certs == null) && WolfSSLUtil.isAndroid()) { - certs = LoadAndroidSystemCertsManually(); + certs = LoadAndroidSystemCertsManually(requiredType); } } else { @@ -631,6 +720,13 @@ protected void engineInit(KeyStore in) throws KeyStoreException { "input KeyStore provided, using for trusted certs"); } + /* Verify KeyStore we got matches our requirements, for example + * type may be restricted by users trying to conform to FIPS + * requirements */ + if (certs != null) { + WolfSSLUtil.checkKeyStoreRequirements(certs); + } + this.store = certs; this.initialized = true; } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java index 63a289b9..cea09075 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java @@ -219,6 +219,57 @@ protected static String[] getSupportedCurves() { return curves.split(","); } + /** + * Return KeyStore type restriction if set in java.security + * with 'wolfjsse.keystore.type.required' Security property. + * + * @return String with required KeyStore type, or null if no + * requirement set + */ + protected static String getRequiredKeyStoreType() { + + String requiredType = + Security.getProperty("wolfjsse.keystore.type.required"); + + if (requiredType == null || requiredType.isEmpty()) { + return null; + } + else { + requiredType = requiredType.toUpperCase(); + } + + return requiredType; + } + + /** + * Check given KeyStore against any pre-defind requirements for + * KeyStore use, including the following. + * + * Restricted KeyStore type: wolfjsse.keystore.type.required + * + * @param store Input KeyStore to check against requirements + * + * @throws KeyStoreException if KeyStore given does not meet wolfJSSE + * requirements + */ + protected static void checkKeyStoreRequirements( + KeyStore store) throws KeyStoreException { + + String requiredType = null; + + if (store == null) { + return; + } + + requiredType = getRequiredKeyStoreType(); + if ((requiredType != null) && + (!store.getType().equals(requiredType))) { + throw new KeyStoreException( + "KeyStore does not match required type, got " + + store.getType() + ", required " + requiredType); + } + } + /** * Return maximum key size allowed if minimum is set in * jdk.tls.disabledAlgorithms security property for specified algorithm. From 855fdeff2a0fb517c6780eb03ec9a1651bf8dd64 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Fri, 15 Mar 2024 11:28:16 -0600 Subject: [PATCH 5/5] JSSE: add script to convert example .jks files to wolfJCE WKS KeyStore type (.wks) --- examples/provider/convert-to-wks.sh | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 examples/provider/convert-to-wks.sh diff --git a/examples/provider/convert-to-wks.sh b/examples/provider/convert-to-wks.sh new file mode 100755 index 00000000..db9775d8 --- /dev/null +++ b/examples/provider/convert-to-wks.sh @@ -0,0 +1,56 @@ + +if [ -z "$1" ]; then + echo "Expected provider location for wolfJCE provider JAR directory." + echo "Example \"./convert-to-wks.sh ~/wolfcryptjni/lib\"" + exit 1 +fi +PROVIDER_DIR="$1" + +# Export library paths for Linux and Mac to find shared JNI library +export LD_LIBRARY_PATH=$PROVIDER_DIR:$LD_LIBRARY_PATH +export DYLD_LIBRARY_PATH=$PROVIDER_DIR:$DYLD_LIBRARY_PATH + +convert () { +keytool -importkeystore -srckeystore ${1}.jks -destkeystore ${1}.wks -srcstoretype JKS -deststoretype WKS -srcstorepass "wolfSSL test" -deststorepass "wolfSSL test" -provider com.wolfssl.provider.jce.WolfCryptProvider --providerpath "$PROVIDER_DIR/wolfcrypt-jni.jar" + +} + +rm -f all.bks &> /dev/null +convert "all" + +rm -f all_mixed.bks &> /dev/null +convert "all_mixed" + +rm -f client.bks &> /dev/null +convert "client" + +rm -f client-rsa-1024.bks &> /dev/null +convert "client-rsa-1024" + +rm -f client-rsa.bks &> /dev/null +convert "client-rsa" + +rm -f client-ecc.bks &> /dev/null +convert "client-ecc" + +rm -f server.bks &> /dev/null +convert "server" + +rm -f server-rsa-1024.bks &> /dev/null +convert "server-rsa-1024" + +rm -f server-rsa.bks &> /dev/null +convert "server-rsa" + +rm -f server-ecc.bks &> /dev/null +convert "server-ecc" + +rm -f cacerts.bks &> /dev/null +convert "cacerts" + +rm -f ca-client.bks &> /dev/null +convert "ca-client" + +rm -f ca-server.bks &> /dev/null +convert "ca-server" +