From b80a989b4eb9ddf290fb2c74637589fc7bc4f28d Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 14 Mar 2024 15:36:05 -0600 Subject: [PATCH] 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 | 843 ++++++++++++------ .../wolfssl/provider/jsse/WolfSSLUtil.java | 107 +++ 3 files changed, 777 insertions(+), 358 deletions(-) diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java b/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java index 4e75f18..dc1bdad 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLKeyManager.java @@ -40,13 +40,109 @@ import javax.net.ssl.ManagerFactoryParameters; * 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 @@ public class WolfSSLKeyManager extends KeyManagerFactorySpi { 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"); - } + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "input KeyStore null, trying to load KeyStore from " + + "system properties"); - /* 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(); - } - - } 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 8120979..f20f631 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.FileNotFoundException; 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 + /** + * 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 + */ + 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"); + } + + /* 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 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( + tsFile, passArr, "BKS"); + } + + /* Try falling back to JKS */ + if (sysStore == null) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "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(); + } + + 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 (stream != null) { + stream.close(); + } + } 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 { + if (stream != null) { + stream.close(); + } + } catch (IOException 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; + } + + try { + derArray = certPem.getDer(); + } catch (WolfSSLJNIException e) { + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + "Error getting DER from PEM cert, skipping: " + + fullCertPath); + } finally { + certPem.free(); + } + + bis = new ByteArrayInputStream(derArray); + + try { + tmpCert = cfactory.generateCertificate(bis); + + } 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) { + } + } + + 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; + } + + /* increment alias counter for unique aliases */ + aliasCnt++; + } + + 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. - * 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 + * 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()"); + "entered engineInit(KeyStore in)"); + /* [1] Just use KeyStore passed in by user if available */ 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; - try { - if (pass != 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, - "javax.net.ssl.trustStorePassword system property " + - "set, using password"); - passAr = pass.toCharArray(); - } else { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "javax.net.ssl.trustStorePassword system property " + - "not set"); - } - - /* 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 { - 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"); - } - } - } 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 new KeyStoreException(e); - } - - if (file == null) { - 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("/"); - } - - /* 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"); - } - - /* 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"); - } - - 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"); - } - } - - 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; - } - - /* 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 */ - } - - if (systemCertsFound == false) { - WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, - "No trusted system certs found, none " + - "loaded by default"); - } + "$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 9999c36..63a289b 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.Arrays; 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 @@ public class WolfSSLUtil { 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; + } + }