From bb8d57c23104fe6ff8571fad60f91b5b7db49c40 Mon Sep 17 00:00:00 2001 From: Kostas Tsiounis Date: Fri, 4 Oct 2024 17:48:58 -0400 Subject: [PATCH] Check if OpenSSL is in FIPS mode and use Java for some algorithms If the OpenSSL library used is in FIPS mode, avoid using it for algorithms that are not FIPS compliant and revert to the original Java implementation. Signed-off-by: Kostas Tsiounis --- .../jdk/crypto/jniprovider/NativeCrypto.java | 51 +++++++++++++++++++ .../share/native/libjncrypto/NativeCrypto.c | 36 +++++++++++++ .../com/sun/crypto/provider/SunJCE.java | 5 +- .../share/classes/sun/security/ec/SunEC.java | 10 ++-- .../sun/security/provider/SunEntries.java | 3 +- 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java b/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java index c8358b4b4d5..4487aca4f11 100644 --- a/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java +++ b/closed/src/java.base/share/classes/jdk/crypto/jniprovider/NativeCrypto.java @@ -25,6 +25,7 @@ import java.lang.ref.Cleaner; import java.security.*; +import java.util.Set; import com.ibm.oti.vm.VM; @@ -70,6 +71,8 @@ public class NativeCrypto { private static final boolean traceEnabled = Boolean.parseBoolean( GetPropertyAction.privilegedGetProperty("jdk.nativeCryptoTrace", "false")); + private static final Set disallowedAlgosFIPS = Set.of("MD5", "ChaCha20"); + private static final class InstanceHolder { private static final NativeCrypto instance = new NativeCrypto(); } @@ -79,6 +82,8 @@ private static final class InstanceHolder { // or one of the OPENSSL_VERSION_x_x_x constants private final long ossl_ver; + private final boolean isOpenSSLFIPS; + private static long loadCryptoLibraries() { long osslVersion; @@ -105,6 +110,11 @@ private static long loadCryptoLibraries() { @SuppressWarnings("removal") private NativeCrypto() { ossl_ver = AccessController.doPrivileged((PrivilegedAction) () -> loadCryptoLibraries()).longValue(); + if (ossl_ver != -1) { + isOpenSSLFIPS = isOpenSSLFIPS(); + } else { + isOpenSSLFIPS = false; + } } /** @@ -178,6 +188,45 @@ public static final boolean isTraceEnabled() { return traceEnabled; } + public static final boolean isOpenSSLFIPSVersion() { + return InstanceHolder.instance.isOpenSSLFIPS; + } + + /** + * Check whether a native implementation is available in the loaded OpenSSL library. + * Note that, an algorithm could be unavailable due to options used to build the + * OpenSSL version utilized, or using a FIPS version that doesn't allow it. + * + * @param algorithm the algorithm checked + * @return whether a native implementation of the given crypto algorithm is available + */ + public static final boolean isAlgorithmAvailable(String algorithm) { + boolean isAlgorithmAvailable = false; + if (isAllowedAndLoaded()) { + if (isOpenSSLFIPSVersion()) { + if (disallowedAlgosFIPS.contains(algorithm)) { + return false; + } + } + switch (algorithm) { + case "MD5": + return isMD5Available(); + default: + return true; + } + } + + //Issue a message indicating whether the crypto implementation is available. + if (traceEnabled) { + if (isAlgorithmAvailable) { + System.err.println(algorithm + " native crypto implementation is available."); + } else { + System.err.println(algorithm + " native crypto implementation is not available."); + } + } + return isAlgorithmAvailable; + } + @CallerSensitive public static NativeCrypto getNativeCrypto() { ClassLoader callerClassLoader = Reflection.getCallerClass().getClassLoader(); @@ -202,6 +251,8 @@ public void run() { private static final native long loadCrypto(boolean trace); + private static final native boolean isOpenSSLFIPS(); + public static final native boolean isMD5Available(); public final native long DigestCreateContext(long nativeBuffer, diff --git a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c index 4d9a05a58c7..8e76b3278c0 100644 --- a/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c +++ b/closed/src/java.base/share/native/libjncrypto/NativeCrypto.c @@ -82,6 +82,9 @@ int OSSL102_RSA_set0_crt_params(RSA *, BIGNUM *, BIGNUM *, BIGNUM *); #define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG #endif +/* Check whether loaded library is in FIPS mode. */ +jboolean OSSL_IS_FIPS; + /* Header for EC algorithm */ jboolean OSSL_ECGF2M; int setECPublicCoordinates(EC_KEY *, BIGNUM *, BIGNUM *, int); @@ -365,6 +368,18 @@ static jlong extractVersionToJlong(const char *astring) } static void *crypto_library = NULL; + +/* + * Class: jdk_crypto_jniprovider_NativeCrypto + * Method: isOpenSSLFIPS + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_isOpenSSLFIPS + (JNIEnv *env, jclass thisObj) +{ + return OSSL_IS_FIPS; +} + /* * Class: jdk_crypto_jniprovider_NativeCrypto * Method: loadCrypto @@ -440,6 +455,27 @@ JNIEXPORT jlong JNICALL Java_jdk_crypto_jniprovider_NativeCrypto_loadCrypto } } + /* Check whether the loaded OpenSSL library is in FIPS mode. */ + if (ossl_ver >= OPENSSL_VERSION_3_0_0) { + typedef int OSSL_fipsmode_t(OSSL_LIB_CTX *); + OSSL_fipsmode_t* OSSL_fipsmode; + OSSL_fipsmode = (OSSL_fipsmode_t*)find_crypto_symbol(crypto_library, "EVP_default_properties_is_fips_enabled"); + if ((NULL != OSSL_fipsmode) && ((*OSSL_fipsmode)(NULL) == 1)) { + OSSL_IS_FIPS = JNI_TRUE; + } else { + OSSL_IS_FIPS = JNI_FALSE; + } + } else { + typedef int OSSL_fipsmode_t(void); + OSSL_fipsmode_t* OSSL_fipsmode; + OSSL_fipsmode = (OSSL_fipsmode_t*)find_crypto_symbol(crypto_library, "FIPS_mode"); + if ((NULL != OSSL_fipsmode) && ((*OSSL_fipsmode)() == 1)) { + OSSL_IS_FIPS = JNI_TRUE; + } else { + OSSL_IS_FIPS = JNI_FALSE; + } + } + /* Load the function symbols for OpenSSL errors. */ OSSL_error_string_n = (OSSL_error_string_n_t*)find_crypto_symbol(crypto_library, "ERR_error_string_n"); OSSL_error_string = (OSSL_error_string_t*)find_crypto_symbol(crypto_library, "ERR_error_string"); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java index 863ec6e077b..975d679875a 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -340,7 +340,10 @@ void putEntries() { attrs.clear(); attrs.put("SupportedKeyFormats", "RAW"); - if (useNativeChaCha20Cipher && (NativeCrypto.getVersionIfAvailable() >= NativeCrypto.OPENSSL_VERSION_1_1_0)) { + if (useNativeChaCha20Cipher + && NativeCrypto.isAlgorithmAvailable("ChaCha20") + && (NativeCrypto.getVersionIfAvailable() >= NativeCrypto.OPENSSL_VERSION_1_1_0) + ) { ps("Cipher", "ChaCha20", "com.sun.crypto.provider.NativeChaCha20Cipher$ChaCha20Only", null, attrs); diff --git a/src/java.base/share/classes/sun/security/ec/SunEC.java b/src/java.base/share/classes/sun/security/ec/SunEC.java index b2c8da66aab..e3d8f195797 100644 --- a/src/java.base/share/classes/sun/security/ec/SunEC.java +++ b/src/java.base/share/classes/sun/security/ec/SunEC.java @@ -65,30 +65,30 @@ public final class SunEC extends Provider { /* The property 'jdk.nativeEC' is used to control enablement of the native * ECDH implementation. */ - private static final boolean useNativeECDH = NativeCrypto.isAlgorithmEnabled("jdk.nativeEC", "SunEC"); + private static final boolean useNativeECDH = NativeCrypto.isAlgorithmEnabled("jdk.nativeEC", "ECDH"); /* The property 'jdk.nativeECKeyGen' is used to control enablement of the native * ECKeyGeneration implementation. * OpenSSL 1.1.0 or above is required for EC key generation support. */ - private static final boolean useNativeECKeyGen = NativeCrypto.isAlgorithmEnabled("jdk.nativeECKeyGen", "SunEC"); + private static final boolean useNativeECKeyGen = NativeCrypto.isAlgorithmEnabled("jdk.nativeECKeyGen", "ECKeyGen"); /* The property 'jdk.nativeECDSA' is used to control enablement of the native * ECDSA signature implementation. */ - private static final boolean useNativeECDSA = NativeCrypto.isAlgorithmEnabled("jdk.nativeECDSA", "SunEC"); + private static final boolean useNativeECDSA = NativeCrypto.isAlgorithmEnabled("jdk.nativeECDSA", "ECDSA"); /* The property 'jdk.nativeXDHKeyAgreement' is used to control enablement of the native * XDH key agreement. XDH key agreement is only supported in OpenSSL 1.1.1 and above. */ private static final boolean useNativeXDHKeyAgreement = - NativeCrypto.isAlgorithmEnabled("jdk.nativeXDHKeyAgreement", "SunEC"); + NativeCrypto.isAlgorithmEnabled("jdk.nativeXDHKeyAgreement", "XDHKeyAgreement"); /* The property 'jdk.nativeXDHKeyGen' is used to control enablement of the native * XDH key generation. XDH key generation is only supported in OpenSSL 1.1.1 and above. */ private static final boolean useNativeXDHKeyGen = - NativeCrypto.isAlgorithmEnabled("jdk.nativeXDHKeyGen", "SunEC"); + NativeCrypto.isAlgorithmEnabled("jdk.nativeXDHKeyGen", "XDHKeyGen"); private static class ProviderServiceA extends ProviderService { ProviderServiceA(Provider p, String type, String algo, String cn, diff --git a/src/java.base/share/classes/sun/security/provider/SunEntries.java b/src/java.base/share/classes/sun/security/provider/SunEntries.java index 8928a9e0fd7..df990a7030c 100644 --- a/src/java.base/share/classes/sun/security/provider/SunEntries.java +++ b/src/java.base/share/classes/sun/security/provider/SunEntries.java @@ -330,8 +330,7 @@ public final class SunEntries { */ /* Don't use native MD5 on AIX due to an observed performance regression. */ if (useNativeMD5 - && NativeCrypto.isAllowedAndLoaded() - && NativeCrypto.isMD5Available() + && NativeCrypto.isAlgorithmAvailable("MD5") && !OperatingSystem.isAix() ) { providerMD5 = "sun.security.provider.NativeMD5";