diff --git a/README_JCE.md b/README_JCE.md index 3dc0ecb..503c2e6 100644 --- a/README_JCE.md +++ b/README_JCE.md @@ -36,6 +36,40 @@ file for JCE provider customization: | --- | --- | --- | --- | | wolfjce.wks.iterationCount | 210,000 | Numeric | PBKDF2 iteration count (10,000 minimum) | | wolfjce.wks.maxCertChainLength | 100 | Integer | Max cert chain length | +| wolfjce.mapJKStoWKS | UNSET | true | Register fake JKS KeyStore service mapped to WKS | +| wolfjce.mapPKCS12toWKS | UNSET | true | Register fake PKCS12 KeyStore service mapped to WKS | + +**wolfjce.mapJKStoWKS** - this Security property should be used with caution. +When enabled, this will register a "JKS" KeyStore type in wolfJCE, which means +calling applications using `KeyStore.getInstance("JKS")` will get a KeyStore +implementation from wolfJCE. BUT, this KeyStore type will actually be a +WolfSSLKeyStore (WKS) type internally. Loading actual JKS files will fail. +This can be helpful when FIPS compliance is required, but existing code gets +a JKS KeyStore instance - and this assumes the caller has the flexibility to +actually load a real WKS KeyStore file into this KeyStore object. If this +property is being set at runtime programatically, the wolfJCE provider services +will need to be refreshed / reloaded, by doing: + +``` +WolfCryptProvider prov = (WolfCryptProvider)Security.getProvider("wolfJCE"); +prov.refreshServices(); +``` + +**wolfjce.mapPKCS12toWKS** - this Security property should be used with caution. +When enabled, this will register a "PKCS12" KeyStore type in wolfJCE, which +means calling applications using `KeyStore.getInstance("PKCS12")` will get a +KeyStore implementation from wolfJCE. BUT, this KeyStore type will actually be a +WolfSSLKeyStore (WKS) type internally. Loading actual PKCS12 files will fail. +This can be helpful when FIPS compliance is required, but existing code gets +a PKCS12 KeyStore instance - and this assumes the caller has the flexibility to +actually load a real WKS KeyStore file into this KeyStore object. If this +property is being set at runtime programatically, the wolfJCE provider services +will need to be refreshed / reloaded, by doing: + +``` +WolfCryptProvider prov = (WolfCryptProvider)Security.getProvider("wolfJCE"); +prov.refreshServices(); +``` #### System Property Support diff --git a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java index 7dca8e1..c2e53c8 100644 --- a/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java +++ b/src/main/java/com/wolfssl/provider/jce/WolfCryptProvider.java @@ -22,6 +22,7 @@ package com.wolfssl.provider.jce; import java.security.Provider; +import java.security.Security; import com.wolfssl.wolfcrypt.FeatureDetect; import com.wolfssl.wolfcrypt.Fips; @@ -37,6 +38,27 @@ public final class WolfCryptProvider extends Provider { */ public WolfCryptProvider() { super("wolfJCE", 1.7, "wolfCrypt JCE Provider"); + registerServices(); + } + + /** + * Refresh the services provided by this JCE provider. + * + * This is required when one of the Security properties has been changed + * that affect the services offered by this provider. For example: + * wolfjce.mapJKStoWKS + * wolfjce.mapPKCS12toWKS + */ + public void refreshServices() { + registerServices(); + } + + /** + * Register services provided by wolfJCE, called by class constructor. + */ + private void registerServices() { + String mapJksToWks = null; + String mapPkcs12ToWks = null; /* MessageDigest */ if (FeatureDetect.Md5Enabled()) { @@ -222,6 +244,32 @@ public WolfCryptProvider() { put("KeyStore.WKS", "com.wolfssl.provider.jce.WolfSSLKeyStore"); + /* Fake mapping of JKS to WKS type. Use with caution! This is + * usually used when FIPS compliance is needed but code cannot be + * changed that creates a JKS KeyStore object type. Any files loaded + * into this fake JKS KeyStore MUST be of actual type WKS or failures + * will happen. Remove service first here in case of refresh. */ + remove("KeyStore.JKS"); + mapJksToWks = Security.getProperty("wolfjce.mapJKStoWKS"); + if (mapJksToWks != null && !mapJksToWks.isEmpty() && + mapJksToWks.equalsIgnoreCase("true")) { + put("KeyStore.JKS", + "com.wolfssl.provider.jce.WolfSSLKeyStore"); + } + + /* Fake mapping of PKCS12 to WKS type. Use with caution! This is + * usually used when FIPS compliance is needed but code cannot be + * changed that creates a JKS KeyStore object type. Any files loaded + * into this fake JKS KeyStore MUST be of actual type WKS or failures + * will happen. Remove service first here in case of refresh. */ + remove("KeyStore.PKCS12"); + mapPkcs12ToWks = Security.getProperty("wolfjce.mapPKCS12toWKS"); + if (mapPkcs12ToWks != null && !mapPkcs12ToWks.isEmpty() && + mapPkcs12ToWks.equalsIgnoreCase("true")) { + put("KeyStore.PKCS12", + "com.wolfssl.provider.jce.WolfSSLKeyStore"); + } + /* If using a FIPS version of wolfCrypt, allow private key to be * exported for use. Only applicable to FIPS 140-3 */ if (Fips.enabled) { diff --git a/src/test/java/com/wolfssl/provider/jce/test/WolfSSLKeyStoreTest.java b/src/test/java/com/wolfssl/provider/jce/test/WolfSSLKeyStoreTest.java index c70fc40..04535f4 100644 --- a/src/test/java/com/wolfssl/provider/jce/test/WolfSSLKeyStoreTest.java +++ b/src/test/java/com/wolfssl/provider/jce/test/WolfSSLKeyStoreTest.java @@ -136,6 +136,12 @@ public class WolfSSLKeyStoreTest { private static Certificate[] eccServerChain = null; /* ECC chain */ private static Certificate[] invalidChain = null; + /* Example .jks KeyStore file paths */ + private static String clientJKS = null; /* client.jks */ + + /* Examnple .p12 KeyStore file paths */ + private static String clientP12 = null; /* client.p12 */ + /* Example .wks KeyStore file paths */ private static String clientWKS = null; /* client.wks */ private static String clientRsa1024WKS = null; /* client-rsa-1024.wks */ @@ -381,6 +387,14 @@ public static void testSetupAndProviderInstallation() intEccInt2CertDer = certPre.concat("examples/certs/intermediate/ca-int2-ecc-cert.der"); + /* Set paths to example JKS KeyStore files */ + clientJKS = + certPre.concat("examples/certs/client.jks"); + + /* Set paths to example PKCS12 KeyStore files */ + clientP12 = + certPre.concat("examples/certs/client.p12"); + /* Set paths to example WKS KeyStore files */ clientWKS = certPre.concat("examples/certs/client.wks"); @@ -1426,6 +1440,134 @@ public void testLoadWKSFromFile() assertEquals(1, store.size()); } + @Test + public void testLoadWKSasJKSFromFile() + throws KeyStoreException, IOException, FileNotFoundException, + NoSuchProviderException, NoSuchAlgorithmException, + CertificateException, InvalidKeySpecException, + UnrecoverableKeyException { + + WolfCryptProvider prov = null; + KeyStore store = null; + + /* Use client.wks (clientWKS) to test. Any WKS KeyStore could be used, + * this was just picked since was first used/tested in test above. */ + + /* If Security property "wolfjce.mapJKStoWKS=true" has been set, + * WolfSSLKeyStore should be able to load a WKS file when using a + * "JKS" KeyStore type. */ + String origProperty = Security.getProperty("wolfjce.mapJKStoWKS"); + + /* The wolfJCE service list needs to be refreshed after changing + * Security properties that will adjust the services we register */ + Security.setProperty("wolfjce.mapJKStoWKS", "true"); + prov = (WolfCryptProvider)Security.getProvider("wolfJCE"); + prov.refreshServices(); + + /* Load WKS as JKS, should work w/o exception */ + store = KeyStore.getInstance("JKS"); + assertTrue(store.getProvider().contains("wolfJCE")); + store.load(new FileInputStream(clientWKS), storePass.toCharArray()); + assertEquals(2, store.size()); + + /* Load JKS as JKS when this is set should fail, since using WKS + * implementation underneath fake JKS mapping */ + try { + store.load(new FileInputStream(clientJKS), storePass.toCharArray()); + fail("Loaded JKS as JKS, but shouldn't with fake mapping set"); + } catch (IOException e) { + /* expected */ + } + + /* Set mapping to false, loading a WKS as JKS should throw exception */ + Security.setProperty("wolfjce.mapJKStoWKS", "false"); + prov = (WolfCryptProvider)Security.getProvider("wolfJCE"); + prov.refreshServices(); + store = KeyStore.getInstance("JKS"); + assertTrue(!store.getProvider().contains("wolfJCE")); + try { + store.load(new FileInputStream(clientWKS), storePass.toCharArray()); + fail("Loaded WKS as JKS, but shouldn't have been able to"); + } catch (IOException e) { + /* expected */ + } + + /* Loading JKS as JKS should work when mapping not set */ + store.load(new FileInputStream(clientJKS), storePass.toCharArray()); + + /* Restore Security property */ + if (origProperty == null) { + Security.setProperty("wolfjce.mapJKStoWKS", ""); + } + else { + Security.setProperty("wolfjce.mapJKStoWKS", origProperty); + } + } + + @Test + public void testLoadWKSasPKCS12FromFile() + throws KeyStoreException, IOException, FileNotFoundException, + NoSuchProviderException, NoSuchAlgorithmException, + CertificateException, InvalidKeySpecException, + UnrecoverableKeyException { + + WolfCryptProvider prov = null; + KeyStore store = null; + + /* Use client.wks (clientWKS) to test. Any WKS KeyStore could be used, + * this was just picked since was first used/tested in test above. */ + + /* If Security property "wolfjce.mapPKCS12toWKS=true" has been set, + * WolfSSLKeyStore should be able to load a WKS file when using a + * "PKCS12" KeyStore type. */ + String origProperty = Security.getProperty("wolfjce.mapPKCS12toWKS"); + + /* The wolfJCE service list needs to be refreshed after changing + * Security properties that will adjust the services we register */ + Security.setProperty("wolfjce.mapPKCS12toWKS", "true"); + prov = (WolfCryptProvider)Security.getProvider("wolfJCE"); + prov.refreshServices(); + + /* Load WKS as PKCS12, should work w/o exception */ + store = KeyStore.getInstance("PKCS12"); + assertTrue(store.getProvider().contains("wolfJCE")); + store.load(new FileInputStream(clientWKS), storePass.toCharArray()); + assertEquals(2, store.size()); + + /* Load PKCS12 as PKCS12 when this is set should fail, since using WKS + * implementation underneath fake PKCS12 mapping */ + try { + store.load(new FileInputStream(clientP12), storePass.toCharArray()); + fail("Loaded PKCS12 as PKCS12, but shouldn't with fake mapping set"); + } catch (IOException e) { + /* expected */ + } + + /* Set mapping to false, loading WKS as PKCS12 should throw exception */ + Security.setProperty("wolfjce.mapPKCS12toWKS", "false"); + prov = (WolfCryptProvider)Security.getProvider("wolfJCE"); + prov.refreshServices(); + store = KeyStore.getInstance("PKCS12"); + assertTrue(!store.getProvider().contains("wolfJCE")); + try { + store.load(new FileInputStream(clientWKS), storePass.toCharArray()); + fail("Loaded WKS as PKCS12, but shouldn't have been able to"); + } catch (IOException e) { + /* expected */ + } + + /* Loading PKCS12 as PKCS12 should work when mapping not set */ + store.load(new FileInputStream(clientP12), storePass.toCharArray()); + + /* Restore Security property */ + if (origProperty == null) { + Security.setProperty("wolfjce.mapPKCS12toWKS", ""); + } + else { + Security.setProperty("wolfjce.mapPKCS12toWKS", origProperty); + } + } + @Test public void testLoadSystemCAKeyStore() throws KeyStoreException, IOException, FileNotFoundException,