JCE: initial implementation of WolfCryptUtil.convertKeyStoreToWKS()
parent
783c9275de
commit
0834dbdafc
|
@ -234,8 +234,7 @@ SecretKey objects.
|
|||
#### Converting Other KeyStore Formats to WKS
|
||||
|
||||
The Java `keytool` application can be used to convert between KeyStore formats.
|
||||
This can be easily used for example to convert a JKS KeyStore into a WKS
|
||||
format KeyStore.
|
||||
This can be easily used to convert a JKS KeyStore into a WKS format KeyStore.
|
||||
|
||||
The following example command would convert a KeyStore in JKS format named
|
||||
`server.jks` to a KeyStore in WKS format named `server.wks`:
|
||||
|
@ -248,6 +247,50 @@ keytool -importkeystore -srckeystore server.jks -destkeystore server.wks \
|
|||
--providerpath /path/to/wolfcrypt-jni.jar
|
||||
```
|
||||
|
||||
Additionally, wolfJCE provides a utility method `WolfCryptUtil.convertKeyStoreToWKS()`
|
||||
that can be used programmatically to convert KeyStore formats. This method
|
||||
supports converting from JKS, PKCS12, and WKS formats to WKS format. When
|
||||
converting from WKS to WKS, the method efficiently returns the same input
|
||||
stream without performing any conversion.
|
||||
|
||||
The method automatically detects the input KeyStore format and handles the
|
||||
conversion appropriately. It supports the following features:
|
||||
|
||||
- Automatic format detection (WKS, JKS, PKCS12)
|
||||
- Preservation of all certificates and keys from the source KeyStore
|
||||
- Support for both key entries (with certificate chains) and certificate-only entries
|
||||
- Efficient handling of WKS input (returns same stream)
|
||||
- Proper stream handling with mark/reset support for large KeyStores
|
||||
|
||||
**FIPS NOTE:** This utility method will call Sun provider code for JKS
|
||||
and PKCS12. This means that if using wolfCrypt FIPS, these calls will make
|
||||
calls into non-FIPS compliant cryptography for the conversion. Please take
|
||||
this into consideration when being used in a FIPS compliant environment.
|
||||
|
||||
Example usage:
|
||||
|
||||
```java
|
||||
import com.wolfssl.provider.jce.WolfCryptUtil;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
/* Load your source KeyStore (JKS, PKCS12, or WKS) */
|
||||
InputStream sourceStream = ...;
|
||||
char[] password = "your_password".toCharArray();
|
||||
|
||||
/* Convert to WKS format, fail on insert errors */
|
||||
InputStream wksStream = WolfCryptUtil.convertKeyStoreToWKS(sourceStream, password, true);
|
||||
|
||||
/* Load the converted WKS KeyStore */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStream, password);
|
||||
```
|
||||
|
||||
The method respects the Security properties `wolfjce.mapJKStoWKS` and
|
||||
`wolfjce.mapPKCS12toWKS` when performing conversions. If these properties are
|
||||
set to "true", the method will use reflection to find the Sun provider
|
||||
implementations for JKS and PKCS12 to use for conversion.
|
||||
|
||||
To list entries inside a WKS keystore using the `keytool`, a command
|
||||
similar to the following can be used (with the `-list` option):
|
||||
|
||||
|
@ -388,7 +431,7 @@ ant build system, please see the main README.md included in this package.
|
|||
wolfSSL (company) has it's own set of code signing certificates from Oracle
|
||||
that allow wolfJCE to be authenticated in the Oracle JDK. With each release
|
||||
of wolfJCE, wolfSSL ships a couple pre-signed versions of the
|
||||
‘wolfcrypt-jni.jar”, located at:
|
||||
'wolfcrypt-jni.jar", located at:
|
||||
|
||||
wolfcrypt-jni-X.X.X/lib/signed/debug/wolfcrypt-jni.jar
|
||||
wolfcrypt-jni-X.X.X/lib/signed/release/wolfcrypt-jni.jar
|
||||
|
|
|
@ -82,6 +82,7 @@ infer --fail-on-issue run -- javac \
|
|||
src/main/java/com/wolfssl/provider/jce/WolfCryptRandom.java \
|
||||
src/main/java/com/wolfssl/provider/jce/WolfCryptSecretKeyFactory.java \
|
||||
src/main/java/com/wolfssl/provider/jce/WolfCryptSignature.java \
|
||||
src/main/java/com/wolfssl/provider/jce/WolfCryptUtil.java \
|
||||
src/main/java/com/wolfssl/provider/jce/WolfSSLKeyStore.java
|
||||
|
||||
RETVAL=$?
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/* WolfCryptUtil.java
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
package com.wolfssl.provider.jce;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Key;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Utility class containing helper functions for wolfCrypt JCE provider.
|
||||
*/
|
||||
public class WolfCryptUtil {
|
||||
|
||||
/**
|
||||
* Maximum size of the keystore buffer to mark. We try to set this
|
||||
* high enough to handle any large keystore. Although there is no
|
||||
* upper limit on the size of a keystore, looking at the JDK 23 cacerts
|
||||
* KeyStore file, that is 190kB. We leave ample room for growth here
|
||||
* with 512kB.
|
||||
*/
|
||||
private static final int MAX_KEYSTORE_SIZE = 512 * 1024;
|
||||
|
||||
/**
|
||||
* Chunk size for reading the keystore. We use 4kB as a happy medium
|
||||
* between memory usage and performance.
|
||||
*/
|
||||
private static final int KEYSTORE_CHUNK_SIZE = 4 * 1024;
|
||||
|
||||
/**
|
||||
* Internal method for logging output.
|
||||
*
|
||||
* @param msg message to be logged
|
||||
*/
|
||||
private static synchronized void log(String msg) {
|
||||
WolfCryptDebug.log(WolfCryptUtil.class, WolfCryptDebug.INFO,
|
||||
() -> msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Java KeyStore (JKS or PKCS12) to a WolfSSLKeyStore (WKS)
|
||||
* format.
|
||||
*
|
||||
* This method detects the type of the input KeyStore (WKS, JKS, or PKCS12)
|
||||
* and converts it to WKS format if needed. All certificates and keys from
|
||||
* the source KeyStore are transferred to the destination KeyStore. If the
|
||||
* input KeyStore is already of type WKS, the method will return the same
|
||||
* InputStream.
|
||||
*
|
||||
* @param stream Input stream containing a WKS, JKS, or PKCS12 KeyStore
|
||||
* @param oldPassword Password used to decrypt KeyStore entries.
|
||||
* @param newPassword Password used to encrypt KeyStore entries. When used
|
||||
* with wolfCrypt FIPS, this password needs to meet FIPS minimum
|
||||
* HMAC key size requirements and must be at least 14 characters.
|
||||
* @param failOnInsertErrors If true, throw an exception if an error occurs
|
||||
* during the insertion of a certificate or key into the newly
|
||||
* created WKS KeyStore. If false, log the error and continue
|
||||
* inserting the remaining entries. When used with wolfCrypt FIPS,
|
||||
* entries can fail to insert into WKS KeyStore due to FIPS
|
||||
* restrictions on the algorithms used.
|
||||
* @return InputStream containing the newly created WKS KeyStore
|
||||
* @throws IOException If an I/O error occurs
|
||||
* @throws NoSuchProviderException If required security providers are not
|
||||
* available or if reflection operations fail when accessing the
|
||||
* original KeyStore implementations
|
||||
*/
|
||||
public static InputStream convertKeyStoreToWKS(InputStream stream,
|
||||
char[] oldPassword, char[] newPassword, boolean failOnInsertErrors)
|
||||
throws IOException, NoSuchProviderException {
|
||||
|
||||
boolean mapJksToWks = false;
|
||||
boolean mapPkcs12ToWks = false;
|
||||
boolean wksFound = false;
|
||||
boolean jksFound = false;
|
||||
KeyStore sourceStore = null;
|
||||
|
||||
log("converting KeyStore InputStream to WKS format");
|
||||
|
||||
if (stream == null) {
|
||||
throw new IllegalArgumentException("Input stream cannot be null");
|
||||
}
|
||||
|
||||
if (oldPassword == null) {
|
||||
throw new IllegalArgumentException("Old password cannot be null");
|
||||
}
|
||||
|
||||
if (newPassword == null) {
|
||||
throw new IllegalArgumentException("New password cannot be null");
|
||||
}
|
||||
|
||||
/* Make sure wolfJCE provider is available and registered */
|
||||
Provider wolfJCE = Security.getProvider("wolfJCE");
|
||||
if (wolfJCE == null) {
|
||||
Security.addProvider(new WolfCryptProvider());
|
||||
}
|
||||
|
||||
try {
|
||||
/* Check if wolfJCE has mapped JKS or PKCS12 to WKS */
|
||||
String mapJksToWksStr =
|
||||
Security.getProperty("wolfjce.mapJKStoWKS");
|
||||
if (mapJksToWksStr != null && !mapJksToWksStr.isEmpty() &&
|
||||
mapJksToWksStr.equalsIgnoreCase("true")) {
|
||||
mapJksToWks = true;
|
||||
}
|
||||
|
||||
String mapPkcs12ToWksStr =
|
||||
Security.getProperty("wolfjce.mapPKCS12toWKS");
|
||||
if (mapPkcs12ToWksStr != null && !mapPkcs12ToWksStr.isEmpty() &&
|
||||
mapPkcs12ToWksStr.equalsIgnoreCase("true")) {
|
||||
mapPkcs12ToWks = true;
|
||||
}
|
||||
|
||||
log("JKS to WKS mapping enabled: " + mapJksToWks);
|
||||
log("PKCS12 to WKS mapping enabled: " + mapPkcs12ToWks);
|
||||
|
||||
/* Since we will be doing KeyStore type detection by trying to
|
||||
* read the KeyStore, we want to make sure we have the ability
|
||||
* to mark() the stream. If we don't have the ability, we copy
|
||||
* the stream into a ByteArrayOutputStream and then into a
|
||||
* ByteArrayInputStream which is markable. */
|
||||
if (!stream.markSupported()) {
|
||||
try {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int numRead;
|
||||
byte[] data = new byte[KEYSTORE_CHUNK_SIZE];
|
||||
while ((numRead = stream.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, numRead);
|
||||
}
|
||||
buffer.flush();
|
||||
stream = new ByteArrayInputStream(buffer.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to read stream contents", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark the current position in the stream */
|
||||
stream.mark(MAX_KEYSTORE_SIZE);
|
||||
|
||||
/* Try WKS */
|
||||
try {
|
||||
sourceStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
sourceStore.load(stream, oldPassword);
|
||||
wksFound = true;
|
||||
|
||||
log("Input KeyStore is already in WKS format");
|
||||
} catch (KeyStoreException | NoSuchAlgorithmException |
|
||||
CertificateException | IOException e) {
|
||||
/* Not a WKS KeyStore, continue with other formats */
|
||||
} finally {
|
||||
stream.reset();
|
||||
}
|
||||
|
||||
/* Try JKS */
|
||||
if (!wksFound) {
|
||||
try {
|
||||
if (mapJksToWks) {
|
||||
/* If JKS is mapped to WKS, use reflection to get the
|
||||
* Sun provider's JKS implementation */
|
||||
try {
|
||||
sourceStore = getJksKeyStoreFromSunProvider();
|
||||
} catch (ReflectiveOperationException |
|
||||
KeyStoreException ex) {
|
||||
throw new NoSuchProviderException(
|
||||
"Failed to get JKS implementation via " +
|
||||
"reflection from Sun provider: " +
|
||||
ex.getMessage());
|
||||
}
|
||||
} else {
|
||||
sourceStore = KeyStore.getInstance("JKS");
|
||||
}
|
||||
sourceStore.load(stream, oldPassword);
|
||||
jksFound = true;
|
||||
|
||||
log("Input KeyStore is in JKS format");
|
||||
} catch (IOException | NoSuchAlgorithmException |
|
||||
CertificateException e) {
|
||||
/* Not a JKS KeyStore, continue with other formats */
|
||||
} finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/* Try PKCS12 */
|
||||
if (!wksFound && !jksFound) {
|
||||
try {
|
||||
if (mapPkcs12ToWks) {
|
||||
/* If PKCS12 is mapped to WKS, use reflection to get
|
||||
* the Sun provider's PKCS12 implementation */
|
||||
try {
|
||||
sourceStore = getPkcs12KeyStoreFromSunProvider();
|
||||
} catch (ReflectiveOperationException |
|
||||
KeyStoreException ex) {
|
||||
throw new NoSuchProviderException(
|
||||
"Failed to get PKCS12 implementation via " +
|
||||
"reflection from Sun provider: " +
|
||||
ex.getMessage());
|
||||
}
|
||||
} else {
|
||||
sourceStore = KeyStore.getInstance("PKCS12");
|
||||
}
|
||||
sourceStore.load(stream, oldPassword);
|
||||
|
||||
log("Input KeyStore is in PKCS12 format");
|
||||
} catch (KeyStoreException | NoSuchAlgorithmException |
|
||||
CertificateException ex) {
|
||||
throw new IOException(
|
||||
"Input KeyStore is neither WKS, JKS nor " +
|
||||
"PKCS12 KeyStore format", ex);
|
||||
} finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/* Create destination WKS KeyStore */
|
||||
KeyStore destStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
destStore.load(null, newPassword);
|
||||
log("Creating destination WKS KeyStore to populate");
|
||||
|
||||
/* Copy all entries from source to destination */
|
||||
Enumeration<String> aliases = sourceStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = aliases.nextElement();
|
||||
|
||||
if (sourceStore.isKeyEntry(alias)) {
|
||||
/* Handle key entries (may include a certificate chain) */
|
||||
try {
|
||||
Key key = sourceStore.getKey(alias, oldPassword);
|
||||
Certificate[] chain =
|
||||
sourceStore.getCertificateChain(alias);
|
||||
destStore.setKeyEntry(alias, key, newPassword, chain);
|
||||
} catch (UnrecoverableKeyException | KeyStoreException e) {
|
||||
if (failOnInsertErrors) {
|
||||
throw new IOException("Failed to copy key entry: " +
|
||||
alias, e);
|
||||
} else {
|
||||
log("Failed to copy key entry: " + alias +
|
||||
", continuing with next entry");
|
||||
}
|
||||
}
|
||||
} else if (sourceStore.isCertificateEntry(alias)) {
|
||||
/* Handle certificate-only entries */
|
||||
try {
|
||||
Certificate cert = sourceStore.getCertificate(alias);
|
||||
destStore.setCertificateEntry(alias, cert);
|
||||
} catch (KeyStoreException e) {
|
||||
if (failOnInsertErrors) {
|
||||
throw new IOException(
|
||||
"Failed to copy certificate entry: " +
|
||||
alias, e);
|
||||
} else {
|
||||
log("Failed to copy certificate entry: " + alias +
|
||||
", continuing with next entry");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log("Copied all entries from source to destination KeyStore");
|
||||
|
||||
/* Write the WKS KeyStore to a byte array and return as
|
||||
* InputStream */
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
destStore.store(baos, newPassword);
|
||||
|
||||
return new ByteArrayInputStream(baos.toByteArray());
|
||||
|
||||
} catch (KeyStoreException | NoSuchAlgorithmException |
|
||||
CertificateException e) {
|
||||
throw new IOException("Error during KeyStore conversion", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sun provider JKS KeyStore implementation using reflection.
|
||||
* This is used when wolfJCE has registered itself as the JKS provider
|
||||
* using the Security property "wolfjce.mapJKStoWKS".
|
||||
*
|
||||
* @return A KeyStore instance from the Sun provider for JKS format
|
||||
* @throws ReflectiveOperationException If reflection fails
|
||||
* @throws KeyStoreException If the KeyStore cannot be created
|
||||
*/
|
||||
private static KeyStore getJksKeyStoreFromSunProvider()
|
||||
throws ReflectiveOperationException, KeyStoreException {
|
||||
/* Try to find the Sun provider */
|
||||
Provider sunProvider = Security.getProvider("SUN");
|
||||
if (sunProvider == null) {
|
||||
throw new KeyStoreException("SUN provider not available");
|
||||
}
|
||||
|
||||
try {
|
||||
/* Try to get the KeyStore from the explicit provider first */
|
||||
return KeyStore.getInstance("JKS", sunProvider);
|
||||
|
||||
} catch (Exception e) {
|
||||
/* Fallback to using reflection if the first approach fails */
|
||||
/* Load the JKS KeyStore class directly from the Sun provider */
|
||||
Class<?> jksKeyStoreClass =
|
||||
Class.forName("sun.security.provider.JavaKeyStore$JKS");
|
||||
Constructor<?> constructor =
|
||||
jksKeyStoreClass.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
KeyStore ks = (KeyStore) constructor.newInstance();
|
||||
|
||||
/* Initialize the KeyStore */
|
||||
Method engineInitMethod =
|
||||
jksKeyStoreClass.getDeclaredMethod("engineInit");
|
||||
engineInitMethod.setAccessible(true);
|
||||
engineInitMethod.invoke(ks);
|
||||
|
||||
return ks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sun provider PKCS12 KeyStore implementation using reflection.
|
||||
* This is used when wolfJCE has registered itself as the PKCS12 provider
|
||||
* using the Security property "wolfjce.mapPKCS12toWKS".
|
||||
*
|
||||
* @return A KeyStore instance from the Sun provider for PKCS12 format
|
||||
* @throws ReflectiveOperationException If reflection fails
|
||||
* @throws KeyStoreException If the KeyStore cannot be created
|
||||
*/
|
||||
private static KeyStore getPkcs12KeyStoreFromSunProvider()
|
||||
throws ReflectiveOperationException, KeyStoreException {
|
||||
/* Try to find the SunJSSE provider */
|
||||
Provider sunJsseProvider = Security.getProvider("SunJSSE");
|
||||
if (sunJsseProvider == null) {
|
||||
/* Try Sun provider as fallback */
|
||||
sunJsseProvider = Security.getProvider("SUN");
|
||||
if (sunJsseProvider == null) {
|
||||
throw new KeyStoreException(
|
||||
"Neither SunJSSE nor SUN provider available");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
/* Try to get the KeyStore through the provider first */
|
||||
return KeyStore.getInstance("PKCS12", sunJsseProvider);
|
||||
|
||||
} catch (Exception e) {
|
||||
/* Fallback to using reflection if the first approach fails */
|
||||
/* Load the PKCS12 KeyStore class */
|
||||
Class<?> pkcs12KeyStoreClass =
|
||||
Class.forName("sun.security.pkcs12.PKCS12KeyStore");
|
||||
Constructor<?> constructor =
|
||||
pkcs12KeyStoreClass.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
KeyStore ks = (KeyStore) constructor.newInstance();
|
||||
|
||||
/* Initialize the KeyStore */
|
||||
Method engineInitMethod =
|
||||
pkcs12KeyStoreClass.getDeclaredMethod("engineInit");
|
||||
engineInitMethod.setAccessible(true);
|
||||
engineInitMethod.invoke(ks);
|
||||
|
||||
return ks;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,468 @@
|
|||
/* WolfCryptUtilsTest.java
|
||||
*
|
||||
* Copyright (C) 2006-2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
package com.wolfssl.provider.jce.test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.AfterClass;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.security.Provider;
|
||||
import java.security.KeyStore;
|
||||
import com.wolfssl.provider.jce.WolfCryptProvider;
|
||||
import com.wolfssl.provider.jce.WolfCryptUtil;
|
||||
import com.wolfssl.wolfcrypt.Fips;
|
||||
|
||||
/**
|
||||
* Test suite for WolfCryptUtils.convertKeyStoreToWKS method.
|
||||
*
|
||||
* Tests converting JKS KeyStore to WKS format and error handling.
|
||||
*/
|
||||
public class WolfCryptUtilTest {
|
||||
|
||||
/* Common test password for test KeyStores */
|
||||
private static final char[] PASSWORD = "wolfsslpassword".toCharArray();
|
||||
private static final String WKS_PROVIDER = "wolfJCE";
|
||||
private static final String TEST_ALIAS = "server";
|
||||
private static final String TEST_JKS_PATH = "examples/certs/server.jks";
|
||||
private static final String TEST_P12_PATH = "examples/certs/client.p12";
|
||||
private static final String TEST_WKS_PATH = "examples/certs/server.wks";
|
||||
private static final char[] CACERTS_PASSWORD = "changeit".toCharArray();
|
||||
|
||||
/* Original security property values */
|
||||
private static String origMapJksToWks = null;
|
||||
private static String origMapPkcs12ToWks = null;
|
||||
|
||||
@Rule(order = Integer.MIN_VALUE)
|
||||
public TestRule testWatcher = new TestWatcher() {
|
||||
protected void starting(Description desc) {
|
||||
System.out.println("\t" + desc.getMethodName());
|
||||
}
|
||||
};
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
|
||||
System.out.println("JCE WolfCryptUtils Class");
|
||||
|
||||
/* Register wolfJCE provider if not already done */
|
||||
Provider wolfJCE = Security.getProvider(WKS_PROVIDER);
|
||||
if (wolfJCE == null) {
|
||||
Security.insertProviderAt(new WolfCryptProvider(), 1);
|
||||
}
|
||||
|
||||
/* Store original security property values */
|
||||
origMapJksToWks = Security.getProperty("wolfjce.mapJKStoWKS");
|
||||
origMapPkcs12ToWks = Security.getProperty("wolfjce.mapPKCS12toWKS");
|
||||
|
||||
/* Make sure we set them to known values at the start */
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", "false");
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", "false");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
/* Restore original security property values */
|
||||
if (origMapJksToWks != null) {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", origMapJksToWks);
|
||||
} else {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", "false");
|
||||
}
|
||||
|
||||
if (origMapPkcs12ToWks != null) {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", origMapPkcs12ToWks);
|
||||
} else {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", "false");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to load a KeyStore file into a ByteArrayInputStream
|
||||
* @param path Path to the KeyStore file
|
||||
* @return ByteArrayInputStream containing the KeyStore data
|
||||
* @throws Exception if file cannot be read
|
||||
*/
|
||||
private static synchronized ByteArrayInputStream loadKeyStoreFile(
|
||||
String path) throws Exception {
|
||||
|
||||
FileInputStream fis = new FileInputStream(path);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
fis.close();
|
||||
|
||||
return new ByteArrayInputStream(baos.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to find the Java cacerts file
|
||||
* @return Path to cacerts file if found, null otherwise
|
||||
*/
|
||||
private static String findCacertsFile() {
|
||||
String javaHome = System.getProperty("java.home");
|
||||
if (javaHome == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Common locations for cacerts file */
|
||||
String[] possiblePaths = {
|
||||
/* Standard locations */
|
||||
javaHome + "/lib/security/cacerts",
|
||||
javaHome + "/jre/lib/security/cacerts",
|
||||
/* Android locations */
|
||||
javaHome + "/etc/security/cacerts",
|
||||
/* Windows specific */
|
||||
javaHome + "\\lib\\security\\cacerts",
|
||||
javaHome + "\\jre\\lib\\security\\cacerts"
|
||||
};
|
||||
|
||||
for (String path : possiblePaths) {
|
||||
File file = new File(path);
|
||||
if (file.exists() && file.isFile()) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with null input stream (should throw an exception)
|
||||
*/
|
||||
@Test
|
||||
public void testNullInputStream() {
|
||||
try {
|
||||
WolfCryptUtil.convertKeyStoreToWKS(null, PASSWORD, PASSWORD, true);
|
||||
fail("Should have thrown an exception for null input stream");
|
||||
} catch (IllegalArgumentException e) {
|
||||
/* Expected exception */
|
||||
assertTrue("Exception message should indicate null input stream",
|
||||
e.getMessage().contains("null"));
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected exception type: " + e.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with null password (should throw an exception)
|
||||
*/
|
||||
@Test
|
||||
public void testNullPassword() {
|
||||
/* Create a dummy keystore for testing */
|
||||
try {
|
||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
ks.load(null, PASSWORD);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ks.store(baos, PASSWORD);
|
||||
ByteArrayInputStream bais =
|
||||
new ByteArrayInputStream(baos.toByteArray());
|
||||
|
||||
WolfCryptUtil.convertKeyStoreToWKS(bais, PASSWORD, null, true);
|
||||
fail("Should have thrown an exception for null password");
|
||||
} catch (IllegalArgumentException e) {
|
||||
/* Expected exception */
|
||||
assertTrue("Exception message should indicate null password",
|
||||
e.getMessage().contains("null"));
|
||||
} catch (Exception e) {
|
||||
fail("Unexpected exception type: " + e.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test converting JKS to WKS with mapJKStoWKS=false
|
||||
*/
|
||||
@Test
|
||||
public void testConvertJksToWksWithoutMapping() throws Exception {
|
||||
/* Store original property value */
|
||||
String origValue = Security.getProperty("wolfjce.mapJKStoWKS");
|
||||
|
||||
try {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", "false");
|
||||
|
||||
/* Load test JKS KeyStore */
|
||||
ByteArrayInputStream jksStream = loadKeyStoreFile(TEST_JKS_PATH);
|
||||
|
||||
/* Convert to WKS */
|
||||
ByteArrayInputStream wksStream =
|
||||
(ByteArrayInputStream)WolfCryptUtil.convertKeyStoreToWKS(
|
||||
jksStream, PASSWORD, PASSWORD, true);
|
||||
|
||||
/* Load the converted WKS KeyStore */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStream, PASSWORD);
|
||||
|
||||
/* Verify the key and certificate were properly converted */
|
||||
assertTrue("Key entry should exist",
|
||||
wksStore.isKeyEntry(TEST_ALIAS));
|
||||
assertNotNull("Private key should exist",
|
||||
wksStore.getKey(TEST_ALIAS, PASSWORD));
|
||||
assertNotNull("Certificate chain should exist",
|
||||
wksStore.getCertificateChain(TEST_ALIAS));
|
||||
assertEquals("Certificate chain should have length 2",
|
||||
2, wksStore.getCertificateChain(TEST_ALIAS).length);
|
||||
} finally {
|
||||
/* Restore original property value */
|
||||
if (origValue != null) {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", origValue);
|
||||
} else {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test converting JKS to WKS with mapJKStoWKS=true
|
||||
*/
|
||||
@Test
|
||||
public void testConvertJksToWksWithMapping() throws Exception {
|
||||
/* Store original property value */
|
||||
String origValue = Security.getProperty("wolfjce.mapJKStoWKS");
|
||||
|
||||
try {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", "true");
|
||||
|
||||
/* Load test JKS KeyStore */
|
||||
ByteArrayInputStream jksStream = loadKeyStoreFile(TEST_JKS_PATH);
|
||||
|
||||
/* Convert to WKS */
|
||||
ByteArrayInputStream wksStream =
|
||||
(ByteArrayInputStream)WolfCryptUtil.convertKeyStoreToWKS(
|
||||
jksStream, PASSWORD, PASSWORD, true);
|
||||
|
||||
/* Load the converted WKS KeyStore */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStream, PASSWORD);
|
||||
|
||||
/* Verify the key and certificate were properly converted */
|
||||
assertTrue("Key entry should exist",
|
||||
wksStore.isKeyEntry(TEST_ALIAS));
|
||||
assertNotNull("Private key should exist",
|
||||
wksStore.getKey(TEST_ALIAS, PASSWORD));
|
||||
assertNotNull("Certificate chain should exist",
|
||||
wksStore.getCertificateChain(TEST_ALIAS));
|
||||
assertEquals("Certificate chain should have length 2",
|
||||
2, wksStore.getCertificateChain(TEST_ALIAS).length);
|
||||
|
||||
} finally {
|
||||
/* Restore original property value */
|
||||
if (origValue != null) {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", origValue);
|
||||
} else {
|
||||
Security.setProperty("wolfjce.mapJKStoWKS", "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test converting PKCS12 to WKS with mapPKCS12toWKS=false
|
||||
*/
|
||||
@Test
|
||||
public void testConvertP12ToWksWithoutMapping() throws Exception {
|
||||
/* Store original property value */
|
||||
String origValue = Security.getProperty("wolfjce.mapPKCS12toWKS");
|
||||
|
||||
try {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", "false");
|
||||
|
||||
/* Load test PKCS12 KeyStore */
|
||||
ByteArrayInputStream p12Stream = loadKeyStoreFile(TEST_P12_PATH);
|
||||
|
||||
/* Convert to WKS */
|
||||
ByteArrayInputStream wksStream =
|
||||
(ByteArrayInputStream)WolfCryptUtil.convertKeyStoreToWKS(
|
||||
p12Stream, PASSWORD, PASSWORD, true);
|
||||
|
||||
/* Load the converted WKS KeyStore */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStream, PASSWORD);
|
||||
|
||||
/* Verify both entries were properly converted */
|
||||
assertTrue("RSA key entry should exist",
|
||||
wksStore.isKeyEntry("client"));
|
||||
assertTrue("ECC key entry should exist",
|
||||
wksStore.isKeyEntry("client-ecc"));
|
||||
|
||||
/* Verify RSA key and certificate */
|
||||
assertNotNull("RSA private key should exist",
|
||||
wksStore.getKey("client", PASSWORD));
|
||||
assertNotNull("RSA certificate chain should exist",
|
||||
wksStore.getCertificateChain("client"));
|
||||
assertEquals("RSA certificate chain should have length 1",
|
||||
1, wksStore.getCertificateChain("client").length);
|
||||
|
||||
/* Verify ECC key and certificate */
|
||||
assertNotNull("ECC private key should exist",
|
||||
wksStore.getKey("client-ecc", PASSWORD));
|
||||
assertNotNull("ECC certificate chain should exist",
|
||||
wksStore.getCertificateChain("client-ecc"));
|
||||
assertEquals("ECC certificate chain should have length 1",
|
||||
1, wksStore.getCertificateChain("client-ecc").length);
|
||||
|
||||
} finally {
|
||||
/* Restore original property value */
|
||||
if (origValue != null) {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", origValue);
|
||||
} else {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test converting PKCS12 to WKS with mapPKCS12toWKS=true
|
||||
*/
|
||||
@Test
|
||||
public void testConvertP12ToWksWithMapping() throws Exception {
|
||||
/* Store original property value */
|
||||
String origValue = Security.getProperty("wolfjce.mapPKCS12toWKS");
|
||||
|
||||
try {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", "true");
|
||||
|
||||
/* Load test PKCS12 KeyStore */
|
||||
ByteArrayInputStream p12Stream = loadKeyStoreFile(TEST_P12_PATH);
|
||||
|
||||
/* Convert to WKS */
|
||||
ByteArrayInputStream wksStream =
|
||||
(ByteArrayInputStream)WolfCryptUtil.convertKeyStoreToWKS(
|
||||
p12Stream, PASSWORD, PASSWORD, true);
|
||||
|
||||
/* Load the converted WKS KeyStore */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStream, PASSWORD);
|
||||
|
||||
/* Verify both entries were properly converted */
|
||||
assertTrue("RSA key entry should exist",
|
||||
wksStore.isKeyEntry("client"));
|
||||
assertTrue("ECC key entry should exist",
|
||||
wksStore.isKeyEntry("client-ecc"));
|
||||
|
||||
/* Verify RSA key and certificate */
|
||||
assertNotNull("RSA private key should exist",
|
||||
wksStore.getKey("client", PASSWORD));
|
||||
assertNotNull("RSA certificate chain should exist",
|
||||
wksStore.getCertificateChain("client"));
|
||||
assertEquals("RSA certificate chain should have length 1",
|
||||
1, wksStore.getCertificateChain("client").length);
|
||||
|
||||
/* Verify ECC key and certificate */
|
||||
assertNotNull("ECC private key should exist",
|
||||
wksStore.getKey("client-ecc", PASSWORD));
|
||||
assertNotNull("ECC certificate chain should exist",
|
||||
wksStore.getCertificateChain("client-ecc"));
|
||||
assertEquals("ECC certificate chain should have length 1",
|
||||
1, wksStore.getCertificateChain("client-ecc").length);
|
||||
|
||||
} finally {
|
||||
/* Restore original property value */
|
||||
if (origValue != null) {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", origValue);
|
||||
} else {
|
||||
Security.setProperty("wolfjce.mapPKCS12toWKS", "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test converting WKS to WKS (should return same InputStream)
|
||||
*/
|
||||
@Test
|
||||
public void testConvertWksToWks() throws Exception {
|
||||
/* Load test WKS KeyStore */
|
||||
ByteArrayInputStream wksStream = loadKeyStoreFile(TEST_WKS_PATH);
|
||||
|
||||
/* Convert WKS to WKS */
|
||||
ByteArrayInputStream wksStreamB =
|
||||
(ByteArrayInputStream)WolfCryptUtil.convertKeyStoreToWKS(
|
||||
wksStream, PASSWORD, PASSWORD, true);
|
||||
|
||||
/* Verify the KeyStore can still be loaded */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStreamB, PASSWORD);
|
||||
|
||||
/* Verify the key and certificate were preserved */
|
||||
assertTrue("Key entry should exist", wksStore.isKeyEntry(TEST_ALIAS));
|
||||
assertNotNull("Private key should exist",
|
||||
wksStore.getKey(TEST_ALIAS, PASSWORD));
|
||||
assertNotNull("Certificate chain should exist",
|
||||
wksStore.getCertificateChain(TEST_ALIAS));
|
||||
assertEquals("Certificate chain should have length 2",
|
||||
2, wksStore.getCertificateChain(TEST_ALIAS).length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test converting Java cacerts to WKS format
|
||||
*/
|
||||
@Test
|
||||
public void testConvertCacertsToWks() throws Exception {
|
||||
|
||||
boolean failOnInsertErrors = true;
|
||||
char[] password = CACERTS_PASSWORD;
|
||||
|
||||
/* Find cacerts file */
|
||||
String cacertsPath = findCacertsFile();
|
||||
if (cacertsPath == null) {
|
||||
System.out.println(
|
||||
"Skipping testConvertCacertsToWks: cacerts file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* If using FIPS mode, set failOnInsertErrors to false */
|
||||
if (Fips.enabled) {
|
||||
failOnInsertErrors = false;
|
||||
password = (new String(CACERTS_PASSWORD) +
|
||||
new String(CACERTS_PASSWORD)).toCharArray();
|
||||
}
|
||||
|
||||
/* Load cacerts KeyStore */
|
||||
ByteArrayInputStream cacertsStream = loadKeyStoreFile(cacertsPath);
|
||||
|
||||
/* Convert to WKS */
|
||||
ByteArrayInputStream wksStream =
|
||||
(ByteArrayInputStream)WolfCryptUtil.convertKeyStoreToWKS(
|
||||
cacertsStream, CACERTS_PASSWORD, password, failOnInsertErrors);
|
||||
|
||||
/* Load the converted WKS KeyStore */
|
||||
KeyStore wksStore = KeyStore.getInstance("WKS", "wolfJCE");
|
||||
wksStore.load(wksStream, password);
|
||||
|
||||
/* Verify the KeyStore was converted and contains entries */
|
||||
assertTrue("WKS KeyStore should contain entries",
|
||||
wksStore.size() > 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +43,8 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
WolfCryptKeyGeneratorTest.class,
|
||||
WolfCryptKeyPairGeneratorTest.class,
|
||||
WolfCryptPKIXCertPathValidatorTest.class,
|
||||
WolfSSLKeyStoreTest.class
|
||||
WolfSSLKeyStoreTest.class,
|
||||
WolfCryptUtilTest.class
|
||||
})
|
||||
|
||||
public class WolfJCETestSuite { }
|
||||
|
|
|
@ -1430,7 +1430,6 @@ public class WolfSSLKeyStoreTest {
|
|||
assertEquals(1, store.size());
|
||||
}
|
||||
|
||||
//CHRIS
|
||||
@Test
|
||||
public void testLoadWKSasJKSFromFile()
|
||||
throws KeyStoreException, IOException, FileNotFoundException,
|
||||
|
|
Loading…
Reference in New Issue