Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Factory
java-17-openjdk
fips.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File fips.patch of Package java-17-openjdk
--- a/make/autoconf/build-aux/pkg.m4 +++ b/make/autoconf/build-aux/pkg.m4 @@ -179,3 +179,19 @@ else ifelse([$3], , :, [$3]) fi[]dnl ])# PKG_CHECK_MODULES + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR --- /dev/null +++ b/make/autoconf/lib-sysconf.m4 @@ -0,0 +1,87 @@ +# +# Copyright (c) 2021, Red Hat, Inc. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code 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 +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +################################################################################ +# Setup system configuration libraries +################################################################################ +AC_DEFUN_ONCE([LIB_SETUP_SYSCONF_LIBS], +[ + ############################################################################### + # + # Check for the NSS library + # + AC_MSG_CHECKING([for NSS library directory]) + PKG_CHECK_VAR(NSS_LIBDIR, nss, libdir, [AC_MSG_RESULT([$NSS_LIBDIR])], [AC_MSG_RESULT([not found])]) + + AC_MSG_CHECKING([whether to link the system NSS library with the System Configurator (libsysconf)]) + + # default is not available + DEFAULT_SYSCONF_NSS=no + + AC_ARG_ENABLE([sysconf-nss], [AS_HELP_STRING([--enable-sysconf-nss], + [build the System Configurator (libsysconf) using the system NSS library if available @<:@disabled@:>@])], + [ + case "${enableval}" in + yes) + sysconf_nss=yes + ;; + *) + sysconf_nss=no + ;; + esac + ], + [ + sysconf_nss=${DEFAULT_SYSCONF_NSS} + ]) + AC_MSG_RESULT([$sysconf_nss]) + + USE_SYSCONF_NSS=false + if test "x${sysconf_nss}" = "xyes"; then + PKG_CHECK_MODULES(NSS, nss >= 3.53, [NSS_FOUND=yes], [NSS_FOUND=no]) + if test "x${NSS_FOUND}" = "xyes"; then + AC_MSG_CHECKING([for system FIPS support in NSS]) + saved_libs="${LIBS}" + saved_cflags="${CFLAGS}" + CFLAGS="${CFLAGS} ${NSS_CFLAGS}" + LIBS="${LIBS} ${NSS_LIBS}" + AC_LANG_PUSH([C]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <nss3/pk11pub.h>]], + [[SECMOD_GetSystemFIPSEnabled()]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([System NSS FIPS detection unavailable])]) + AC_LANG_POP([C]) + CFLAGS="${saved_cflags}" + LIBS="${saved_libs}" + USE_SYSCONF_NSS=true + else + dnl NSS 3.53 is the one that introduces the SECMOD_GetSystemFIPSEnabled API + dnl in nss3/pk11pub.h. + AC_MSG_ERROR([--enable-sysconf-nss specified, but NSS 3.53 or above not found.]) + fi + fi + AC_SUBST(USE_SYSCONF_NSS) + AC_SUBST(NSS_LIBDIR) +]) --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -33,6 +33,7 @@ m4_include([lib-std.m4]) m4_include([lib-x11.m4]) m4_include([lib-fontconfig.m4]) m4_include([lib-tests.m4]) +m4_include([lib-sysconf.m4]) ################################################################################ # Determine which libraries are needed for this configuration @@ -104,6 +105,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES], LIB_SETUP_BUNDLED_LIBS LIB_SETUP_MISC_LIBS LIB_TESTS_SETUP_GTEST + LIB_SETUP_SYSCONF_LIBS BASIC_JDKLIB_LIBS="" if test "x$TOOLCHAIN_TYPE" != xmicrosoft; then --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -844,6 +844,11 @@ INSTALL_SYSCONFDIR=@sysconfdir@ # Libraries # +USE_SYSCONF_NSS:=@USE_SYSCONF_NSS@ +NSS_LIBS:=@NSS_LIBS@ +NSS_CFLAGS:=@NSS_CFLAGS@ +NSS_LIBDIR:=@NSS_LIBDIR@ + USE_EXTERNAL_LCMS:=@USE_EXTERNAL_LCMS@ LCMS_CFLAGS:=@LCMS_CFLAGS@ LCMS_LIBS:=@LCMS_LIBS@ --- a/make/modules/java.base/Gendata.gmk +++ b/make/modules/java.base/Gendata.gmk @@ -98,3 +98,17 @@ $(GENDATA_JAVA_SECURITY): $(BUILD_TOOLS_JDK) $(GENDATA_JAVA_SECURITY_SRC) $(REST TARGETS += $(GENDATA_JAVA_SECURITY) ################################################################################ + +GENDATA_NSS_FIPS_CFG_SRC := $(TOPDIR)/src/java.base/share/conf/security/nss.fips.cfg.in +GENDATA_NSS_FIPS_CFG := $(SUPPORT_OUTPUTDIR)/modules_conf/java.base/security/nss.fips.cfg + +$(GENDATA_NSS_FIPS_CFG): $(GENDATA_NSS_FIPS_CFG_SRC) + $(call LogInfo, Generating nss.fips.cfg) + $(call MakeTargetDir) + $(call ExecuteWithLog, $(SUPPORT_OUTPUTDIR)/gensrc/java.base/_$(@F), \ + ( $(SED) -e 's:@NSS_LIBDIR@:$(NSS_LIBDIR):g' $< ) > $@ \ + ) + +TARGETS += $(GENDATA_NSS_FIPS_CFG) + +################################################################################ --- a/make/modules/java.base/Lib.gmk +++ b/make/modules/java.base/Lib.gmk @@ -167,6 +167,29 @@ ifeq ($(call isTargetOsType, unix), true) endif endif +################################################################################ +# Create the systemconf library + +LIBSYSTEMCONF_CFLAGS := +LIBSYSTEMCONF_CXXFLAGS := + +ifeq ($(USE_SYSCONF_NSS), true) + LIBSYSTEMCONF_CFLAGS += $(NSS_CFLAGS) -DSYSCONF_NSS + LIBSYSTEMCONF_CXXFLAGS += $(NSS_CFLAGS) -DSYSCONF_NSS +endif + +$(eval $(call SetupJdkLibrary, BUILD_LIBSYSTEMCONF, \ + NAME := systemconf, \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CFLAGS_JDKLIB) $(LIBSYSTEMCONF_CFLAGS), \ + CXXFLAGS := $(CXXFLAGS_JDKLIB) $(LIBSYSTEMCONF_CXXFLAGS), \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ + $(call SET_SHARED_LIBRARY_ORIGIN), \ + LIBS_unix := $(LIBDL) $(NSS_LIBS), \ +)) + +TARGETS += $(BUILD_LIBSYSTEMCONF) + ################################################################################ # Create the symbols file for static builds. --- a/src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/HmacPKCS12PBECore.java @@ -25,13 +25,12 @@ package com.sun.crypto.provider; -import java.util.Arrays; - import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.PBEKeySpec; import java.security.*; import java.security.spec.*; +import sun.security.util.PBEUtil; /** * This is an implementation of the HMAC algorithms as defined @@ -108,79 +107,15 @@ abstract class HmacPKCS12PBECore extends HmacCore { */ protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - char[] passwdChars; - byte[] salt = null; - int iCount = 0; - if (key instanceof javax.crypto.interfaces.PBEKey) { - javax.crypto.interfaces.PBEKey pbeKey = - (javax.crypto.interfaces.PBEKey) key; - passwdChars = pbeKey.getPassword(); - salt = pbeKey.getSalt(); // maybe null if unspecified - iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified - } else if (key instanceof SecretKey) { - byte[] passwdBytes; - if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) || - (passwdBytes = key.getEncoded()) == null) { - throw new InvalidKeyException("Missing password"); - } - passwdChars = new char[passwdBytes.length]; - for (int i=0; i<passwdChars.length; i++) { - passwdChars[i] = (char) (passwdBytes[i] & 0x7f); - } - Arrays.fill(passwdBytes, (byte)0x00); - } else { - throw new InvalidKeyException("SecretKey of PBE type required"); - } - + PBEKeySpec keySpec = PBEUtil.getPBAKeySpec(key, params); byte[] derivedKey; try { - if (params == null) { - // should not auto-generate default values since current - // javax.crypto.Mac api does not have any method for caller to - // retrieve the generated defaults. - if ((salt == null) || (iCount == 0)) { - throw new InvalidAlgorithmParameterException - ("PBEParameterSpec required for salt and iteration count"); - } - } else if (!(params instanceof PBEParameterSpec)) { - throw new InvalidAlgorithmParameterException - ("PBEParameterSpec type required"); - } else { - PBEParameterSpec pbeParams = (PBEParameterSpec) params; - // make sure the parameter values are consistent - if (salt != null) { - if (!Arrays.equals(salt, pbeParams.getSalt())) { - throw new InvalidAlgorithmParameterException - ("Inconsistent value of salt between key and params"); - } - } else { - salt = pbeParams.getSalt(); - } - if (iCount != 0) { - if (iCount != pbeParams.getIterationCount()) { - throw new InvalidAlgorithmParameterException - ("Different iteration count between key and params"); - } - } else { - iCount = pbeParams.getIterationCount(); - } - } - // For security purpose, we need to enforce a minimum length - // for salt; just require the minimum salt length to be 8-byte - // which is what PKCS#5 recommends and openssl does. - if (salt.length < 8) { - throw new InvalidAlgorithmParameterException - ("Salt must be at least 8 bytes long"); - } - if (iCount <= 0) { - throw new InvalidAlgorithmParameterException - ("IterationCount must be a positive number"); - } - derivedKey = PKCS12PBECipherCore.derive(passwdChars, salt, - iCount, engineGetMacLength(), PKCS12PBECipherCore.MAC_KEY, - algorithm, bl); + derivedKey = PKCS12PBECipherCore.derive( + keySpec.getPassword(), keySpec.getSalt(), + keySpec.getIterationCount(), engineGetMacLength(), + PKCS12PBECipherCore.MAC_KEY, algorithm, bl); } finally { - Arrays.fill(passwdChars, '\0'); + keySpec.clearPassword(); } SecretKey cipherKey = new SecretKeySpec(derivedKey, "HmacSHA1"); super.engineInit(cipherKey, null); --- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java @@ -27,10 +27,11 @@ package com.sun.crypto.provider; import java.security.*; import java.security.spec.*; -import java.util.Arrays; import javax.crypto.*; import javax.crypto.spec.*; +import sun.security.util.PBEUtil; + /** * This class represents password-based encryption as defined by the PKCS #5 * standard. @@ -54,9 +55,8 @@ abstract class PBES2Core extends CipherSpi { private final PBKDF2Core kdf; private final String pbeAlgo; private final String cipherAlgo; - private int iCount = DEFAULT_COUNT; - private byte[] salt = null; - private IvParameterSpec ivSpec = null; + private final PBEUtil.PBES2Helper pbes2Helper = new PBEUtil.PBES2Helper( + DEFAULT_SALT_LENGTH, DEFAULT_COUNT); /** * Creates an instance of PBE Scheme 2 according to the selected @@ -129,32 +129,8 @@ abstract class PBES2Core extends CipherSpi { } protected AlgorithmParameters engineGetParameters() { - AlgorithmParameters params = null; - if (salt == null) { - // generate random salt and use default iteration count - salt = new byte[DEFAULT_SALT_LENGTH]; - SunJCE.getRandom().nextBytes(salt); - iCount = DEFAULT_COUNT; - } - if (ivSpec == null) { - // generate random IV - byte[] ivBytes = new byte[blkSize]; - SunJCE.getRandom().nextBytes(ivBytes); - ivSpec = new IvParameterSpec(ivBytes); - } - PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount, ivSpec); - try { - params = AlgorithmParameters.getInstance(pbeAlgo, - SunJCE.getInstance()); - params.init(pbeSpec); - } catch (NoSuchAlgorithmException nsae) { - // should never happen - throw new RuntimeException("SunJCE called, but not configured"); - } catch (InvalidParameterSpecException ipse) { - // should never happen - throw new RuntimeException("PBEParameterSpec not supported"); - } - return params; + return pbes2Helper.getAlgorithmParameters( + blkSize, pbeAlgo, SunJCE.getInstance(), SunJCE.getRandom()); } protected void engineInit(int opmode, Key key, SecureRandom random) @@ -174,105 +150,8 @@ abstract class PBES2Core extends CipherSpi { SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - if (key == null) { - throw new InvalidKeyException("Null key"); - } - - byte[] passwdBytes = key.getEncoded(); - char[] passwdChars = null; - PBEKeySpec pbeSpec; - try { - if ((passwdBytes == null) || - !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) { - throw new InvalidKeyException("Missing password"); - } - - // TBD: consolidate the salt, ic and IV parameter checks below - - // Extract salt and iteration count from the key, if present - if (key instanceof javax.crypto.interfaces.PBEKey) { - salt = ((javax.crypto.interfaces.PBEKey)key).getSalt(); - if (salt != null && salt.length < 8) { - throw new InvalidAlgorithmParameterException( - "Salt must be at least 8 bytes long"); - } - iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount(); - if (iCount == 0) { - iCount = DEFAULT_COUNT; - } else if (iCount < 0) { - throw new InvalidAlgorithmParameterException( - "Iteration count must be a positive number"); - } - } - - // Extract salt, iteration count and IV from the params, if present - if (params == null) { - if (salt == null) { - // generate random salt and use default iteration count - salt = new byte[DEFAULT_SALT_LENGTH]; - random.nextBytes(salt); - iCount = DEFAULT_COUNT; - } - if ((opmode == Cipher.ENCRYPT_MODE) || - (opmode == Cipher.WRAP_MODE)) { - // generate random IV - byte[] ivBytes = new byte[blkSize]; - random.nextBytes(ivBytes); - ivSpec = new IvParameterSpec(ivBytes); - } - } else { - if (!(params instanceof PBEParameterSpec)) { - throw new InvalidAlgorithmParameterException - ("Wrong parameter type: PBE expected"); - } - // salt and iteration count from the params take precedence - byte[] specSalt = ((PBEParameterSpec) params).getSalt(); - if (specSalt != null && specSalt.length < 8) { - throw new InvalidAlgorithmParameterException( - "Salt must be at least 8 bytes long"); - } - salt = specSalt; - int specICount = ((PBEParameterSpec) params).getIterationCount(); - if (specICount == 0) { - specICount = DEFAULT_COUNT; - } else if (specICount < 0) { - throw new InvalidAlgorithmParameterException( - "Iteration count must be a positive number"); - } - iCount = specICount; - - AlgorithmParameterSpec specParams = - ((PBEParameterSpec) params).getParameterSpec(); - if (specParams != null) { - if (specParams instanceof IvParameterSpec) { - ivSpec = (IvParameterSpec)specParams; - } else { - throw new InvalidAlgorithmParameterException( - "Wrong parameter type: IV expected"); - } - } else if ((opmode == Cipher.ENCRYPT_MODE) || - (opmode == Cipher.WRAP_MODE)) { - // generate random IV - byte[] ivBytes = new byte[blkSize]; - random.nextBytes(ivBytes); - ivSpec = new IvParameterSpec(ivBytes); - } else { - throw new InvalidAlgorithmParameterException( - "Missing parameter type: IV expected"); - } - } - - passwdChars = new char[passwdBytes.length]; - for (int i = 0; i < passwdChars.length; i++) - passwdChars[i] = (char) (passwdBytes[i] & 0x7f); - - pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength); - // password char[] was cloned in PBEKeySpec constructor, - // so we can zero it out here - } finally { - if (passwdChars != null) Arrays.fill(passwdChars, '\0'); - if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00); - } + PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec(blkSize, keyLength, + opmode, key, params, random); PBKDF2KeyImpl s; @@ -291,22 +170,14 @@ abstract class PBES2Core extends CipherSpi { SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo); // initialize the underlying cipher - cipher.init(opmode, cipherKey, ivSpec, random); + cipher.init(opmode, cipherKey, pbes2Helper.getIvSpec(), random); } protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - AlgorithmParameterSpec pbeSpec = null; - if (params != null) { - try { - pbeSpec = params.getParameterSpec(PBEParameterSpec.class); - } catch (InvalidParameterSpecException ipse) { - throw new InvalidAlgorithmParameterException( - "Wrong parameter type: PBE expected"); - } - } - engineInit(opmode, key, pbeSpec, random); + engineInit(opmode, key, PBEUtil.PBES2Helper.getParameterSpec(params), + random); } protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { --- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -31,6 +31,7 @@ import java.security.SecureRandom; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.List; +import jdk.internal.access.SharedSecrets; import static sun.security.util.SecurityConstants.PROVIDER_VER; import static sun.security.util.SecurityProviderConstants.*; @@ -78,6 +79,10 @@ import static sun.security.util.SecurityProviderConstants.*; public final class SunJCE extends Provider { + private static final boolean systemFipsEnabled = + SharedSecrets.getJavaSecuritySystemConfiguratorAccess() + .isSystemFipsEnabled(); + @java.io.Serial private static final long serialVersionUID = 6812507587804302833L; @@ -143,6 +148,7 @@ public final class SunJCE extends Provider { void putEntries() { // reuse attribute map and reset before each reuse HashMap<String, String> attrs = new HashMap<>(3); + if (!systemFipsEnabled) { attrs.put("SupportedModes", "ECB"); attrs.put("SupportedPaddings", "NOPADDING|PKCS1PADDING|OAEPPADDING" + "|OAEPWITHMD5ANDMGF1PADDING" @@ -422,6 +428,7 @@ public final class SunJCE extends Provider { psA("KeyPairGenerator", "DiffieHellman", "com.sun.crypto.provider.DHKeyPairGenerator", null); + } /* * Algorithm parameter generation engines @@ -430,6 +437,7 @@ public final class SunJCE extends Provider { "DiffieHellman", "com.sun.crypto.provider.DHParameterGenerator", null); + if (!systemFipsEnabled) { /* * Key Agreement engines */ @@ -439,6 +447,7 @@ public final class SunJCE extends Provider { psA("KeyAgreement", "DiffieHellman", "com.sun.crypto.provider.DHKeyAgreement", attrs); + } /* * Algorithm Parameter engines @@ -610,6 +619,7 @@ public final class SunJCE extends Provider { ps("SecretKeyFactory", "PBEWithHmacSHA512AndAES_256", "com.sun.crypto.provider.PBEKeyFactory$PBEWithHmacSHA512AndAES_256"); + if (!systemFipsEnabled) { // PBKDF2 psA("SecretKeyFactory", "PBKDF2WithHmacSHA1", "com.sun.crypto.provider.PBKDF2Core$HmacSHA1", @@ -723,6 +733,7 @@ public final class SunJCE extends Provider { "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator", List.of("SunTls12RsaPremasterSecret"), null); } + } // Return the instance of this class or create one if needed. static SunJCE getInstance() { --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -33,6 +33,7 @@ import java.net.URL; import jdk.internal.access.JavaSecurityPropertiesAccess; import jdk.internal.event.EventHelper; import jdk.internal.event.SecurityPropertyModificationEvent; +import jdk.internal.access.JavaSecuritySystemConfiguratorAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.StaticProperty; import sun.security.util.Debug; @@ -57,6 +58,11 @@ import sun.security.jca.*; public final class Security { + private static final String SYS_PROP_SWITCH = + "java.security.disableSystemPropertiesFile"; + private static final String SEC_PROP_SWITCH = + "security.useSystemPropertiesFile"; + /* Are we debugging? -- for developers */ private static final Debug sdebug = Debug.getInstance("properties"); @@ -74,6 +80,19 @@ public final class Security { } static { + // Initialise here as used by code with system properties disabled + SharedSecrets.setJavaSecuritySystemConfiguratorAccess( + new JavaSecuritySystemConfiguratorAccess() { + @Override + public boolean isSystemFipsEnabled() { + return SystemConfigurator.isSystemFipsEnabled(); + } + @Override + public boolean isPlainKeySupportEnabled() { + return SystemConfigurator.isPlainKeySupportEnabled(); + } + }); + // doPrivileged here because there are multiple // things in initialize that might require privs. // (the FileInputStream call and the File.exists call, @@ -97,6 +116,7 @@ public final class Security { private static void initialize() { props = new Properties(); boolean overrideAll = false; + boolean systemSecPropsEnabled = false; // first load the system properties file // to determine the value of security.overridePropertiesFile @@ -117,6 +137,60 @@ public final class Security { } loadProps(null, extraPropFile, overrideAll); } + + boolean sysUseProps = Boolean.valueOf(System.getProperty(SYS_PROP_SWITCH, "false")); + boolean secUseProps = Boolean.valueOf(props.getProperty(SEC_PROP_SWITCH)); + if (sdebug != null) { + sdebug.println(SYS_PROP_SWITCH + "=" + sysUseProps); + sdebug.println(SEC_PROP_SWITCH + "=" + secUseProps); + } + if (!sysUseProps && secUseProps) { + systemSecPropsEnabled = SystemConfigurator.configureSysProps(props); + if (!systemSecPropsEnabled) { + if (sdebug != null) { + sdebug.println("WARNING: System security properties could not be loaded."); + } + } + } else { + if (sdebug != null) { + sdebug.println("System security property support disabled by user."); + } + } + + if (systemSecPropsEnabled) { + boolean shouldEnable; + String sysProp = System.getProperty("com.suse.fips"); + if (sysProp == null) { + shouldEnable = true; + if (sdebug != null) { + sdebug.println("com.suse.fips unset, using default value of true"); + } + } else { + shouldEnable = Boolean.valueOf(sysProp); + if (sdebug != null) { + sdebug.println("com.suse.fips set, using its value " + shouldEnable); + } + } + if (shouldEnable) { + boolean fipsEnabled = SystemConfigurator.configureFIPS(props); + if (sdebug != null) { + if (fipsEnabled) { + sdebug.println("FIPS mode support configured and enabled."); + } else { + sdebug.println("FIPS mode support disabled."); + } + } + } else { + if (sdebug != null ) { + sdebug.println("FIPS mode support disabled by user."); + } + } + } else { + if (sdebug != null) { + sdebug.println("WARNING: FIPS mode support can not be enabled without " + + "system security properties being enabled."); + } + } initialSecurityProperties = (Properties) props.clone(); if (sdebug != null) { for (String key : props.stringPropertyNames()) { @@ -124,10 +198,9 @@ public final class Security { props.getProperty(key)); } } - } - private static boolean loadProps(File masterFile, String extraPropFile, boolean overrideAll) { + static boolean loadProps(File masterFile, String extraPropFile, boolean overrideAll) { InputStream is = null; try { if (masterFile != null && masterFile.exists()) { --- /dev/null +++ b/src/java.base/share/classes/java/security/SystemConfigurator.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2019, 2021, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.security; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; + +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Properties; + +import sun.security.util.Debug; + +/** + * Internal class to align OpenJDK with global crypto-policies. + * Called from java.security.Security class initialization, + * during startup. + * + */ + +final class SystemConfigurator { + + private static final Debug sdebug = + Debug.getInstance("properties"); + + private static final String CRYPTO_POLICIES_BASE_DIR = + "/etc/crypto-policies"; + + private static final String CRYPTO_POLICIES_JAVA_CONFIG = + CRYPTO_POLICIES_BASE_DIR + "/back-ends/java.config"; + + private static boolean systemFipsEnabled = false; + private static boolean plainKeySupportEnabled = false; + + private static final String SYSTEMCONF_NATIVE_LIB = "systemconf"; + + private static native boolean getSystemFIPSEnabled() + throws IOException; + + static { + @SuppressWarnings("removal") + var dummy = AccessController.doPrivileged(new PrivilegedAction<Void>() { + public Void run() { + System.loadLibrary(SYSTEMCONF_NATIVE_LIB); + return null; + } + }); + } + + /* + * Invoked when java.security.Security class is initialized, if + * java.security.disableSystemPropertiesFile property is not set and + * security.useSystemPropertiesFile is true. + */ + static boolean configureSysProps(Properties props) { + // now load the system file, if it exists, so its values + // will win if they conflict with the earlier values + return Security.loadProps(null, CRYPTO_POLICIES_JAVA_CONFIG, false); + } + + /* + * Invoked at the end of java.security.Security initialisation + * if java.security properties have been loaded + */ + static boolean configureFIPS(Properties props) { + boolean loadedProps = false; + + try { + if (enableFips()) { + if (sdebug != null) { sdebug.println("FIPS mode detected"); } + // Remove all security providers + Iterator<Entry<Object, Object>> i = props.entrySet().iterator(); + while (i.hasNext()) { + Entry<Object, Object> e = i.next(); + if (((String) e.getKey()).startsWith("security.provider")) { + if (sdebug != null) { sdebug.println("Removing provider: " + e); } + i.remove(); + } + } + // Add FIPS security providers + String fipsProviderValue = null; + for (int n = 1; + (fipsProviderValue = (String) props.get("fips.provider." + n)) != null; n++) { + String fipsProviderKey = "security.provider." + n; + if (sdebug != null) { + sdebug.println("Adding provider " + n + ": " + + fipsProviderKey + "=" + fipsProviderValue); + } + props.put(fipsProviderKey, fipsProviderValue); + } + // Add other security properties + String keystoreTypeValue = (String) props.get("fips.keystore.type"); + if (keystoreTypeValue != null) { + String nonFipsKeystoreType = props.getProperty("keystore.type"); + props.put("keystore.type", keystoreTypeValue); + if (keystoreTypeValue.equals("PKCS11")) { + // If keystore.type is PKCS11, javax.net.ssl.keyStore + // must be "NONE". See JDK-8238264. + System.setProperty("javax.net.ssl.keyStore", "NONE"); + } + if (System.getProperty("javax.net.ssl.trustStoreType") == null) { + // If no trustStoreType has been set, use the + // previous keystore.type under FIPS mode. In + // a default configuration, the Trust Store will + // be 'cacerts' (JKS type). + System.setProperty("javax.net.ssl.trustStoreType", + nonFipsKeystoreType); + } + if (sdebug != null) { + sdebug.println("FIPS mode default keystore.type = " + + keystoreTypeValue); + sdebug.println("FIPS mode javax.net.ssl.keyStore = " + + System.getProperty("javax.net.ssl.keyStore", "")); + sdebug.println("FIPS mode javax.net.ssl.trustStoreType = " + + System.getProperty("javax.net.ssl.trustStoreType", "")); + } + } + loadedProps = true; + systemFipsEnabled = true; + String plainKeySupport = System.getProperty("com.suse.fips.plainKeySupport", + "true"); + plainKeySupportEnabled = !"false".equals(plainKeySupport); + if (sdebug != null) { + if (plainKeySupportEnabled) { + sdebug.println("FIPS support enabled with plain key support"); + } else { + sdebug.println("FIPS support enabled without plain key support"); + } + } + } else { + if (sdebug != null) { sdebug.println("FIPS mode not detected"); } + } + } catch (Exception e) { + if (sdebug != null) { + sdebug.println("unable to load FIPS configuration"); + e.printStackTrace(); + } + } + return loadedProps; + } + + /** + * Returns whether or not global system FIPS alignment is enabled. + * + * Value is always 'false' before java.security.Security class is + * initialized. + * + * Call from out of this package through SharedSecrets: + * SharedSecrets.getJavaSecuritySystemConfiguratorAccess() + * .isSystemFipsEnabled(); + * + * @return a boolean value indicating whether or not global + * system FIPS alignment is enabled. + */ + static boolean isSystemFipsEnabled() { + return systemFipsEnabled; + } + + /** + * Returns {@code true} if system FIPS alignment is enabled + * and plain key support is allowed. Plain key support is + * enabled by default but can be disabled with + * {@code -Dcom.suse.fips.plainKeySupport=false}. + * + * @return a boolean indicating whether plain key support + * should be enabled. + */ + static boolean isPlainKeySupportEnabled() { + return plainKeySupportEnabled; + } + + /** + * Determines whether FIPS mode should be enabled. + * + * OpenJDK FIPS mode will be enabled only if the system is in + * FIPS mode. + * + * Calls to this method only occur if the system property + * com.suse.fips is not set to false. + * + * There are 2 possible ways in which OpenJDK detects that the system + * is in FIPS mode: 1) if the NSS SECMOD_GetSystemFIPSEnabled API is + * available at OpenJDK's built-time, it is called; 2) otherwise, the + * /proc/sys/crypto/fips_enabled file is read. + * + * @return true if the system is in FIPS mode + */ + private static boolean enableFips() throws Exception { + if (sdebug != null) { + sdebug.println("Calling getSystemFIPSEnabled (libsystemconf)..."); + } + try { + boolean fipsEnabled = getSystemFIPSEnabled(); + if (sdebug != null) { + sdebug.println("Call to getSystemFIPSEnabled (libsystemconf) returned: " + + fipsEnabled); + } + return fipsEnabled; + } catch (IOException e) { + if (sdebug != null) { + sdebug.println("Call to getSystemFIPSEnabled (libsystemconf) failed:"); + sdebug.println(e.getMessage()); + } + throw e; + } + } +} --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/access/JavaSecuritySystemConfiguratorAccess.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.access; + +public interface JavaSecuritySystemConfiguratorAccess { + boolean isSystemFipsEnabled(); + boolean isPlainKeySupportEnabled(); +} --- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java @@ -40,6 +40,7 @@ import java.io.FilePermission; import java.io.ObjectInputStream; import java.io.RandomAccessFile; import java.security.ProtectionDomain; +import java.security.Security; import java.security.Signature; /** A repository of "shared secrets", which are a mechanism for @@ -83,6 +84,7 @@ public class SharedSecrets { private static JavaSecuritySpecAccess javaSecuritySpecAccess; private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; private static JavaxCryptoSpecAccess javaxCryptoSpecAccess; + private static JavaSecuritySystemConfiguratorAccess javaSecuritySystemConfiguratorAccess; public static void setJavaUtilCollectionAccess(JavaUtilCollectionAccess juca) { javaUtilCollectionAccess = juca; @@ -457,4 +459,15 @@ public class SharedSecrets { MethodHandles.lookup().ensureInitialized(c); } catch (IllegalAccessException e) {} } + + public static void setJavaSecuritySystemConfiguratorAccess(JavaSecuritySystemConfiguratorAccess jssca) { + javaSecuritySystemConfiguratorAccess = jssca; + } + + public static JavaSecuritySystemConfiguratorAccess getJavaSecuritySystemConfiguratorAccess() { + if (javaSecuritySystemConfiguratorAccess == null) { + ensureClassInitialized(Security.class); + } + return javaSecuritySystemConfiguratorAccess; + } } --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -152,6 +152,8 @@ module java.base { java.naming, java.rmi, jdk.charsets, + jdk.crypto.cryptoki, + jdk.crypto.ec, jdk.jartool, jdk.jlink, jdk.jfr, --- a/src/java.base/share/classes/sun/security/provider/SunEntries.java +++ b/src/java.base/share/classes/sun/security/provider/SunEntries.java @@ -30,6 +30,7 @@ import java.net.*; import java.util.*; import java.security.*; +import jdk.internal.access.SharedSecrets; import jdk.internal.util.StaticProperty; import sun.security.action.GetPropertyAction; import sun.security.util.SecurityProviderConstants; @@ -83,6 +84,10 @@ import static sun.security.util.SecurityProviderConstants.getAliases; public final class SunEntries { + private static final boolean systemFipsEnabled = + SharedSecrets.getJavaSecuritySystemConfiguratorAccess() + .isSystemFipsEnabled(); + // the default algo used by SecureRandom class for new SecureRandom() calls public static final String DEF_SECURE_RANDOM_ALGO; @@ -94,6 +99,7 @@ public final class SunEntries { // common attribute map HashMap<String, String> attrs = new HashMap<>(3); + if (!systemFipsEnabled) { /* * SecureRandom engines */ @@ -177,6 +183,8 @@ public final class SunEntries { "sun.security.provider.DSA$SHA3_384withDSAinP1363Format"); add(p, "Signature", "SHA3-512withDSAinP1363Format", "sun.security.provider.DSA$SHA3_512withDSAinP1363Format"); + } + /* * Key Pair Generator engines */ @@ -184,9 +192,11 @@ public final class SunEntries { attrs.put("ImplementedIn", "Software"); attrs.put("KeySize", "2048"); // for DSA KPG and APG only + if (!systemFipsEnabled) { String dsaKPGImplClass = "sun.security.provider.DSAKeyPairGenerator$"; dsaKPGImplClass += (useLegacyDSA? "Legacy" : "Current"); addWithAlias(p, "KeyPairGenerator", "DSA", dsaKPGImplClass, attrs); + } /* * Algorithm Parameter Generator engines @@ -201,6 +211,7 @@ public final class SunEntries { addWithAlias(p, "AlgorithmParameters", "DSA", "sun.security.provider.DSAParameters", attrs); + if (!systemFipsEnabled) { /* * Key factories */ @@ -235,6 +246,7 @@ public final class SunEntries { "sun.security.provider.SHA3$SHA384", attrs); addWithAlias(p, "MessageDigest", "SHA3-512", "sun.security.provider.SHA3$SHA512", attrs); + } /* * Certificates --- a/src/java.base/share/classes/sun/security/rsa/SunRsaSignEntries.java +++ b/src/java.base/share/classes/sun/security/rsa/SunRsaSignEntries.java @@ -27,6 +27,7 @@ package sun.security.rsa; import java.util.*; import java.security.Provider; +import jdk.internal.access.SharedSecrets; import static sun.security.util.SecurityProviderConstants.getAliases; /** @@ -36,6 +37,10 @@ import static sun.security.util.SecurityProviderConstants.getAliases; */ public final class SunRsaSignEntries { + private static final boolean systemFipsEnabled = + SharedSecrets.getJavaSecuritySystemConfiguratorAccess() + .isSystemFipsEnabled(); + private void add(Provider p, String type, String algo, String cn, List<String> aliases, HashMap<String, String> attrs) { services.add(new Provider.Service(p, type, algo, cn, @@ -63,6 +68,8 @@ public final class SunRsaSignEntries { add(p, "KeyFactory", "RSA", "sun.security.rsa.RSAKeyFactory$Legacy", getAliases("PKCS1"), null); + + if (!systemFipsEnabled) { add(p, "KeyPairGenerator", "RSA", "sun.security.rsa.RSAKeyPairGenerator$Legacy", getAliases("PKCS1"), null); @@ -92,13 +99,18 @@ public final class SunRsaSignEntries { "sun.security.rsa.RSASignature$SHA3_384withRSA", attrs); addA(p, "Signature", "SHA3-512withRSA", "sun.security.rsa.RSASignature$SHA3_512withRSA", attrs); + } addA(p, "KeyFactory", "RSASSA-PSS", "sun.security.rsa.RSAKeyFactory$PSS", attrs); + + if (!systemFipsEnabled) { addA(p, "KeyPairGenerator", "RSASSA-PSS", "sun.security.rsa.RSAKeyPairGenerator$PSS", attrs); addA(p, "Signature", "RSASSA-PSS", "sun.security.rsa.RSAPSSSignature", attrs); + } + addA(p, "AlgorithmParameters", "RSASSA-PSS", "sun.security.rsa.PSSParameters", null); } --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/PBEUtil.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +public final class PBEUtil { + + // Used by SunJCE and SunPKCS11 + public final static class PBES2Helper { + private int iCount; + private byte[] salt; + private IvParameterSpec ivSpec; + private final int defaultSaltLength; + private final int defaultCount; + + public PBES2Helper(int defaultSaltLength, int defaultCount) { + this.defaultSaltLength = defaultSaltLength; + this.defaultCount = defaultCount; + } + + public IvParameterSpec getIvSpec() { + return ivSpec; + } + + public AlgorithmParameters getAlgorithmParameters( + int blkSize, String pbeAlgo, Provider p, SecureRandom random) { + AlgorithmParameters params = null; + if (salt == null) { + // generate random salt and use default iteration count + salt = new byte[defaultSaltLength]; + random.nextBytes(salt); + iCount = defaultCount; + } + if (ivSpec == null) { + // generate random IV + byte[] ivBytes = new byte[blkSize]; + random.nextBytes(ivBytes); + ivSpec = new IvParameterSpec(ivBytes); + } + PBEParameterSpec pbeSpec = new PBEParameterSpec( + salt, iCount, ivSpec); + try { + params = (p == null) ? + AlgorithmParameters.getInstance(pbeAlgo) : + AlgorithmParameters.getInstance(pbeAlgo, p); + params.init(pbeSpec); + } catch (NoSuchAlgorithmException nsae) { + // should never happen + throw new RuntimeException("AlgorithmParameters for " + + pbeAlgo + " not configured"); + } catch (InvalidParameterSpecException ipse) { + // should never happen + throw new RuntimeException("PBEParameterSpec not supported"); + } + return params; + } + + public PBEKeySpec getPBEKeySpec( + int blkSize, int keyLength, int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + if (key == null) { + throw new InvalidKeyException("Null key"); + } + + byte[] passwdBytes = key.getEncoded(); + char[] passwdChars = null; + PBEKeySpec pbeSpec; + try { + if ((passwdBytes == null) || !(key.getAlgorithm().regionMatches( + true, 0, "PBE", 0, 3))) { + throw new InvalidKeyException("Missing password"); + } + + // TBD: consolidate the salt, ic and IV parameter checks below + + // Extract salt and iteration count from the key, if present + if (key instanceof javax.crypto.interfaces.PBEKey) { + salt = ((javax.crypto.interfaces.PBEKey)key).getSalt(); + if (salt != null && salt.length < 8) { + throw new InvalidAlgorithmParameterException( + "Salt must be at least 8 bytes long"); + } + iCount = ((javax.crypto.interfaces.PBEKey)key) + .getIterationCount(); + if (iCount == 0) { + iCount = defaultCount; + } else if (iCount < 0) { + throw new InvalidAlgorithmParameterException( + "Iteration count must be a positive number"); + } + } + + // Extract salt, iteration count and IV from the params, + // if present + if (params == null) { + if (salt == null) { + // generate random salt and use default iteration count + salt = new byte[defaultSaltLength]; + random.nextBytes(salt); + iCount = defaultCount; + } + if ((opmode == Cipher.ENCRYPT_MODE) || + (opmode == Cipher.WRAP_MODE)) { + // generate random IV + byte[] ivBytes = new byte[blkSize]; + random.nextBytes(ivBytes); + ivSpec = new IvParameterSpec(ivBytes); + } + } else { + if (!(params instanceof PBEParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("Wrong parameter type: PBE expected"); + } + // salt and iteration count from the params take precedence + byte[] specSalt = ((PBEParameterSpec) params).getSalt(); + if (specSalt != null && specSalt.length < 8) { + throw new InvalidAlgorithmParameterException( + "Salt must be at least 8 bytes long"); + } + salt = specSalt; + int specICount = ((PBEParameterSpec) params) + .getIterationCount(); + if (specICount == 0) { + specICount = defaultCount; + } else if (specICount < 0) { + throw new InvalidAlgorithmParameterException( + "Iteration count must be a positive number"); + } + iCount = specICount; + + AlgorithmParameterSpec specParams = + ((PBEParameterSpec) params).getParameterSpec(); + if (specParams != null) { + if (specParams instanceof IvParameterSpec) { + ivSpec = (IvParameterSpec)specParams; + } else { + throw new InvalidAlgorithmParameterException( + "Wrong parameter type: IV expected"); + } + } else if ((opmode == Cipher.ENCRYPT_MODE) || + (opmode == Cipher.WRAP_MODE)) { + // generate random IV + byte[] ivBytes = new byte[blkSize]; + random.nextBytes(ivBytes); + ivSpec = new IvParameterSpec(ivBytes); + } else { + throw new InvalidAlgorithmParameterException( + "Missing parameter type: IV expected"); + } + } + + passwdChars = new char[passwdBytes.length]; + for (int i = 0; i < passwdChars.length; i++) + passwdChars[i] = (char) (passwdBytes[i] & 0x7f); + + pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength); + // password char[] was cloned in PBEKeySpec constructor, + // so we can zero it out here + } finally { + if (passwdChars != null) Arrays.fill(passwdChars, '\0'); + if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00); + } + return pbeSpec; + } + + public static AlgorithmParameterSpec getParameterSpec( + AlgorithmParameters params) + throws InvalidAlgorithmParameterException { + AlgorithmParameterSpec pbeSpec = null; + if (params != null) { + try { + pbeSpec = params.getParameterSpec(PBEParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + throw new InvalidAlgorithmParameterException( + "Wrong parameter type: PBE expected"); + } + } + return pbeSpec; + } + } + + // Used by SunJCE and SunPKCS11 + public static PBEKeySpec getPBAKeySpec(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + char[] passwdChars; + byte[] salt = null; + int iCount = 0; + if (key instanceof javax.crypto.interfaces.PBEKey) { + javax.crypto.interfaces.PBEKey pbeKey = + (javax.crypto.interfaces.PBEKey) key; + passwdChars = pbeKey.getPassword(); + salt = pbeKey.getSalt(); // maybe null if unspecified + iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified + } else if (key instanceof SecretKey) { + byte[] passwdBytes; + if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) || + (passwdBytes = key.getEncoded()) == null) { + throw new InvalidKeyException("Missing password"); + } + passwdChars = new char[passwdBytes.length]; + for (int i=0; i<passwdChars.length; i++) { + passwdChars[i] = (char) (passwdBytes[i] & 0x7f); + } + Arrays.fill(passwdBytes, (byte)0x00); + } else { + throw new InvalidKeyException("SecretKey of PBE type required"); + } + + try { + if (params == null) { + // should not auto-generate default values since current + // javax.crypto.Mac api does not have any method for caller to + // retrieve the generated defaults. + if ((salt == null) || (iCount == 0)) { + throw new InvalidAlgorithmParameterException + ("PBEParameterSpec required for salt and iteration count"); + } + } else if (!(params instanceof PBEParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("PBEParameterSpec type required"); + } else { + PBEParameterSpec pbeParams = (PBEParameterSpec) params; + // make sure the parameter values are consistent + if (salt != null) { + if (!Arrays.equals(salt, pbeParams.getSalt())) { + throw new InvalidAlgorithmParameterException + ("Inconsistent value of salt between key and params"); + } + } else { + salt = pbeParams.getSalt(); + } + if (iCount != 0) { + if (iCount != pbeParams.getIterationCount()) { + throw new InvalidAlgorithmParameterException + ("Different iteration count between key and params"); + } + } else { + iCount = pbeParams.getIterationCount(); + } + } + // For security purpose, we need to enforce a minimum length + // for salt; just require the minimum salt length to be 8-byte + // which is what PKCS#5 recommends and openssl does. + if (salt.length < 8) { + throw new InvalidAlgorithmParameterException + ("Salt must be at least 8 bytes long"); + } + if (iCount <= 0) { + throw new InvalidAlgorithmParameterException + ("IterationCount must be a positive number"); + } + return new PBEKeySpec(passwdChars, salt, iCount); + } finally { + Arrays.fill(passwdChars, '\0'); + } + } +} --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -82,6 +82,17 @@ security.provider.tbd=Apple #endif security.provider.tbd=SunPKCS11 +# +# Security providers used when FIPS mode support is active +# +fips.provider.1=SunPKCS11 ${java.home}/conf/security/nss.fips.cfg +fips.provider.2=SUN +fips.provider.3=SunEC +fips.provider.4=SunJSSE +fips.provider.5=SunJCE +fips.provider.6=SunRsaSign +fips.provider.7=XMLDSig + # # A list of preferred providers for specific algorithms. These providers will # be searched for matching algorithms before the list of registered providers. @@ -292,6 +303,47 @@ policy.ignoreIdentityScope=false # keystore.type=pkcs12 +# +# Default keystore type used when global crypto-policies are set to FIPS. +# +fips.keystore.type=pkcs12 + +# +# Location of the NSS DB keystore (PKCS11) in FIPS mode. +# +# The syntax for this property is identical to the 'nssSecmodDirectory' +# attribute available in the SunPKCS11 NSS configuration file. Use the +# 'sql:' prefix to refer to an SQLite DB. +# +# If the system property fips.nssdb.path is also specified, it supersedes +# the security property value defined here. +# +# Note: the default value for this property points to an NSS DB that might be +# readable by multiple operating system users and unsuitable to store keys. +# +fips.nssdb.path=sql:/etc/pki/nssdb + +# +# PIN for the NSS DB keystore (PKCS11) in FIPS mode. +# +# Values must take any of the following forms: +# 1) pin:<value> +# Value: clear text PIN value. +# 2) env:<value> +# Value: environment variable containing the PIN value. +# 3) file:<value> +# Value: path to a file containing the PIN value in its first +# line. +# +# If the system property fips.nssdb.pin is also specified, it supersedes +# the security property value defined here. +# +# When used as a system property, UTF-8 encoded values are valid. When +# used as a security property (such as in this file), encode non-Basic +# Latin Unicode characters with \uXXXX. +# +fips.nssdb.pin=pin: + # # Controls compatibility mode for JKS and PKCS12 keystore types. # @@ -329,6 +381,13 @@ package.definition=sun.misc.,\ # security.overridePropertiesFile=true +# +# Determines whether this properties file will be appended to +# using the system properties file stored at +# /etc/crypto-policies/back-ends/java.config +# +security.useSystemPropertiesFile=true + # # Determines the default key and trust manager factory algorithms for # the javax.net.ssl package. --- /dev/null +++ b/src/java.base/share/conf/security/nss.fips.cfg.in @@ -0,0 +1,8 @@ +name = NSS-FIPS +nssLibraryDirectory = @NSS_LIBDIR@ +nssSecmodDirectory = ${fips.nssdb.path} +nssDbMode = readWrite +nssModule = fips + +attributes(*,CKO_SECRET_KEY,CKK_GENERIC_SECRET)={ CKA_SIGN=true } + --- a/src/java.base/share/lib/security/default.policy +++ b/src/java.base/share/lib/security/default.policy @@ -123,6 +123,7 @@ grant codeBase "jrt:/jdk.charsets" { grant codeBase "jrt:/jdk.crypto.ec" { permission java.lang.RuntimePermission "accessClassInPackage.sun.security.*"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.access"; permission java.lang.RuntimePermission "loadLibrary.sunec"; permission java.security.SecurityPermission "putProviderProperty.SunEC"; permission java.security.SecurityPermission "clearProviderProperties.SunEC"; @@ -132,6 +133,7 @@ grant codeBase "jrt:/jdk.crypto.ec" { grant codeBase "jrt:/jdk.crypto.cryptoki" { permission java.lang.RuntimePermission "accessClassInPackage.com.sun.crypto.provider"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.access"; permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; permission java.lang.RuntimePermission "accessClassInPackage.sun.security.*"; @@ -142,6 +144,8 @@ grant codeBase "jrt:/jdk.crypto.cryptoki" { permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "jdk.crypto.KeyAgreement.legacyKDF", "read"; + permission java.util.PropertyPermission "fips.nssdb.path", "read,write"; + permission java.util.PropertyPermission "fips.nssdb.pin", "read"; permission java.security.SecurityPermission "putProviderProperty.*"; permission java.security.SecurityPermission "clearProviderProperties.*"; permission java.security.SecurityPermission "removeProviderProperty.*"; --- /dev/null +++ b/src/java.base/share/native/libsystemconf/systemconf.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2021, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include <jni.h> +#include <jni_util.h> +#include "jvm_md.h" +#include <stdio.h> + +#ifdef LINUX + +#ifdef SYSCONF_NSS +#include <nss3/pk11pub.h> +#else +#include <dlfcn.h> +#endif //SYSCONF_NSS + +#include "java_security_SystemConfigurator.h" + +#define MSG_MAX_SIZE 256 +#define FIPS_ENABLED_PATH "/proc/sys/crypto/fips_enabled" + +typedef int (SECMOD_GET_SYSTEM_FIPS_ENABLED_TYPE)(void); + +static SECMOD_GET_SYSTEM_FIPS_ENABLED_TYPE *getSystemFIPSEnabled; +static jmethodID debugPrintlnMethodID = NULL; +static jobject debugObj = NULL; + +static void dbgPrint(JNIEnv *env, const char* msg) +{ + jstring jMsg; + if (debugObj != NULL) { + jMsg = (*env)->NewStringUTF(env, msg); + CHECK_NULL(jMsg); + (*env)->CallVoidMethod(env, debugObj, debugPrintlnMethodID, jMsg); + } +} + +static void throwIOException(JNIEnv *env, const char *msg) +{ + jclass cls = (*env)->FindClass(env, "java/io/IOException"); + if (cls != 0) + (*env)->ThrowNew(env, cls, msg); +} + +static void handle_msg(JNIEnv *env, const char* msg, int msg_bytes) +{ + if (msg_bytes > 0 && msg_bytes < MSG_MAX_SIZE) { + dbgPrint(env, msg); + } else { + dbgPrint(env, "systemconf: cannot render message"); + } +} + +// Only used when NSS is not linked at build time +#ifndef SYSCONF_NSS + +static void *nss_handle; + +static jboolean loadNSS(JNIEnv *env) +{ + char msg[MSG_MAX_SIZE]; + int msg_bytes; + const char* errmsg; + + nss_handle = dlopen(JNI_LIB_NAME("nss3"), RTLD_LAZY); + if (nss_handle == NULL) { + errmsg = dlerror(); + msg_bytes = snprintf(msg, MSG_MAX_SIZE, "loadNSS: dlopen: %s\n", + errmsg); + handle_msg(env, msg, msg_bytes); + return JNI_FALSE; + } + dlerror(); /* Clear errors */ + getSystemFIPSEnabled = (SECMOD_GET_SYSTEM_FIPS_ENABLED_TYPE*)dlsym(nss_handle, "SECMOD_GetSystemFIPSEnabled"); + if ((errmsg = dlerror()) != NULL) { + msg_bytes = snprintf(msg, MSG_MAX_SIZE, "loadNSS: dlsym: %s\n", + errmsg); + handle_msg(env, msg, msg_bytes); + return JNI_FALSE; + } + return JNI_TRUE; +} + +static void closeNSS(JNIEnv *env) +{ + char msg[MSG_MAX_SIZE]; + int msg_bytes; + const char* errmsg; + + if (dlclose(nss_handle) != 0) { + errmsg = dlerror(); + msg_bytes = snprintf(msg, MSG_MAX_SIZE, "closeNSS: dlclose: %s\n", + errmsg); + handle_msg(env, msg, msg_bytes); + } +} + +#endif + +/* + * Class: java_security_SystemConfigurator + * Method: JNI_OnLoad + */ +JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) +{ + JNIEnv *env; + jclass sysConfCls, debugCls; + jfieldID sdebugFld; + + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) { + return JNI_EVERSION; /* JNI version not supported */ + } + + sysConfCls = (*env)->FindClass(env,"java/security/SystemConfigurator"); + if (sysConfCls == NULL) { + printf("libsystemconf: SystemConfigurator class not found\n"); + return JNI_ERR; + } + sdebugFld = (*env)->GetStaticFieldID(env, sysConfCls, + "sdebug", "Lsun/security/util/Debug;"); + if (sdebugFld == NULL) { + printf("libsystemconf: SystemConfigurator::sdebug field not found\n"); + return JNI_ERR; + } + debugObj = (*env)->GetStaticObjectField(env, sysConfCls, sdebugFld); + if (debugObj != NULL) { + debugCls = (*env)->FindClass(env,"sun/security/util/Debug"); + if (debugCls == NULL) { + printf("libsystemconf: Debug class not found\n"); + return JNI_ERR; + } + debugPrintlnMethodID = (*env)->GetMethodID(env, debugCls, + "println", "(Ljava/lang/String;)V"); + if (debugPrintlnMethodID == NULL) { + printf("libsystemconf: Debug::println(String) method not found\n"); + return JNI_ERR; + } + debugObj = (*env)->NewGlobalRef(env, debugObj); + } + +#ifdef SYSCONF_NSS + getSystemFIPSEnabled = *SECMOD_GetSystemFIPSEnabled; +#else + if (loadNSS(env) == JNI_FALSE) { + dbgPrint(env, "libsystemconf: Failed to load NSS library."); + } +#endif + + return (*env)->GetVersion(env); +} + +/* + * Class: java_security_SystemConfigurator + * Method: JNI_OnUnload + */ +JNIEXPORT void JNICALL DEF_JNI_OnUnload(JavaVM *vm, void *reserved) +{ + JNIEnv *env; + + if (debugObj != NULL) { + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) { + return; /* Should not happen */ + } +#ifndef SYSCONF_NSS + closeNSS(env); +#endif + (*env)->DeleteGlobalRef(env, debugObj); + } +} + +JNIEXPORT jboolean JNICALL Java_java_security_SystemConfigurator_getSystemFIPSEnabled + (JNIEnv *env, jclass cls) +{ + int fips_enabled; + char msg[MSG_MAX_SIZE]; + int msg_bytes; + + if (getSystemFIPSEnabled != NULL) { + dbgPrint(env, "getSystemFIPSEnabled: calling SECMOD_GetSystemFIPSEnabled"); + fips_enabled = (*getSystemFIPSEnabled)(); + msg_bytes = snprintf(msg, MSG_MAX_SIZE, "getSystemFIPSEnabled:" \ + " SECMOD_GetSystemFIPSEnabled returned 0x%x", fips_enabled); + handle_msg(env, msg, msg_bytes); + return (fips_enabled == 1 ? JNI_TRUE : JNI_FALSE); + } else { + FILE *fe; + + dbgPrint(env, "getSystemFIPSEnabled: reading " FIPS_ENABLED_PATH); + if ((fe = fopen(FIPS_ENABLED_PATH, "r")) == NULL) { + throwIOException(env, "Cannot open " FIPS_ENABLED_PATH); + return JNI_FALSE; + } + fips_enabled = fgetc(fe); + fclose(fe); + if (fips_enabled == EOF) { + throwIOException(env, "Cannot read " FIPS_ENABLED_PATH); + return JNI_FALSE; + } + msg_bytes = snprintf(msg, MSG_MAX_SIZE, "getSystemFIPSEnabled:" \ + " read character is '%c'", fips_enabled); + handle_msg(env, msg, msg_bytes); + return (fips_enabled == '1' ? JNI_TRUE : JNI_FALSE); + } +} + +#else // !LINUX + +JNIEXPORT jboolean JNICALL Java_java_security_SystemConfigurator_getSystemFIPSEnabled + (JNIEnv *env, jclass cls) +{ + return JNI_FALSE; +} + +#endif --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/FIPSKeyImporter.java @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2021, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.Provider; +import java.security.Security; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.spec.IvParameterSpec; + +import sun.security.jca.JCAUtil; +import sun.security.pkcs11.TemplateManager; +import sun.security.pkcs11.wrapper.CK_ATTRIBUTE; +import sun.security.pkcs11.wrapper.CK_MECHANISM; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import static sun.security.pkcs11.wrapper.PKCS11Exception.*; +import sun.security.pkcs11.wrapper.PKCS11Exception; +import sun.security.rsa.RSAPrivateCrtKeyImpl; +import sun.security.rsa.RSAUtil; +import sun.security.rsa.RSAUtil.KeyType; +import sun.security.util.Debug; +import sun.security.util.ECUtil; + +final class FIPSKeyImporter { + + private static final Debug debug = + Debug.getInstance("sunpkcs11"); + + private static volatile P11Key importerKey = null; + private static SecretKeySpec exporterKey = null; + private static volatile P11Key exporterKeyP11 = null; + private static final ReentrantLock importerKeyLock = new ReentrantLock(); + // Do not take the exporterKeyLock with the importerKeyLock held. + private static final ReentrantLock exporterKeyLock = new ReentrantLock(); + private static volatile CK_MECHANISM importerKeyMechanism = null; + private static volatile CK_MECHANISM exporterKeyMechanism = null; + private static Cipher importerCipher = null; + private static Cipher exporterCipher = null; + + private static volatile Provider sunECProvider = null; + private static final ReentrantLock sunECProviderLock = new ReentrantLock(); + + static Long importKey(SunPKCS11 sunPKCS11, long hSession, CK_ATTRIBUTE[] attributes) + throws PKCS11Exception { + long keyID = -1; + Token token = sunPKCS11.getToken(); + if (debug != null) { + debug.println("Private or Secret key will be imported in" + + " system FIPS mode."); + } + if (importerKey == null) { + importerKeyLock.lock(); + try { + if (importerKey == null) { + if (importerKeyMechanism == null) { + // Importer Key creation has not been tried yet. Try it. + createImporterKey(token); + } + if (importerKey == null || importerCipher == null) { + if (debug != null) { + debug.println("Importer Key could not be" + + " generated."); + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " fips key importer"); + } + if (debug != null) { + debug.println("Importer Key successfully" + + " generated."); + } + } + } finally { + importerKeyLock.unlock(); + } + } + long importerKeyID = importerKey.getKeyID(); + try { + byte[] keyBytes = null; + byte[] encKeyBytes = null; + long keyClass = 0L; + long keyType = 0L; + Map<Long, CK_ATTRIBUTE> attrsMap = new HashMap<>(); + for (CK_ATTRIBUTE attr : attributes) { + if (attr.type == CKA_CLASS) { + keyClass = attr.getLong(); + } else if (attr.type == CKA_KEY_TYPE) { + keyType = attr.getLong(); + } + attrsMap.put(attr.type, attr); + } + BigInteger v = null; + if (keyClass == CKO_PRIVATE_KEY) { + if (keyType == CKK_RSA) { + if (debug != null) { + debug.println("Importing an RSA private key..."); + } + keyBytes = sun.security.rsa.RSAPrivateCrtKeyImpl.newKey( + KeyType.RSA, + null, + ((v = attrsMap.get(CKA_MODULUS).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_PUBLIC_EXPONENT).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_PRIVATE_EXPONENT).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_PRIME_1).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_PRIME_2).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_EXPONENT_1).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_EXPONENT_2).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_COEFFICIENT).getBigInteger()) != null) + ? v : BigInteger.ZERO + ).getEncoded(); + } else if (keyType == CKK_DSA) { + if (debug != null) { + debug.println("Importing a DSA private key..."); + } + keyBytes = new sun.security.provider.DSAPrivateKey( + ((v = attrsMap.get(CKA_VALUE).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_PRIME).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_SUBPRIME).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ((v = attrsMap.get(CKA_BASE).getBigInteger()) != null) + ? v : BigInteger.ZERO + ).getEncoded(); + if (token.config.getNssNetscapeDbWorkaround() && + attrsMap.get(CKA_NETSCAPE_DB) == null) { + attrsMap.put(CKA_NETSCAPE_DB, + new CK_ATTRIBUTE(CKA_NETSCAPE_DB, BigInteger.ZERO)); + } + } else if (keyType == CKK_EC) { + if (debug != null) { + debug.println("Importing an EC private key..."); + } + if (sunECProvider == null) { + sunECProviderLock.lock(); + try { + if (sunECProvider == null) { + sunECProvider = Security.getProvider("SunEC"); + } + } finally { + sunECProviderLock.unlock(); + } + } + keyBytes = ECUtil.generateECPrivateKey( + ((v = attrsMap.get(CKA_VALUE).getBigInteger()) != null) + ? v : BigInteger.ZERO, + ECUtil.getECParameterSpec(sunECProvider, + attrsMap.get(CKA_EC_PARAMS).getByteArray())) + .getEncoded(); + if (token.config.getNssNetscapeDbWorkaround() && + attrsMap.get(CKA_NETSCAPE_DB) == null) { + attrsMap.put(CKA_NETSCAPE_DB, + new CK_ATTRIBUTE(CKA_NETSCAPE_DB, BigInteger.ZERO)); + } + } else { + if (debug != null) { + debug.println("Unrecognized private key type."); + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " fips key importer"); + } + } else if (keyClass == CKO_SECRET_KEY) { + if (debug != null) { + debug.println("Importing a secret key..."); + } + keyBytes = attrsMap.get(CKA_VALUE).getByteArray(); + } + if (keyBytes == null || keyBytes.length == 0) { + if (debug != null) { + debug.println("Private or secret key plain bytes could" + + " not be obtained. Import failed."); + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " fips key importer"); + } + attributes = new CK_ATTRIBUTE[attrsMap.size()]; + attrsMap.values().toArray(attributes); + importerKeyLock.lock(); + try { + // No need to reset the cipher object because no multi-part + // operations are performed. + encKeyBytes = importerCipher.doFinal(keyBytes); + } finally { + importerKeyLock.unlock(); + } + attributes = token.getAttributes(TemplateManager.O_IMPORT, + keyClass, keyType, attributes); + keyID = token.p11.C_UnwrapKey(hSession, + importerKeyMechanism, importerKeyID, encKeyBytes, attributes); + if (debug != null) { + debug.println("Imported key ID: " + keyID); + } + } catch (Throwable t) { + if (t instanceof PKCS11Exception) { + throw (PKCS11Exception)t; + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + t.getMessage()); + } finally { + importerKey.releaseKeyID(); + } + return Long.valueOf(keyID); + } + + static void exportKey(SunPKCS11 sunPKCS11, long hSession, long hObject, + long keyClass, long keyType, Map<Long, CK_ATTRIBUTE> sensitiveAttrs) + throws PKCS11Exception { + Token token = sunPKCS11.getToken(); + if (debug != null) { + debug.println("Private or Secret key will be exported in" + + " system FIPS mode."); + } + if (exporterKeyP11 == null) { + try { + exporterKeyLock.lock(); + if (exporterKeyP11 == null) { + if (exporterKeyMechanism == null) { + // Exporter Key creation has not been tried yet. Try it. + createExporterKey(token); + } + if (exporterKeyP11 == null || exporterCipher == null) { + if (debug != null) { + debug.println("Exporter Key could not be" + + " generated."); + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " fips key exporter"); + } + if (debug != null) { + debug.println("Exporter Key successfully" + + " generated."); + } + } + } finally { + exporterKeyLock.unlock(); + } + } + long exporterKeyID = exporterKeyP11.getKeyID(); + try { + byte[] wrappedKeyBytes = token.p11.C_WrapKey(hSession, + exporterKeyMechanism, exporterKeyID, hObject); + byte[] plainExportedKey = null; + exporterKeyLock.lock(); + try { + // No need to reset the cipher object because no multi-part + // operations are performed. + plainExportedKey = exporterCipher.doFinal(wrappedKeyBytes); + } finally { + exporterKeyLock.unlock(); + } + if (keyClass == CKO_PRIVATE_KEY) { + exportPrivateKey(sensitiveAttrs, keyType, plainExportedKey); + } else if (keyClass == CKO_SECRET_KEY) { + checkAttrs(sensitiveAttrs, "CKO_SECRET_KEY", CKA_VALUE); + // CKA_VALUE is guaranteed to be present, since sensitiveAttrs' + // size is greater than 0 and no invalid attributes exist + sensitiveAttrs.get(CKA_VALUE).pValue = plainExportedKey; + } else { + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " fips key exporter"); + } + } catch (Throwable t) { + if (t instanceof PKCS11Exception) { + throw (PKCS11Exception)t; + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + t.getMessage()); + } finally { + exporterKeyP11.releaseKeyID(); + } + } + + private static void exportPrivateKey( + Map<Long, CK_ATTRIBUTE> sensitiveAttrs, long keyType, + byte[] plainExportedKey) throws Throwable { + if (keyType == CKK_RSA) { + checkAttrs(sensitiveAttrs, "CKO_PRIVATE_KEY CKK_RSA", + CKA_PRIVATE_EXPONENT, CKA_PRIME_1, CKA_PRIME_2, + CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT); + RSAPrivateKey rsaPKey = RSAPrivateCrtKeyImpl.newKey( + RSAUtil.KeyType.RSA, "PKCS#8", plainExportedKey); + CK_ATTRIBUTE attr; + if ((attr = sensitiveAttrs.get(CKA_PRIVATE_EXPONENT)) != null) { + attr.pValue = rsaPKey.getPrivateExponent().toByteArray(); + } + if (rsaPKey instanceof RSAPrivateCrtKey) { + RSAPrivateCrtKey rsaPCrtKey = (RSAPrivateCrtKey) rsaPKey; + if ((attr = sensitiveAttrs.get(CKA_PRIME_1)) != null) { + attr.pValue = rsaPCrtKey.getPrimeP().toByteArray(); + } + if ((attr = sensitiveAttrs.get(CKA_PRIME_2)) != null) { + attr.pValue = rsaPCrtKey.getPrimeQ().toByteArray(); + } + if ((attr = sensitiveAttrs.get(CKA_EXPONENT_1)) != null) { + attr.pValue = rsaPCrtKey.getPrimeExponentP().toByteArray(); + } + if ((attr = sensitiveAttrs.get(CKA_EXPONENT_2)) != null) { + attr.pValue = rsaPCrtKey.getPrimeExponentQ().toByteArray(); + } + if ((attr = sensitiveAttrs.get(CKA_COEFFICIENT)) != null) { + attr.pValue = rsaPCrtKey.getCrtCoefficient().toByteArray(); + } + } else { + checkAttrs(sensitiveAttrs, "CKO_PRIVATE_KEY CKK_RSA", + CKA_PRIVATE_EXPONENT); + } + } else if (keyType == CKK_DSA) { + checkAttrs(sensitiveAttrs, "CKO_PRIVATE_KEY CKK_DSA", CKA_VALUE); + // CKA_VALUE is guaranteed to be present, since sensitiveAttrs' + // size is greater than 0 and no invalid attributes exist + sensitiveAttrs.get(CKA_VALUE).pValue = + new sun.security.provider.DSAPrivateKey(plainExportedKey) + .getX().toByteArray(); + } else if (keyType == CKK_EC) { + checkAttrs(sensitiveAttrs, "CKO_PRIVATE_KEY CKK_EC", CKA_VALUE); + // CKA_VALUE is guaranteed to be present, since sensitiveAttrs' + // size is greater than 0 and no invalid attributes exist + sensitiveAttrs.get(CKA_VALUE).pValue = + ECUtil.decodePKCS8ECPrivateKey(plainExportedKey) + .getS().toByteArray(); + } else { + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " unsupported CKO_PRIVATE_KEY key type: " + keyType); + } + } + + private static void checkAttrs(Map<Long, CK_ATTRIBUTE> sensitiveAttrs, + String keyName, long... validAttrs) + throws PKCS11Exception { + int sensitiveAttrsCount = sensitiveAttrs.size(); + if (sensitiveAttrsCount <= validAttrs.length) { + int validAttrsCount = 0; + for (long validAttr : validAttrs) { + if (sensitiveAttrs.containsKey(validAttr)) validAttrsCount++; + } + if (validAttrsCount == sensitiveAttrsCount) return; + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + " invalid attribute types for a " + keyName + " key object"); + } + + private static void createImporterKey(Token token) { + if (debug != null) { + debug.println("Generating Importer Key..."); + } + byte[] iv = new byte[16]; + JCAUtil.getSecureRandom().nextBytes(iv); + importerKeyMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, iv); + try { + CK_ATTRIBUTE[] attributes = token.getAttributes(TemplateManager.O_GENERATE, + CKO_SECRET_KEY, CKK_AES, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_VALUE_LEN, 256 >> 3)}); + Session s = null; + try { + s = token.getObjSession(); + long keyID = token.p11.C_GenerateKey( + s.id(), new CK_MECHANISM(CKM_AES_KEY_GEN), + attributes); + if (debug != null) { + debug.println("Importer Key ID: " + keyID); + } + importerKey = (P11Key)P11Key.secretKey(s, keyID, "AES", + 256 >> 3, null); + } catch (PKCS11Exception e) { + // best effort + } finally { + token.releaseSession(s); + } + if (importerKey != null) { + importerCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + importerCipher.init(Cipher.ENCRYPT_MODE, importerKey, + new IvParameterSpec( + (byte[])importerKeyMechanism.pParameter), null); + } + } catch (Throwable t) { + // best effort + importerKey = null; + importerCipher = null; + // importerKeyMechanism value is kept initialized to indicate that + // Importer Key creation has been tried and failed. + if (debug != null) { + debug.println("Error generating the Importer Key"); + } + } + } + + private static void createExporterKey(Token token) { + if (debug != null) { + debug.println("Generating Exporter Key..."); + } + byte[] iv = new byte[16]; + JCAUtil.getSecureRandom().nextBytes(iv); + exporterKeyMechanism = new CK_MECHANISM(CKM_AES_CBC_PAD, iv); + byte[] exporterKeyRaw = new byte[32]; + JCAUtil.getSecureRandom().nextBytes(exporterKeyRaw); + exporterKey = new SecretKeySpec(exporterKeyRaw, "AES"); + try { + SecretKeyFactory skf = SecretKeyFactory.getInstance("AES"); + exporterKeyP11 = (P11Key)(skf.translateKey(exporterKey)); + if (exporterKeyP11 != null) { + exporterCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + exporterCipher.init(Cipher.DECRYPT_MODE, exporterKey, + new IvParameterSpec( + (byte[])exporterKeyMechanism.pParameter), null); + } + } catch (Throwable t) { + // best effort + exporterKey = null; + exporterKeyP11 = null; + exporterCipher = null; + // exporterKeyMechanism value is kept initialized to indicate that + // Exporter Key creation has been tried and failed. + if (debug != null) { + debug.println("Error generating the Exporter Key"); + } + } + } +} --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/FIPSTokenLoginHandler.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.security.ProviderException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import sun.security.util.Debug; +import sun.security.util.SecurityProperties; + +final class FIPSTokenLoginHandler implements CallbackHandler { + + private static final String FIPS_NSSDB_PIN_PROP = "fips.nssdb.pin"; + + private static final Debug debug = Debug.getInstance("sunpkcs11"); + + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + if (!(callbacks[0] instanceof PasswordCallback)) { + throw new UnsupportedCallbackException(callbacks[0]); + } + PasswordCallback pc = (PasswordCallback)callbacks[0]; + pc.setPassword(getFipsNssdbPin()); + } + + private static char[] getFipsNssdbPin() throws ProviderException { + if (debug != null) { + debug.println("FIPS: Reading NSS DB PIN for token..."); + } + String pinProp = SecurityProperties + .privilegedGetOverridable(FIPS_NSSDB_PIN_PROP); + if (pinProp != null && !pinProp.isEmpty()) { + String[] pinPropParts = pinProp.split(":", 2); + if (pinPropParts.length < 2) { + throw new ProviderException("Invalid " + FIPS_NSSDB_PIN_PROP + + " property value."); + } + String prefix = pinPropParts[0].toLowerCase(); + String value = pinPropParts[1]; + String pin = null; + if (prefix.equals("env")) { + if (debug != null) { + debug.println("FIPS: PIN value from the '" + value + + "' environment variable."); + } + pin = System.getenv(value); + } else if (prefix.equals("file")) { + if (debug != null) { + debug.println("FIPS: PIN value from the '" + value + + "' file."); + } + pin = getPinFromFile(Paths.get(value)); + } else if (prefix.equals("pin")) { + if (debug != null) { + debug.println("FIPS: PIN value from the " + + FIPS_NSSDB_PIN_PROP + " property."); + } + pin = value; + } else { + throw new ProviderException("Unsupported prefix for " + + FIPS_NSSDB_PIN_PROP + "."); + } + if (pin != null && !pin.isEmpty()) { + if (debug != null) { + debug.println("FIPS: non-empty PIN."); + } + /* + * C_Login in libj2pkcs11 receives the PIN in a char[] and + * discards the upper byte of each char, before passing + * the value to the NSS Software Token. However, the + * NSS Software Token accepts any UTF-8 PIN value. Thus, + * expand the PIN here to account for later truncation. + */ + byte[] pinUtf8 = pin.getBytes(StandardCharsets.UTF_8); + char[] pinChar = new char[pinUtf8.length]; + for (int i = 0; i < pinChar.length; i++) { + pinChar[i] = (char)(pinUtf8[i] & 0xFF); + } + return pinChar; + } + } + if (debug != null) { + debug.println("FIPS: empty PIN."); + } + return null; + } + + /* + * This method extracts the token PIN from the first line of a password + * file in the same way as NSS modutil. See for example the -newpwfile + * argument used to change the password for an NSS DB. + */ + private static String getPinFromFile(Path f) throws ProviderException { + try (InputStream is = + Files.newInputStream(f, StandardOpenOption.READ)) { + /* + * SECU_FilePasswd in NSS (nss/cmd/lib/secutil.c), used by modutil, + * reads up to 4096 bytes. In addition, the NSS Software Token + * does not accept PINs longer than 500 bytes (see SFTK_MAX_PIN + * in nss/lib/softoken/pkcs11i.h). + */ + BufferedReader in = + new BufferedReader(new InputStreamReader( + new ByteArrayInputStream(is.readNBytes(4096)), + StandardCharsets.UTF_8)); + return in.readLine(); + } catch (IOException ioe) { + throw new ProviderException("Error reading " + FIPS_NSSDB_PIN_PROP + + " from the '" + f + "' file.", ioe); + } + } +} \ No newline at end of file --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -37,6 +37,8 @@ import javax.crypto.*; import javax.crypto.interfaces.*; import javax.crypto.spec.*; +import jdk.internal.access.SharedSecrets; + import sun.security.rsa.RSAUtil.KeyType; import sun.security.rsa.RSAPublicKeyImpl; import sun.security.rsa.RSAPrivateCrtKeyImpl; @@ -69,6 +71,9 @@ import sun.security.jca.JCAUtil; */ abstract class P11Key implements Key, Length { + private static final boolean plainKeySupportEnabled = SharedSecrets + .getJavaSecuritySystemConfiguratorAccess().isPlainKeySupportEnabled(); + private static final long serialVersionUID = -2575874101938349339L; private static final String PUBLIC = "public"; @@ -393,9 +398,10 @@ abstract class P11Key implements Key, Length { new CK_ATTRIBUTE(CKA_EXTRACTABLE), }); - boolean keySensitive = - (attrs[0].getBoolean() && P11Util.isNSS(session.token)) || - attrs[1].getBoolean() || !attrs[2].getBoolean(); + boolean exportable = plainKeySupportEnabled && !algorithm.equals("DH"); + boolean keySensitive = (!exportable && + ((attrs[0].getBoolean() && P11Util.isNSS(session.token)) || + attrs[1].getBoolean() || !attrs[2].getBoolean())); switch (algorithm) { case "RSA": @@ -450,7 +456,8 @@ abstract class P11Key implements Key, Length { public String getFormat() { token.ensureValid(); - if (sensitive || !extractable || (isNSS && tokenObject)) { + if (!plainKeySupportEnabled && + (sensitive || !extractable || (isNSS && tokenObject))) { return null; } else { return "RAW"; --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java @@ -29,14 +29,17 @@ import java.nio.ByteBuffer; import java.security.*; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; import javax.crypto.MacSpi; +import javax.crypto.spec.PBEKeySpec; import sun.nio.ch.DirectBuffer; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import static sun.security.pkcs11.wrapper.PKCS11Exception.*; +import sun.security.util.PBEUtil; /** * MAC implementation class. This class currently supports HMAC using @@ -202,12 +205,23 @@ final class P11Mac extends MacSpi { // see JCE spec protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (algorithm.startsWith("HmacPBE")) { + PBEKeySpec pbeSpec = PBEUtil.getPBAKeySpec(key, params); + reset(true); + try { + p11Key = P11SecretKeyFactory.derivePBEKey( + token, pbeSpec, algorithm); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } else { if (params != null) { throw new InvalidAlgorithmParameterException ("Parameters not supported"); } reset(true); p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); + } try { initialize(); } catch (PKCS11Exception e) { --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.AlgorithmParameters; +import java.security.Key; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import javax.crypto.BadPaddingException; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.PBEKeySpec; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import sun.security.jca.JCAUtil; +import sun.security.pkcs11.wrapper.PKCS11Exception; +import sun.security.util.PBEUtil; + +final class P11PBECipher extends CipherSpi { + + private static final int DEFAULT_SALT_LENGTH = 20; + private static final int DEFAULT_COUNT = 4096; + + private final Token token; + private final String pbeAlg; + private final P11Cipher cipher; + private final int blkSize; + private final int keyLen; + private final PBEUtil.PBES2Helper pbes2Helper = new PBEUtil.PBES2Helper( + DEFAULT_SALT_LENGTH, DEFAULT_COUNT); + + P11PBECipher(Token token, String pbeAlg, long cipherMech) + throws PKCS11Exception, NoSuchAlgorithmException { + super(); + String cipherTrans; + if (cipherMech == CKM_AES_CBC_PAD || cipherMech == CKM_AES_CBC) { + cipherTrans = "AES/CBC/PKCS5Padding"; + } else { + throw new NoSuchAlgorithmException( + "Cipher transformation not supported."); + } + cipher = new P11Cipher(token, cipherTrans, cipherMech); + blkSize = cipher.engineGetBlockSize(); + assert P11Util.kdfDataMap.get(pbeAlg) != null; + keyLen = P11Util.kdfDataMap.get(pbeAlg).keyLen; + this.pbeAlg = pbeAlg; + this.token = token; + } + + // see JCE spec + @Override + protected void engineSetMode(String mode) + throws NoSuchAlgorithmException { + cipher.engineSetMode(mode); + } + + // see JCE spec + @Override + protected void engineSetPadding(String padding) + throws NoSuchPaddingException { + cipher.engineSetPadding(padding); + } + + // see JCE spec + @Override + protected int engineGetBlockSize() { + return cipher.engineGetBlockSize(); + } + + // see JCE spec + @Override + protected int engineGetOutputSize(int inputLen) { + return cipher.engineGetOutputSize(inputLen); + } + + // see JCE spec + @Override + protected byte[] engineGetIV() { + return cipher.engineGetIV(); + } + + // see JCE spec + @Override + protected AlgorithmParameters engineGetParameters() { + return pbes2Helper.getAlgorithmParameters( + blkSize, pbeAlg, null, JCAUtil.getSecureRandom()); + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, + SecureRandom random) throws InvalidKeyException { + try { + engineInit(opmode, key, (AlgorithmParameterSpec) null, random); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException("requires PBE parameters", e); + } + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, + InvalidAlgorithmParameterException { + + PBEKeySpec pbeSpec = pbes2Helper.getPBEKeySpec(blkSize, keyLen, + opmode, key, params, random); + + Key derivedKey; + try { + derivedKey = P11SecretKeyFactory.derivePBEKey( + token, pbeSpec, pbeAlg); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + cipher.engineInit(opmode, derivedKey, pbes2Helper.getIvSpec(), random); + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameters params, SecureRandom random) + throws InvalidKeyException, + InvalidAlgorithmParameterException { + engineInit(opmode, key, PBEUtil.PBES2Helper.getParameterSpec(params), + random); + } + + // see JCE spec + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, + int inputLen) { + return cipher.engineUpdate(input, inputOffset, inputLen); + } + + // see JCE spec + @Override + protected int engineUpdate(byte[] input, int inputOffset, + int inputLen, byte[] output, int outputOffset) + throws ShortBufferException { + return cipher.engineUpdate(input, inputOffset, inputLen, + output, outputOffset); + } + + // see JCE spec + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + return cipher.engineDoFinal(input, inputOffset, inputLen); + } + + // see JCE spec + @Override + protected int engineDoFinal(byte[] input, int inputOffset, + int inputLen, byte[] output, int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + return cipher.engineDoFinal(input, inputOffset, inputLen, output, + outputOffset); + } + + // see JCE spec + @Override + protected int engineGetKeySize(Key key) + throws InvalidKeyException { + return cipher.engineGetKeySize(key); + } + +} --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java @@ -31,6 +31,7 @@ import java.security.*; import java.security.spec.*; import javax.crypto.*; +import javax.crypto.interfaces.PBEKey; import javax.crypto.spec.*; import static sun.security.pkcs11.TemplateManager.*; @@ -194,6 +195,130 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { return p11Key; } + static P11Key derivePBEKey(Token token, PBEKeySpec keySpec, String algo) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec == null) { + throw new InvalidKeySpecException("PBEKeySpec must not be null"); + } + Session session = null; + try { + session = token.getObjSession(); + P11Util.KDFData kdfData = P11Util.kdfDataMap.get(algo); + CK_MECHANISM ckMech; + char[] password = keySpec.getPassword(); + byte[] salt = keySpec.getSalt(); + int itCount = keySpec.getIterationCount(); + int keySize = keySpec.getKeyLength(); + if (kdfData.keyLen != -1) { + if (keySize == 0) { + keySize = kdfData.keyLen; + } else if (keySize != kdfData.keyLen) { + throw new InvalidKeySpecException( + "Key length is invalid for " + algo); + } + } + + if (kdfData.kdfMech == CKM_PKCS5_PBKD2) { + CK_INFO p11Info = token.p11.getInfo(); + CK_VERSION p11Ver = (p11Info != null ? p11Info.cryptokiVersion + : null); + if (P11Util.isNSS(token) || p11Ver != null && (p11Ver.major < + 2 || p11Ver.major == 2 && p11Ver.minor < 40)) { + // NSS keeps using the old structure beyond PKCS #11 v2.40 + ckMech = new CK_MECHANISM(kdfData.kdfMech, + new CK_PKCS5_PBKD2_PARAMS(password, salt, + itCount, kdfData.prfMech)); + } else { + ckMech = new CK_MECHANISM(kdfData.kdfMech, + new CK_PKCS5_PBKD2_PARAMS2(password, salt, + itCount, kdfData.prfMech)); + } + } else { + // PKCS #12 "General Method" PBKD (RFC 7292, Appendix B.2) + if (P11Util.isNSS(token)) { + // According to PKCS #11, "password" in CK_PBE_PARAMS has + // a CK_UTF8CHAR_PTR type. This suggests that it is encoded + // in UTF-8. However, NSS expects the password to be encoded + // as BMPString with a NULL terminator when C_GenerateKey + // is called for a PKCS #12 "General Method" derivation + // (see RFC 7292, Appendix B.1). + // + // The char size in Java is 2 bytes. When a char is + // converted to a CK_UTF8CHAR, the high-order byte is + // discarded (see jCharArrayToCKUTF8CharArray in + // p11_util.c). In order to have a BMPString passed to + // C_GenerateKey, we need to account for that and expand: + // the high and low parts of each char are split into 2 + // chars. As an example, this is the transformation for + // a NULL terminated password "a": + // char[] => [ 0x0061, 0x0000 ] + // / \ / \ + // Expansion => [0x0000, 0x0061, 0x0000, 0x0000] + // | | | | + // BMPString => [ 0x00, 0x61, 0x00, 0x00] + // + int inputLength = (password == null) ? 0 : password.length; + char[] expPassword = new char[inputLength * 2 + 2]; + for (int i = 0, j = 0; i < inputLength; i++, j += 2) { + expPassword[j] = (char) ((password[i] >>> 8) & 0xFF); + expPassword[j + 1] = (char) (password[i] & 0xFF); + } + password = expPassword; + } + ckMech = new CK_MECHANISM(kdfData.kdfMech, + new CK_PBE_PARAMS(password, salt, itCount)); + } + + long keyType = getKeyType(kdfData.keyAlgo); + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[ + switch (kdfData.op) { + case ENCRYPTION, AUTHENTICATION -> 4; + case GENERIC -> 5; + }]; + attrs[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY); + attrs[1] = new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3); + attrs[2] = new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType); + switch (kdfData.op) { + case ENCRYPTION -> attrs[3] = CK_ATTRIBUTE.ENCRYPT_TRUE; + case AUTHENTICATION -> attrs[3] = CK_ATTRIBUTE.SIGN_TRUE; + case GENERIC -> { + attrs[3] = CK_ATTRIBUTE.ENCRYPT_TRUE; + attrs[4] = CK_ATTRIBUTE.SIGN_TRUE; + } + } + CK_ATTRIBUTE[] attr = token.getAttributes( + O_GENERATE, CKO_SECRET_KEY, keyType, attrs); + long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr); + return (P11Key)P11Key.secretKey( + session, keyID, kdfData.keyAlgo, keySize, attr); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException("Could not create key", e); + } finally { + token.releaseSession(session); + } + } + + static P11Key derivePBEKey(Token token, PBEKey key, String algo) + throws InvalidKeyException { + token.ensureValid(); + if (key == null) { + throw new InvalidKeyException("PBEKey must not be null"); + } + P11Key p11Key = token.secretCache.get(key); + if (p11Key != null) { + return p11Key; + } + try { + p11Key = derivePBEKey(token, new PBEKeySpec(key.getPassword(), + key.getSalt(), key.getIterationCount()), algo); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + token.secretCache.put(key, p11Key); + return p11Key; + } + static void fixDESParity(byte[] key, int offset) { for (int i = 0; i < 8; i++) { int b = key[offset] & 0xfe; @@ -320,6 +445,9 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { keySpec = new SecretKeySpec(keyBytes, "DESede"); return engineGenerateSecret(keySpec); } + } else if (keySpec instanceof PBEKeySpec) { + return (SecretKey)derivePBEKey(token, + (PBEKeySpec)keySpec, algorithm); } throw new InvalidKeySpecException ("Unsupported spec: " + keySpec.getClass().getName()); @@ -373,6 +501,9 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi { // see JCE spec protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { + if (key instanceof PBEKey) { + return (SecretKey)derivePBEKey(token, (PBEKey)key, algorithm); + } return (SecretKey)convertKey(token, key, algorithm); } --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Util.java @@ -27,6 +27,10 @@ package sun.security.pkcs11; import java.math.BigInteger; import java.security.*; +import java.util.HashMap; +import java.util.Map; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** * Collection of static utility methods. @@ -40,6 +44,93 @@ public final class P11Util { private static volatile Provider sun, sunRsaSign, sunJce; + // Used by PBE + static final class KDFData { + public enum Operation {ENCRYPTION, AUTHENTICATION, GENERIC} + public long kdfMech; + public long prfMech; + public String keyAlgo; + public int keyLen; + public Operation op; + KDFData(long kdfMech, long prfMech, String keyAlgo, + int keyLen, Operation op) { + this.kdfMech = kdfMech; + this.prfMech = prfMech; + this.keyAlgo = keyAlgo; + this.keyLen = keyLen; + this.op = op; + } + + public static void addPbkdf2Data(String algo, long kdfMech, + long prfMech) { + kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, + "Generic", -1, Operation.GENERIC)); + } + + public static void addPbkdf2AesData(String algo, long kdfMech, + long prfMech, int keyLen) { + kdfDataMap.put(algo, new KDFData(kdfMech, prfMech, + "AES", keyLen, Operation.ENCRYPTION)); + } + + public static void addPkcs12KDData(String algo, long kdfMech, + int keyLen) { + kdfDataMap.put(algo, new KDFData(kdfMech, -1, + "Generic", keyLen, Operation.AUTHENTICATION)); + } + } + + static final Map<String, KDFData> kdfDataMap = new HashMap<>(); + + static { + KDFData.addPbkdf2AesData("PBEWithHmacSHA1AndAES_128", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA1, 128); + KDFData.addPbkdf2AesData("PBEWithHmacSHA224AndAES_128", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA224, 128); + KDFData.addPbkdf2AesData("PBEWithHmacSHA256AndAES_128", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA256, 128); + KDFData.addPbkdf2AesData("PBEWithHmacSHA384AndAES_128", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA384, 128); + KDFData.addPbkdf2AesData("PBEWithHmacSHA512AndAES_128", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512, 128); + KDFData.addPbkdf2AesData("PBEWithHmacSHA1AndAES_256", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA1, 256); + KDFData.addPbkdf2AesData("PBEWithHmacSHA224AndAES_256", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA224, 256); + KDFData.addPbkdf2AesData("PBEWithHmacSHA256AndAES_256", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA256, 256); + KDFData.addPbkdf2AesData("PBEWithHmacSHA384AndAES_256", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA384, 256); + KDFData.addPbkdf2AesData("PBEWithHmacSHA512AndAES_256", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512, 256); + + KDFData.addPbkdf2Data("PBKDF2WithHmacSHA1", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA1); + KDFData.addPbkdf2Data("PBKDF2WithHmacSHA224", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA224); + KDFData.addPbkdf2Data("PBKDF2WithHmacSHA256", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA256); + KDFData.addPbkdf2Data("PBKDF2WithHmacSHA384", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA384); + KDFData.addPbkdf2Data("PBKDF2WithHmacSHA512", + CKM_PKCS5_PBKD2, CKP_PKCS5_PBKD2_HMAC_SHA512); + + KDFData.addPkcs12KDData("HmacPBESHA1", + CKM_PBA_SHA1_WITH_SHA1_HMAC, 160); + KDFData.addPkcs12KDData("HmacPBESHA224", + CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, 224); + KDFData.addPkcs12KDData("HmacPBESHA256", + CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, 256); + KDFData.addPkcs12KDData("HmacPBESHA384", + CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, 384); + KDFData.addPkcs12KDData("HmacPBESHA512", + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); + KDFData.addPkcs12KDData("HmacPBESHA512/224", + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); + KDFData.addPkcs12KDData("HmacPBESHA512/256", + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512); + } + private P11Util() { // empty } --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -26,6 +26,9 @@ package sun.security.pkcs11; import java.io.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.*; import java.security.*; @@ -42,10 +45,12 @@ import javax.security.auth.callback.PasswordCallback; import com.sun.crypto.provider.ChaCha20Poly1305Parameters; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.InnocuousThread; import sun.security.util.Debug; import sun.security.util.ResourcesMgr; import static sun.security.util.SecurityConstants.PROVIDER_VER; +import sun.security.util.SecurityProperties; import static sun.security.util.SecurityProviderConstants.getAliases; import sun.security.pkcs11.Secmod.*; @@ -62,6 +67,39 @@ import static sun.security.pkcs11.wrapper.PKCS11Exception.*; */ public final class SunPKCS11 extends AuthProvider { + private static final boolean systemFipsEnabled = SharedSecrets + .getJavaSecuritySystemConfiguratorAccess().isSystemFipsEnabled(); + + private static final boolean plainKeySupportEnabled = SharedSecrets + .getJavaSecuritySystemConfiguratorAccess().isPlainKeySupportEnabled(); + + private static final MethodHandle fipsImportKey; + private static final MethodHandle fipsExportKey; + static { + MethodHandle fipsImportKeyTmp = null; + MethodHandle fipsExportKeyTmp = null; + if (plainKeySupportEnabled) { + try { + fipsImportKeyTmp = MethodHandles.lookup().findStatic( + FIPSKeyImporter.class, "importKey", + MethodType.methodType(Long.class, SunPKCS11.class, + long.class, CK_ATTRIBUTE[].class)); + fipsExportKeyTmp = MethodHandles.lookup().findStatic( + FIPSKeyImporter.class, "exportKey", + MethodType.methodType(void.class, SunPKCS11.class, + long.class, long.class, + long.class, long.class, Map.class)); + } catch (Throwable t) { + throw new SecurityException("FIPS key importer-exporter" + + " initialization failed", t); + } + } + fipsImportKey = fipsImportKeyTmp; + fipsExportKey = fipsExportKeyTmp; + } + + private static final String FIPS_NSSDB_PATH_PROP = "fips.nssdb.path"; + private static final long serialVersionUID = -1354835039035306505L; static final Debug debug = Debug.getInstance("sunpkcs11"); @@ -115,6 +153,29 @@ public final class SunPKCS11 extends AuthProvider { return AccessController.doPrivileged(new PrivilegedExceptionAction<>() { @Override public SunPKCS11 run() throws Exception { + if (systemFipsEnabled) { + /* + * The nssSecmodDirectory attribute in the SunPKCS11 + * NSS configuration file takes the value of the + * fips.nssdb.path System property after expansion. + * Security properties expansion is unsupported. + */ + String nssdbPath = + SecurityProperties.privilegedGetOverridable( + FIPS_NSSDB_PATH_PROP); + if (System.getSecurityManager() != null) { + AccessController.doPrivileged( + (PrivilegedAction<Void>) () -> { + System.setProperty( + FIPS_NSSDB_PATH_PROP, + nssdbPath); + return null; + }); + } else { + System.setProperty( + FIPS_NSSDB_PATH_PROP, nssdbPath); + } + } return new SunPKCS11(new Config(newConfigName)); } }); @@ -320,10 +381,19 @@ public final class SunPKCS11 extends AuthProvider { // request multithreaded access first initArgs.flags = CKF_OS_LOCKING_OK; PKCS11 tmpPKCS11; + MethodHandle fipsKeyImporter = null; + MethodHandle fipsKeyExporter = null; + if (plainKeySupportEnabled) { + fipsKeyImporter = MethodHandles.insertArguments( + fipsImportKey, 0, this); + fipsKeyExporter = MethodHandles.insertArguments( + fipsExportKey, 0, this); + } try { tmpPKCS11 = PKCS11.getInstance( library, functionList, initArgs, - config.getOmitInitialize()); + config.getOmitInitialize(), fipsKeyImporter, + fipsKeyExporter); } catch (PKCS11Exception e) { if (debug != null) { debug.println("Multi-threaded initialization failed: " + e); @@ -339,11 +409,12 @@ public final class SunPKCS11 extends AuthProvider { initArgs.flags = 0; } tmpPKCS11 = PKCS11.getInstance(library, - functionList, initArgs, config.getOmitInitialize()); + functionList, initArgs, config.getOmitInitialize(), fipsKeyImporter, + fipsKeyExporter); } p11 = tmpPKCS11; - CK_INFO p11Info = p11.C_GetInfo(); + CK_INFO p11Info = p11.getInfo(); if (p11Info.cryptokiVersion.major < 2) { throw new ProviderException("Only PKCS#11 v2.0 and later " + "supported, library version is v" + p11Info.cryptokiVersion); @@ -417,14 +488,19 @@ public final class SunPKCS11 extends AuthProvider { final String className; final List<String> aliases; final int[] mechanisms; + final int[] requiredMechs; + // mechanisms is a list of possible mechanisms that implement the + // algorithm, at least one of them must be available. requiredMechs + // is a list of auxiliary mechanisms, all of them must be available private Descriptor(String type, String algorithm, String className, - List<String> aliases, int[] mechanisms) { + List<String> aliases, int[] mechanisms, int[] requiredMechs) { this.type = type; this.algorithm = algorithm; this.className = className; this.aliases = aliases; this.mechanisms = mechanisms; + this.requiredMechs = requiredMechs; } private P11Service service(Token token, int mechanism) { return new P11Service @@ -458,18 +534,29 @@ public final class SunPKCS11 extends AuthProvider { private static void d(String type, String algorithm, String className, int[] m) { - register(new Descriptor(type, algorithm, className, null, m)); + register(new Descriptor(type, algorithm, className, null, m, null)); } private static void d(String type, String algorithm, String className, List<String> aliases, int[] m) { - register(new Descriptor(type, algorithm, className, aliases, m)); + register(new Descriptor(type, algorithm, className, aliases, m, null)); + } + + private static void d(String type, String algorithm, String className, + int[] m, int[] requiredMechs) { + register(new Descriptor(type, algorithm, className, null, m, + requiredMechs)); + } + private static void dA(String type, String algorithm, String className, + int[] m, int[] requiredMechs) { + register(new Descriptor(type, algorithm, className, + getAliases(algorithm), m, requiredMechs)); } private static void dA(String type, String algorithm, String className, int[] m) { register(new Descriptor(type, algorithm, className, - getAliases(algorithm), m)); + getAliases(algorithm), m, null)); } private static void register(Descriptor d) { @@ -525,6 +612,7 @@ public final class SunPKCS11 extends AuthProvider { String P11Cipher = "sun.security.pkcs11.P11Cipher"; String P11RSACipher = "sun.security.pkcs11.P11RSACipher"; String P11AEADCipher = "sun.security.pkcs11.P11AEADCipher"; + String P11PBECipher = "sun.security.pkcs11.P11PBECipher"; String P11Signature = "sun.security.pkcs11.P11Signature"; String P11PSSSignature = "sun.security.pkcs11.P11PSSSignature"; @@ -587,6 +675,30 @@ public final class SunPKCS11 extends AuthProvider { d(MAC, "SslMacSHA1", P11Mac, m(CKM_SSL3_SHA1_MAC)); + if (systemFipsEnabled) { + /* + * PBA HMacs + * + * KeyDerivationMech must be supported + * for these services to be available. + * + */ + d(MAC, "HmacPBESHA1", P11Mac, m(CKM_SHA_1_HMAC), + m(CKM_PBA_SHA1_WITH_SHA1_HMAC)); + d(MAC, "HmacPBESHA224", P11Mac, m(CKM_SHA224_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA256", P11Mac, m(CKM_SHA256_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA384", P11Mac, m(CKM_SHA384_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA512", P11Mac, m(CKM_SHA512_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA512/224", P11Mac, m(CKM_SHA512_224_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + d(MAC, "HmacPBESHA512/256", P11Mac, m(CKM_SHA512_256_HMAC), + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + } + d(KPG, "RSA", P11KeyPairGenerator, getAliases("PKCS1"), m(CKM_RSA_PKCS_KEY_PAIR_GEN)); @@ -685,6 +797,66 @@ public final class SunPKCS11 extends AuthProvider { d(SKF, "ChaCha20", P11SecretKeyFactory, m(CKM_CHACHA20_POLY1305)); + if (systemFipsEnabled) { + /* + * PBE Secret Key Factories + * + * KeyDerivationPrf must be supported for these services + * to be available. + * + */ + d(SKF, "PBEWithHmacSHA1AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); + d(SKF, "PBEWithHmacSHA224AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); + d(SKF, "PBEWithHmacSHA256AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); + d(SKF, "PBEWithHmacSHA384AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); + d(SKF, "PBEWithHmacSHA512AndAES_128", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); + d(SKF, "PBEWithHmacSHA1AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); + d(SKF, "PBEWithHmacSHA224AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); + d(SKF, "PBEWithHmacSHA256AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); + d(SKF, "PBEWithHmacSHA384AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); + d(SKF, "PBEWithHmacSHA512AndAES_256", + P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); + /* + * PBA Secret Key Factories + */ + d(SKF, "HmacPBESHA1", P11SecretKeyFactory, + m(CKM_PBA_SHA1_WITH_SHA1_HMAC)); + d(SKF, "HmacPBESHA224", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA256", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA384", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA512", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA512/224", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + d(SKF, "HmacPBESHA512/256", P11SecretKeyFactory, + m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); + /* + * PBKDF2 Secret Key Factories + */ + dA(SKF, "PBKDF2WithHmacSHA1", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); + d(SKF, "PBKDF2WithHmacSHA224", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); + d(SKF, "PBKDF2WithHmacSHA256", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); + d(SKF, "PBKDF2WithHmacSHA384", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); + d(SKF, "PBKDF2WithHmacSHA512", P11SecretKeyFactory, + m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); + } + // XXX attributes for Ciphers (supported modes, padding) dA(CIP, "ARCFOUR", P11Cipher, m(CKM_RC4)); @@ -754,6 +926,46 @@ public final class SunPKCS11 extends AuthProvider { d(CIP, "RSA/ECB/NoPadding", P11RSACipher, m(CKM_RSA_X_509)); + if (systemFipsEnabled) { + /* + * PBE Ciphers + * + * KeyDerivationMech and KeyDerivationPrf must be supported + * for these services to be available. + * + */ + d(CIP, "PBEWithHmacSHA1AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA_1_HMAC)); + d(CIP, "PBEWithHmacSHA224AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA224_HMAC)); + d(CIP, "PBEWithHmacSHA256AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA256_HMAC)); + d(CIP, "PBEWithHmacSHA384AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA384_HMAC)); + d(CIP, "PBEWithHmacSHA512AndAES_128", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA512_HMAC)); + d(CIP, "PBEWithHmacSHA1AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA_1_HMAC)); + d(CIP, "PBEWithHmacSHA224AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA224_HMAC)); + d(CIP, "PBEWithHmacSHA256AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA256_HMAC)); + d(CIP, "PBEWithHmacSHA384AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA384_HMAC)); + d(CIP, "PBEWithHmacSHA512AndAES_256", P11PBECipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC), + m(CKM_PKCS5_PBKD2, CKM_SHA512_HMAC)); + } + d(SIG, "RawDSA", P11Signature, List.of("NONEwithDSA"), m(CKM_DSA)); @@ -1144,9 +1356,21 @@ public final class SunPKCS11 extends AuthProvider { if (ds == null) { continue; } + descLoop: for (Descriptor d : ds) { Integer oldMech = supportedAlgs.get(d); if (oldMech == null) { + if (d.requiredMechs != null) { + // Check that other mechanisms required for the + // service are supported before listing it as + // available for the first time. + for (int requiredMech : d.requiredMechs) { + if (token.getMechanismInfo( + requiredMech & 0xFFFFFFFFL) == null) { + continue descLoop; + } + } + } supportedAlgs.put(d, integerMech); continue; } @@ -1220,11 +1444,52 @@ public final class SunPKCS11 extends AuthProvider { } @Override + @SuppressWarnings("removal") public Object newInstance(Object param) throws NoSuchAlgorithmException { if (token.isValid() == false) { throw new NoSuchAlgorithmException("Token has been removed"); } + if (systemFipsEnabled && !token.fipsLoggedIn && + !getType().equals("KeyStore")) { + /* + * The NSS Software Token in FIPS 140-2 mode requires a + * user login for most operations. See sftk_fipsCheck + * (nss/lib/softoken/fipstokn.c). In case of a KeyStore + * service, let the caller perform the login with + * KeyStore::load. Keytool, for example, does this to pass a + * PIN from either the -srcstorepass or -deststorepass + * argument. In case of a non-KeyStore service, perform the + * login now with the PIN available in the fips.nssdb.pin + * property. + */ + try { + if (System.getSecurityManager() != null) { + try { + AccessController.doPrivileged( + (PrivilegedExceptionAction<Void>) () -> { + token.ensureLoggedIn(null); + return null; + }); + } catch (PrivilegedActionException pae) { + Exception e = pae.getException(); + if (e instanceof LoginException le) { + throw le; + } else if (e instanceof PKCS11Exception p11e) { + throw p11e; + } else { + throw new RuntimeException(e); + } + } + } else { + token.ensureLoggedIn(null); + } + } catch (PKCS11Exception | LoginException e) { + throw new ProviderException("FIPS: error during the Token" + + " login required for the " + getType() + + " service.", e); + } + } try { return newInstance0(param); } catch (PKCS11Exception e) { @@ -1244,6 +1509,8 @@ public final class SunPKCS11 extends AuthProvider { } else if (algorithm.endsWith("GCM/NoPadding") || algorithm.startsWith("ChaCha20-Poly1305")) { return new P11AEADCipher(token, algorithm, mechanism); + } else if (algorithm.startsWith("PBE")) { + return new P11PBECipher(token, algorithm, mechanism); } else { return new P11Cipher(token, algorithm, mechanism); } @@ -1579,6 +1846,9 @@ public final class SunPKCS11 extends AuthProvider { try { session = token.getOpSession(); p11.C_Logout(session.id()); + if (systemFipsEnabled) { + token.fipsLoggedIn = false; + } if (debug != null) { debug.println("logout succeeded"); } --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Token.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Token.java @@ -33,6 +33,7 @@ import java.lang.ref.*; import java.security.*; import javax.security.auth.login.LoginException; +import jdk.internal.access.SharedSecrets; import sun.security.jca.JCAUtil; import sun.security.pkcs11.wrapper.*; @@ -48,6 +49,9 @@ import static sun.security.pkcs11.wrapper.PKCS11Exception.*; */ class Token implements Serializable { + private static final boolean systemFipsEnabled = SharedSecrets + .getJavaSecuritySystemConfiguratorAccess().isSystemFipsEnabled(); + // need to be serializable to allow SecureRandom to be serialized private static final long serialVersionUID = 2541527649100571747L; @@ -114,6 +118,10 @@ class Token implements Serializable { // flag indicating whether we are logged in private volatile boolean loggedIn; + // Flag indicating the login status for the NSS Software Token in FIPS mode. + // This Token is never asynchronously removed. Used from SunPKCS11. + volatile boolean fipsLoggedIn; + // time we last checked login status private long lastLoginCheck; @@ -232,9 +240,14 @@ class Token implements Serializable { // call provider.login() if not void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException { if (isLoggedIn(session) == false) { + if (systemFipsEnabled) { + provider.login(null, new FIPSTokenLoginHandler()); + fipsLoggedIn = true; + } else { provider.login(null, null); } } + } // return whether this token object is valid (i.e. token not removed) // returns value from last check, does not perform new check --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java @@ -100,9 +100,9 @@ public class CK_ECDH1_DERIVE_PARAMS { } /** - * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * Returns the string representation of CK_ECDH1_DERIVE_PARAMS. * - * @return the string representation of CK_PKCS5_PBKD2_PARAMS + * @return the string representation of CK_ECDH1_DERIVE_PARAMS */ public String toString() { StringBuilder sb = new StringBuilder(); --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java @@ -160,6 +160,18 @@ public class CK_MECHANISM { init(mechanism, params); } + public CK_MECHANISM(long mechanism, CK_PBE_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_PKCS5_PBKD2_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_PKCS5_PBKD2_PARAMS2 params) { + init(mechanism, params); + } + // For PSS. the parameter may be set multiple times, use the // CK_MECHANISM(long) constructor and setParameter(CK_RSA_PKCS_PSS_PARAMS) // methods instead of creating yet another constructor --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java @@ -50,15 +50,15 @@ package sun.security.pkcs11.wrapper; /** - * class CK_PBE_PARAMS provides all of the necessary information required byte + * class CK_PBE_PARAMS provides all the necessary information required by * the CKM_PBE mechanisms and the CKM_PBA_SHA1_WITH_SHA1_HMAC mechanism.<p> * <B>PKCS#11 structure:</B> * <PRE> * typedef struct CK_PBE_PARAMS { - * CK_CHAR_PTR pInitVector; - * CK_CHAR_PTR pPassword; + * CK_BYTE_PTR pInitVector; + * CK_UTF8CHAR_PTR pPassword; * CK_ULONG ulPasswordLen; - * CK_CHAR_PTR pSalt; + * CK_BYTE_PTR pSalt; * CK_ULONG ulSaltLen; * CK_ULONG ulIteration; * } CK_PBE_PARAMS; @@ -72,15 +72,15 @@ public class CK_PBE_PARAMS { /** * <B>PKCS#11:</B> * <PRE> - * CK_CHAR_PTR pInitVector; + * CK_BYTE_PTR pInitVector; * </PRE> */ - public char[] pInitVector; + public byte[] pInitVector; /** * <B>PKCS#11:</B> * <PRE> - * CK_CHAR_PTR pPassword; + * CK_UTF8CHAR_PTR pPassword; * CK_ULONG ulPasswordLen; * </PRE> */ @@ -89,11 +89,11 @@ public class CK_PBE_PARAMS { /** * <B>PKCS#11:</B> * <PRE> - * CK_CHAR_PTR pSalt + * CK_BYTE_PTR pSalt * CK_ULONG ulSaltLen; * </PRE> */ - public char[] pSalt; + public byte[] pSalt; /** * <B>PKCS#11:</B> @@ -103,6 +103,12 @@ public class CK_PBE_PARAMS { */ public long ulIteration; + public CK_PBE_PARAMS(char[] pPassword, byte[] pSalt, long ulIteration) { + this.pPassword = pPassword; + this.pSalt = pSalt; + this.ulIteration = ulIteration; + } + /** * Returns the string representation of CK_PBE_PARAMS. * --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java @@ -47,7 +47,7 @@ package sun.security.pkcs11.wrapper; - +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** * class CK_PKCS5_PBKD2_PARAMS provides the parameters to the CKM_PKCS5_PBKD2 @@ -55,13 +55,15 @@ package sun.security.pkcs11.wrapper; * <B>PKCS#11 structure:</B> * <PRE> * typedef struct CK_PKCS5_PBKD2_PARAMS { - * CK_PKCS5_PBKD2_SALT_SOURCE_TYPE saltSource; + * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; * CK_VOID_PTR pSaltSourceData; * CK_ULONG ulSaltSourceDataLen; * CK_ULONG iterations; * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; * CK_VOID_PTR pPrfData; * CK_ULONG ulPrfDataLen; + * CK_UTF8CHAR_PTR pPassword; + * CK_ULONG_PTR ulPasswordLen; * } CK_PKCS5_PBKD2_PARAMS; * </PRE> * @@ -112,6 +114,24 @@ public class CK_PKCS5_PBKD2_PARAMS { */ public byte[] pPrfData; + /** + * <b>PKCS#11:</b> + * <pre> + * CK_UTF8CHAR_PTR pPassword + * CK_ULONG_PTR ulPasswordLen; + * </pre> + */ + public char[] pPassword; + + public CK_PKCS5_PBKD2_PARAMS(char[] pPassword, byte[] pSalt, + long iterations, long prf) { + this.pPassword = pPassword; + this.pSaltSourceData = pSalt; + this.iterations = iterations; + this.prf = prf; + this.saltSource = CKZ_SALT_SPECIFIED; + } + /** * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. * --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11.wrapper; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * class CK_PKCS5_PBKD2_PARAMS2 provides the parameters to the CKM_PKCS5_PBKD2 + * mechanism.<p> + * <b>PKCS#11 structure:</b> + * <pre> + * typedef struct CK_PKCS5_PBKD2_PARAMS2 { + * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + * CK_VOID_PTR pSaltSourceData; + * CK_ULONG ulSaltSourceDataLen; + * CK_ULONG iterations; + * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + * CK_VOID_PTR pPrfData; + * CK_ULONG ulPrfDataLen; + * CK_UTF8CHAR_PTR pPassword; + * CK_ULONG ulPasswordLen; + * } CK_PKCS5_PBKD2_PARAMS2; + * </pre> + * + */ +public class CK_PKCS5_PBKD2_PARAMS2 { + + /** + * <b>PKCS#11:</b> + * <pre> + * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + * </pre> + */ + public long saltSource; + + /** + * <b>PKCS#11:</b> + * <pre> + * CK_VOID_PTR pSaltSourceData; + * CK_ULONG ulSaltSourceDataLen; + * </pre> + */ + public byte[] pSaltSourceData; + + /** + * <b>PKCS#11:</b> + * <pre> + * CK_ULONG iterations; + * </pre> + */ + public long iterations; + + /** + * <b>PKCS#11:</b> + * <pre> + * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + * </pre> + */ + public long prf; + + /** + * <b>PKCS#11:</b> + * <pre> + * CK_VOID_PTR pPrfData; + * CK_ULONG ulPrfDataLen; + * </pre> + */ + public byte[] pPrfData; + + /** + * <b>PKCS#11:</b> + * <pre> + * CK_UTF8CHAR_PTR pPassword + * CK_ULONG ulPasswordLen; + * </pre> + */ + public char[] pPassword; + + public CK_PKCS5_PBKD2_PARAMS2(char[] pPassword, byte[] pSalt, + long iterations, long prf) { + this.pPassword = pPassword; + this.pSaltSourceData = pSalt; + this.iterations = iterations; + this.prf = prf; + this.saltSource = CKZ_SALT_SPECIFIED; + } + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS2. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS2 + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(Constants.INDENT); + sb.append("saltSource: "); + sb.append(saltSource); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("pSaltSourceData: "); + sb.append(Functions.toHexString(pSaltSourceData)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("ulSaltSourceDataLen: "); + sb.append(pSaltSourceData.length); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("iterations: "); + sb.append(iterations); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("prf: "); + sb.append(prf); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("pPrfData: "); + sb.append(Functions.toHexString(pPrfData)); + sb.append(Constants.NEWLINE); + + sb.append(Constants.INDENT); + sb.append("ulPrfDataLen: "); + sb.append(pPrfData.length); + + return sb.toString(); + } + +} --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java @@ -94,9 +94,9 @@ public class CK_X9_42_DH1_DERIVE_PARAMS { public byte[] pPublicData; /** - * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * Returns the string representation of CK_X9_42_DH1_DERIVE_PARAMS. * - * @return the string representation of CK_PKCS5_PBKD2_PARAMS + * @return the string representation of CK_X9_42_DH1_DERIVE_PARAMS */ public String toString() { StringBuilder sb = new StringBuilder(); --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11.java @@ -49,6 +49,9 @@ package sun.security.pkcs11.wrapper; import java.io.File; import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.*; import java.security.AccessController; @@ -113,6 +116,8 @@ public class PKCS11 { private long pNativeData; + private volatile CK_INFO pInfo; + /** * This method does the initialization of the native library. It is called * exactly once for this class. @@ -145,24 +150,49 @@ public class PKCS11 { * @postconditions */ PKCS11(String pkcs11ModulePath, String functionListName) - throws IOException { + throws IOException, PKCS11Exception { connect(pkcs11ModulePath, functionListName); this.pkcs11ModulePath = pkcs11ModulePath; } + /* + * Compatibility wrapper to allow this method to work as before + * when FIPS mode support is not active. + */ public static synchronized PKCS11 getInstance(String pkcs11ModulePath, String functionList, CK_C_INITIALIZE_ARGS pInitArgs, boolean omitInitialize) throws IOException, PKCS11Exception { + return getInstance(pkcs11ModulePath, functionList, + pInitArgs, omitInitialize, null, null); + } + + public static synchronized PKCS11 getInstance(String pkcs11ModulePath, + String functionList, CK_C_INITIALIZE_ARGS pInitArgs, + boolean omitInitialize, MethodHandle fipsKeyImporter, + MethodHandle fipsKeyExporter) + throws IOException, PKCS11Exception { // we may only call C_Initialize once per native .so/.dll // so keep a cache using the (non-canonicalized!) path PKCS11 pkcs11 = moduleMap.get(pkcs11ModulePath); if (pkcs11 == null) { + boolean nssFipsMode = fipsKeyImporter != null && + fipsKeyExporter != null; if ((pInitArgs != null) && ((pInitArgs.flags & CKF_OS_LOCKING_OK) != 0)) { + if (nssFipsMode) { + pkcs11 = new FIPSPKCS11(pkcs11ModulePath, functionList, + fipsKeyImporter, fipsKeyExporter); + } else { pkcs11 = new PKCS11(pkcs11ModulePath, functionList); + } + } else { + if (nssFipsMode) { + pkcs11 = new SynchronizedFIPSPKCS11(pkcs11ModulePath, + functionList, fipsKeyImporter, fipsKeyExporter); } else { pkcs11 = new SynchronizedPKCS11(pkcs11ModulePath, functionList); } + } if (omitInitialize == false) { try { pkcs11.C_Initialize(pInitArgs); @@ -179,6 +209,28 @@ public class PKCS11 { return pkcs11; } + /** + * Returns the CK_INFO structure fetched at initialization with + * C_GetInfo. This structure represent Cryptoki library information. + */ + public CK_INFO getInfo() { + CK_INFO lPInfo = pInfo; + if (lPInfo == null) { + synchronized (this) { + lPInfo = pInfo; + if (lPInfo == null) { + try { + lPInfo = C_GetInfo(); + pInfo = lPInfo; + } catch (PKCS11Exception e) { + // Some PKCS #11 tokens require initialization first. + } + } + } + } + return lPInfo; + } + /** * Connects this object to the specified PKCS#11 library. This method is for * internal use only. @@ -1625,7 +1677,7 @@ public class PKCS11 { static class SynchronizedPKCS11 extends PKCS11 { SynchronizedPKCS11(String pkcs11ModulePath, String functionListName) - throws IOException { + throws IOException, PKCS11Exception { super(pkcs11ModulePath, functionListName); } @@ -1911,4 +1963,194 @@ static class SynchronizedPKCS11 extends PKCS11 { super.C_GenerateRandom(hSession, randomData); } } + +// PKCS11 subclass that allows using plain private or secret keys in +// FIPS-configured NSS Software Tokens. Only used when System FIPS +// is enabled. +static class FIPSPKCS11 extends PKCS11 { + private MethodHandle fipsKeyImporter; + private MethodHandle fipsKeyExporter; + private MethodHandle hC_GetAttributeValue; + FIPSPKCS11(String pkcs11ModulePath, String functionListName, + MethodHandle fipsKeyImporter, MethodHandle fipsKeyExporter) + throws IOException, PKCS11Exception { + super(pkcs11ModulePath, functionListName); + this.fipsKeyImporter = fipsKeyImporter; + this.fipsKeyExporter = fipsKeyExporter; + try { + hC_GetAttributeValue = MethodHandles.insertArguments( + MethodHandles.lookup().findSpecial(PKCS11.class, + "C_GetAttributeValue", MethodType.methodType( + void.class, long.class, long.class, + CK_ATTRIBUTE[].class), + FIPSPKCS11.class), 0, this); + } catch (Throwable t) { + throw new RuntimeException( + "sun.security.pkcs11.wrapper.PKCS11" + + "::C_GetAttributeValue method not found.", t); + } + } + + public long C_CreateObject(long hSession, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + // Creating sensitive key objects from plain key material in a + // FIPS-configured NSS Software Token is not allowed. We apply + // a key-unwrapping scheme to achieve so. + if (FIPSPKCS11Helper.isSensitiveObject(pTemplate)) { + try { + return ((Long)fipsKeyImporter.invoke(hSession, pTemplate)) + .longValue(); + } catch (Throwable t) { + if (t instanceof PKCS11Exception) { + throw (PKCS11Exception)t; + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + t.getMessage()); + } + } + return super.C_CreateObject(hSession, pTemplate); + } + + public void C_GetAttributeValue(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + FIPSPKCS11Helper.C_GetAttributeValue(hC_GetAttributeValue, + fipsKeyExporter, hSession, hObject, pTemplate); + } +} + +// FIPSPKCS11 synchronized counterpart. +static class SynchronizedFIPSPKCS11 extends SynchronizedPKCS11 { + private MethodHandle fipsKeyImporter; + private MethodHandle fipsKeyExporter; + private MethodHandle hC_GetAttributeValue; + SynchronizedFIPSPKCS11(String pkcs11ModulePath, String functionListName, + MethodHandle fipsKeyImporter, MethodHandle fipsKeyExporter) + throws IOException, PKCS11Exception { + super(pkcs11ModulePath, functionListName); + this.fipsKeyImporter = fipsKeyImporter; + this.fipsKeyExporter = fipsKeyExporter; + try { + hC_GetAttributeValue = MethodHandles.insertArguments( + MethodHandles.lookup().findSpecial(SynchronizedPKCS11.class, + "C_GetAttributeValue", MethodType.methodType( + void.class, long.class, long.class, + CK_ATTRIBUTE[].class), + SynchronizedFIPSPKCS11.class), 0, this); + } catch (Throwable t) { + throw new RuntimeException( + "sun.security.pkcs11.wrapper.SynchronizedPKCS11" + + "::C_GetAttributeValue method not found.", t); + } + } + + public synchronized long C_CreateObject(long hSession, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + // See FIPSPKCS11::C_CreateObject. + if (FIPSPKCS11Helper.isSensitiveObject(pTemplate)) { + try { + return ((Long)fipsKeyImporter.invoke(hSession, pTemplate)) + .longValue(); + } catch (Throwable t) { + if (t instanceof PKCS11Exception) { + throw (PKCS11Exception)t; + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + t.getMessage()); + } + } + return super.C_CreateObject(hSession, pTemplate); + } + + public synchronized void C_GetAttributeValue(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + FIPSPKCS11Helper.C_GetAttributeValue(hC_GetAttributeValue, + fipsKeyExporter, hSession, hObject, pTemplate); + } +} + +private static class FIPSPKCS11Helper { + static boolean isSensitiveObject(CK_ATTRIBUTE[] pTemplate) { + for (CK_ATTRIBUTE attr : pTemplate) { + if (attr.type == CKA_CLASS && + (attr.getLong() == CKO_PRIVATE_KEY || + attr.getLong() == CKO_SECRET_KEY)) { + return true; + } + } + return false; + } + static void C_GetAttributeValue(MethodHandle hC_GetAttributeValue, + MethodHandle fipsKeyExporter, long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + Map<Long, CK_ATTRIBUTE> sensitiveAttrs = new HashMap<>(); + List<CK_ATTRIBUTE> nonSensitiveAttrs = new LinkedList<>(); + FIPSPKCS11Helper.getAttributesBySensitivity(pTemplate, + sensitiveAttrs, nonSensitiveAttrs); + try { + if (sensitiveAttrs.size() > 0) { + long keyClass = -1L; + long keyType = -1L; + try { + // Secret and private keys have both class and type + // attributes, so we can query them at once. + CK_ATTRIBUTE[] queryAttrs = new CK_ATTRIBUTE[]{ + new CK_ATTRIBUTE(CKA_CLASS), + new CK_ATTRIBUTE(CKA_KEY_TYPE), + }; + hC_GetAttributeValue.invoke(hSession, hObject, queryAttrs); + keyClass = queryAttrs[0].getLong(); + keyType = queryAttrs[1].getLong(); + } catch (PKCS11Exception e) { + // If the query fails, the object is neither a secret nor a + // private key. As this case won't be handled with the FIPS + // Key Exporter, we keep keyClass initialized to -1L. + } + if (keyClass == CKO_SECRET_KEY || keyClass == CKO_PRIVATE_KEY) { + fipsKeyExporter.invoke(hSession, hObject, keyClass, keyType, + sensitiveAttrs); + if (nonSensitiveAttrs.size() > 0) { + CK_ATTRIBUTE[] pNonSensitiveAttrs = + new CK_ATTRIBUTE[nonSensitiveAttrs.size()]; + int i = 0; + for (CK_ATTRIBUTE nonSensAttr : nonSensitiveAttrs) { + pNonSensitiveAttrs[i++] = nonSensAttr; + } + hC_GetAttributeValue.invoke(hSession, hObject, + pNonSensitiveAttrs); + // libj2pkcs11 allocates new CK_ATTRIBUTE objects, so we + // update the reference on the previous CK_ATTRIBUTEs + i = 0; + for (CK_ATTRIBUTE nonSensAttr : nonSensitiveAttrs) { + nonSensAttr.pValue = pNonSensitiveAttrs[i++].pValue; + } + } + return; + } + } + hC_GetAttributeValue.invoke(hSession, hObject, pTemplate); + } catch (Throwable t) { + if (t instanceof PKCS11Exception) { + throw (PKCS11Exception)t; + } + throw new PKCS11Exception(CKR_GENERAL_ERROR, + t.getMessage()); + } + } + private static void getAttributesBySensitivity(CK_ATTRIBUTE[] pTemplate, + Map<Long, CK_ATTRIBUTE> sensitiveAttrs, + List<CK_ATTRIBUTE> nonSensitiveAttrs) { + for (CK_ATTRIBUTE attr : pTemplate) { + long type = attr.type; + // Aligned with NSS' sftk_isSensitive in lib/softoken/pkcs11u.c + if (type == CKA_VALUE || type == CKA_PRIVATE_EXPONENT || + type == CKA_PRIME_1 || type == CKA_PRIME_2 || + type == CKA_EXPONENT_1 || type == CKA_EXPONENT_2 || + type == CKA_COEFFICIENT) { + sensitiveAttrs.put(type, attr); + } else { + nonSensitiveAttrs.add(attr); + } + } + } +} } --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java @@ -1104,17 +1104,6 @@ public interface PKCS11Constants { public static final long CKD_BLAKE2B_384_KDF = 0x00000019L; public static final long CKD_BLAKE2B_512_KDF = 0x0000001aL; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA1 = 0x00000001L; - public static final long CKP_PKCS5_PBKD2_HMAC_GOSTR3411 = 0x00000002L; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA224 = 0x00000003L; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA256 = 0x00000004L; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA384 = 0x00000005L; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA512 = 0x00000006L; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA512_224 = 0x00000007L; - public static final long CKP_PKCS5_PBKD2_HMAC_SHA512_256 = 0x00000008L; - - public static final long CKZ_SALT_SPECIFIED = 0x00000001L; - public static final long CK_OTP_VALUE = 0x00000000L; public static final long CK_OTP_PIN = 0x00000001L; public static final long CK_OTP_CHALLENGE = 0x00000002L; @@ -1150,12 +1139,23 @@ public interface PKCS11Constants { public static final long CKF_HKDF_SALT_KEY = 0x00000004L; */ + // PBKDF2 support, used in P11Util + public static final long CKZ_SALT_SPECIFIED = 0x00000001L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA1 = 0x00000001L; + public static final long CKP_PKCS5_PBKD2_HMAC_GOSTR3411 = 0x00000002L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA224 = 0x00000003L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA256 = 0x00000004L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA384 = 0x00000005L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA512 = 0x00000006L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA512_224 = 0x00000007L; + public static final long CKP_PKCS5_PBKD2_HMAC_SHA512_256 = 0x00000008L; + // private NSS attribute (for DSA and DH private keys) public static final long CKA_NETSCAPE_DB = 0xD5A0DB00L; // base number of NSS private attributes public static final long CKA_NETSCAPE_BASE /*0x80000000L + 0x4E534350L*/ - = 0xCE534350L; + /* now known as CKM_NSS ^ */ = 0xCE534350L; // object type for NSS trust public static final long CKO_NETSCAPE_TRUST = 0xCE534353L; @@ -1180,4 +1180,14 @@ public interface PKCS11Constants { = 0xCE534355L; public static final long CKT_NETSCAPE_VALID = 0xCE53435AL; public static final long CKT_NETSCAPE_VALID_DELEGATOR = 0xCE53435BL; + + // Additional PKCS #12 PBE key derivation algorithms defined in NSS v3.29 + public static final long CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN + /* (CKM_NSS + 29) */ = 0xCE53436DL; + public static final long CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN + /* (CKM_NSS + 30) */ = 0xCE53436EL; + public static final long CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN + /* (CKM_NSS + 31) */ = 0xCE53436FL; + public static final long CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN + /* (CKM_NSS + 32) */ = 0xCE534370L; } --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c @@ -1515,6 +1515,10 @@ CK_VOID_PTR jMechParamToCKMechParamPtrSlow(JNIEnv *env, jobject jParam, case CKM_PBE_SHA1_DES3_EDE_CBC: case CKM_PBE_SHA1_DES2_EDE_CBC: case CKM_PBA_SHA1_WITH_SHA1_HMAC: + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: ckpParamPtr = jPbeParamToCKPbeParamPtr(env, jParam, ckpLength); break; case CKM_PKCS5_PBKD2: @@ -1658,13 +1662,13 @@ jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) // retrieve java values jPbeParamsClass = (*env)->FindClass(env, CLASS_PBE_PARAMS); if (jPbeParamsClass == NULL) { return NULL; } - fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pInitVector", "[C"); + fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pInitVector", "[B"); if (fieldID == NULL) { return NULL; } jInitVector = (*env)->GetObjectField(env, jParam, fieldID); fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pPassword", "[C"); if (fieldID == NULL) { return NULL; } jPassword = (*env)->GetObjectField(env, jParam, fieldID); - fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pSalt", "[C"); + fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "pSalt", "[B"); if (fieldID == NULL) { return NULL; } jSalt = (*env)->GetObjectField(env, jParam, fieldID); fieldID = (*env)->GetFieldID(env, jPbeParamsClass, "ulIteration", "J"); @@ -1680,15 +1684,15 @@ jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) // populate using java values ckParamPtr->ulIteration = jLongToCKULong(jIteration); - jCharArrayToCKCharArray(env, jInitVector, &(ckParamPtr->pInitVector), &ckTemp); + jByteArrayToCKByteArray(env, jInitVector, &(ckParamPtr->pInitVector), &ckTemp); if ((*env)->ExceptionCheck(env)) { goto cleanup; } - jCharArrayToCKCharArray(env, jPassword, &(ckParamPtr->pPassword), &(ckParamPtr->ulPasswordLen)); + jCharArrayToCKUTF8CharArray(env, jPassword, &(ckParamPtr->pPassword), &(ckParamPtr->ulPasswordLen)); if ((*env)->ExceptionCheck(env)) { goto cleanup; } - jCharArrayToCKCharArray(env, jSalt, &(ckParamPtr->pSalt), &(ckParamPtr->ulSaltLen)); + jByteArrayToCKByteArray(env, jSalt, &(ckParamPtr->pSalt), &(ckParamPtr->ulSaltLen)); if ((*env)->ExceptionCheck(env)) { goto cleanup; } @@ -1767,31 +1771,59 @@ void copyBackPBEInitializationVector(JNIEnv *env, CK_MECHANISM *ckMechanism, job } } +#define PBKD2_PARAM_SET(member, value) \ + do { \ + if(ckParamPtr->version == PARAMS) { \ + ckParamPtr->params.v1.member = value; \ + } else { \ + ckParamPtr->params.v2.member = value; \ + } \ + } while(0) + +#define PBKD2_PARAM_ADDR(member) \ + ( \ + (ckParamPtr->version == PARAMS) ? \ + (void*) &ckParamPtr->params.v1.member : \ + (void*) &ckParamPtr->params.v2.member \ + ) + /* - * converts the Java CK_PKCS5_PBKD2_PARAMS object to a CK_PKCS5_PBKD2_PARAMS + * converts a Java CK_PKCS5_PBKD2_PARAMS object to a CK_PKCS5_PBKD2_PARAMS + * pointer, or a Java CK_PKCS5_PBKD2_PARAMS2 object to a CK_PKCS5_PBKD2_PARAMS2 * pointer * - * @param env - used to call JNI funktions to get the Java classes and objects - * @param jParam - the Java CK_PKCS5_PBKD2_PARAMS object to convert + * @param env - used to call JNI functions to get the Java classes and objects + * @param jParam - the Java object to convert * @param pLength - length of the allocated memory of the returned pointer - * @return pointer to the new CK_PKCS5_PBKD2_PARAMS structure + * @return pointer to the new structure */ -CK_PKCS5_PBKD2_PARAMS_PTR +CK_VOID_PTR jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength) { - CK_PKCS5_PBKD2_PARAMS_PTR ckParamPtr; + VersionedPbkd2ParamsPtr ckParamPtr; + ParamVersion paramVersion; + CK_ULONG_PTR pUlPasswordLen; jclass jPkcs5Pbkd2ParamsClass; jfieldID fieldID; jlong jSaltSource, jIteration, jPrf; - jobject jSaltSourceData, jPrfData; + jobject jSaltSourceData, jPrfData, jPassword; if (pLength != NULL) { *pLength = 0L; } // retrieve java values - jPkcs5Pbkd2ParamsClass = (*env)->FindClass(env, CLASS_PKCS5_PBKD2_PARAMS); - if (jPkcs5Pbkd2ParamsClass == NULL) { return NULL; } + if ((jPkcs5Pbkd2ParamsClass = + (*env)->FindClass(env, CLASS_PKCS5_PBKD2_PARAMS)) != NULL + && (*env)->IsInstanceOf(env, jParam, jPkcs5Pbkd2ParamsClass)) { + paramVersion = PARAMS; + } else if ((jPkcs5Pbkd2ParamsClass = + (*env)->FindClass(env, CLASS_PKCS5_PBKD2_PARAMS2)) != NULL + && (*env)->IsInstanceOf(env, jParam, jPkcs5Pbkd2ParamsClass)) { + paramVersion = PARAMS2; + } else { + return NULL; + } fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "saltSource", "J"); if (fieldID == NULL) { return NULL; } jSaltSource = (*env)->GetLongField(env, jParam, fieldID); @@ -1807,36 +1839,60 @@ jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pL fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "pPrfData", "[B"); if (fieldID == NULL) { return NULL; } jPrfData = (*env)->GetObjectField(env, jParam, fieldID); + fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "pPassword", "[C"); + if (fieldID == NULL) { return NULL; } + jPassword = (*env)->GetObjectField(env, jParam, fieldID); - // allocate memory for CK_PKCS5_PBKD2_PARAMS pointer - ckParamPtr = calloc(1, sizeof(CK_PKCS5_PBKD2_PARAMS)); + // allocate memory for VersionedPbkd2Params and store the structure version + ckParamPtr = calloc(1, sizeof(VersionedPbkd2Params)); if (ckParamPtr == NULL) { throwOutOfMemoryError(env, 0); return NULL; } + ckParamPtr->version = paramVersion; // populate using java values - ckParamPtr->saltSource = jLongToCKULong(jSaltSource); - jByteArrayToCKByteArray(env, jSaltSourceData, (CK_BYTE_PTR *) - &(ckParamPtr->pSaltSourceData), &(ckParamPtr->ulSaltSourceDataLen)); + PBKD2_PARAM_SET(saltSource, jLongToCKULong(jSaltSource)); + jByteArrayToCKByteArray(env, jSaltSourceData, + (CK_BYTE_PTR *) PBKD2_PARAM_ADDR(pSaltSourceData), + PBKD2_PARAM_ADDR(ulSaltSourceDataLen)); if ((*env)->ExceptionCheck(env)) { goto cleanup; } - ckParamPtr->iterations = jLongToCKULong(jIteration); - ckParamPtr->prf = jLongToCKULong(jPrf); - jByteArrayToCKByteArray(env, jPrfData, (CK_BYTE_PTR *) - &(ckParamPtr->pPrfData), &(ckParamPtr->ulPrfDataLen)); + PBKD2_PARAM_SET(iterations, jLongToCKULong(jIteration)); + PBKD2_PARAM_SET(prf, jLongToCKULong(jPrf)); + jByteArrayToCKByteArray(env, jPrfData, + (CK_BYTE_PTR *) PBKD2_PARAM_ADDR(pPrfData), + PBKD2_PARAM_ADDR(ulPrfDataLen)); + if ((*env)->ExceptionCheck(env)) { + goto cleanup; + } + if (ckParamPtr->version == PARAMS) { + pUlPasswordLen = calloc(1, sizeof(CK_ULONG)); + if (pUlPasswordLen == NULL) { + throwOutOfMemoryError(env, 0); + goto cleanup; + } + ckParamPtr->params.v1.ulPasswordLen = pUlPasswordLen; + } else { + pUlPasswordLen = &ckParamPtr->params.v2.ulPasswordLen; + } + jCharArrayToCKUTF8CharArray(env, jPassword, + (CK_CHAR_PTR *) PBKD2_PARAM_ADDR(pPassword), + pUlPasswordLen); if ((*env)->ExceptionCheck(env)) { goto cleanup; } if (pLength != NULL) { - *pLength = sizeof(CK_PKCS5_PBKD2_PARAMS); + *pLength = (ckParamPtr->version == PARAMS ? + sizeof(ckParamPtr->params.v1) : + sizeof(ckParamPtr->params.v2)); } + // VersionedPbkd2ParamsPtr is equivalent to CK_PKCS5_PBKD2_PARAMS[2]_PTR return ckParamPtr; cleanup: - free(ckParamPtr->pSaltSourceData); - free(ckParamPtr->pPrfData); + FREE_VERSIONED_PBKD2_MEMBERS(ckParamPtr); free(ckParamPtr); return NULL; --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c @@ -410,11 +410,27 @@ void freeCKMechanismPtr(CK_MECHANISM_PTR mechPtr) { case CKM_CAMELLIA_CTR: // params do not contain pointers break; + case CKM_PKCS5_PBKD2: + // get the versioned structure from behind memory + TRACE0(((VersionedPbkd2ParamsPtr)tmp)->version == PARAMS ? + "[ CK_PKCS5_PBKD2_PARAMS ]\n" : + "[ CK_PKCS5_PBKD2_PARAMS2 ]\n"); + FREE_VERSIONED_PBKD2_MEMBERS((VersionedPbkd2ParamsPtr)tmp); + break; + case CKM_PBA_SHA1_WITH_SHA1_HMAC: + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + free(((CK_PBE_PARAMS_PTR)tmp)->pInitVector); + free(((CK_PBE_PARAMS_PTR)tmp)->pPassword); + free(((CK_PBE_PARAMS_PTR)tmp)->pSalt); + break; default: // currently unsupported mechs by SunPKCS11 provider // CKM_RSA_PKCS_OAEP, CKM_ECMQV_DERIVE, // CKM_X9_42_*, CKM_KEA_DERIVE, CKM_RC2_*, CKM_RC5_*, - // CKM_SKIPJACK_*, CKM_KEY_WRAP_SET_OAEP, CKM_PKCS5_PBKD2, + // CKM_SKIPJACK_*, CKM_KEY_WRAP_SET_OAEP, // PBE mechs, WTLS mechs, CMS mechs, // CKM_EXTRACT_KEY_FROM_KEY, CKM_OTP, CKM_KIP, // CKM_DSA_PARAMETER_GEN?, CKM_GOSTR3410_* @@ -517,12 +533,11 @@ void jBooleanArrayToCKBBoolArray(JNIEnv *env, const jbooleanArray jArray, CK_BBO jboolean* jpTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jpTemp = (jboolean*) calloc(*ckpLength, sizeof(jboolean)); if (jpTemp == NULL) { throwOutOfMemoryError(env, 0); @@ -559,12 +574,11 @@ void jByteArrayToCKByteArray(JNIEnv *env, const jbyteArray jArray, CK_BYTE_PTR * jbyte* jpTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jpTemp = (jbyte*) calloc(*ckpLength, sizeof(jbyte)); if (jpTemp == NULL) { throwOutOfMemoryError(env, 0); @@ -606,12 +620,11 @@ void jLongArrayToCKULongArray(JNIEnv *env, const jlongArray jArray, CK_ULONG_PTR jlong* jTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jTemp = (jlong*) calloc(*ckpLength, sizeof(jlong)); if (jTemp == NULL) { throwOutOfMemoryError(env, 0); @@ -648,12 +661,11 @@ void jCharArrayToCKCharArray(JNIEnv *env, const jcharArray jArray, CK_CHAR_PTR * jchar* jpTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jpTemp = (jchar*) calloc(*ckpLength, sizeof(jchar)); if (jpTemp == NULL) { throwOutOfMemoryError(env, 0); @@ -690,12 +702,11 @@ void jCharArrayToCKUTF8CharArray(JNIEnv *env, const jcharArray jArray, CK_UTF8CH jchar* jTemp; CK_ULONG i; - if(jArray == NULL) { + *ckpLength = jArray == NULL ? 0L : (*env)->GetArrayLength(env, jArray); + if(*ckpLength == 0L) { *ckpArray = NULL_PTR; - *ckpLength = 0L; return; } - *ckpLength = (*env)->GetArrayLength(env, jArray); jTemp = (jchar*) calloc(*ckpLength, sizeof(jchar)); if (jTemp == NULL) { throwOutOfMemoryError(env, 0); --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/pkcs11wrapper.h @@ -68,6 +68,7 @@ /* extra PKCS#11 constants not in the standard include files */ #define CKA_NETSCAPE_BASE (0x80000000 + 0x4E534350) +/* ^ now known as CKM_NSS (CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS) */ #define CKA_NETSCAPE_TRUST_BASE (CKA_NETSCAPE_BASE + 0x2000) #define CKA_NETSCAPE_TRUST_SERVER_AUTH (CKA_NETSCAPE_TRUST_BASE + 8) #define CKA_NETSCAPE_TRUST_CLIENT_AUTH (CKA_NETSCAPE_TRUST_BASE + 9) @@ -76,6 +77,12 @@ #define CKA_NETSCAPE_DB 0xD5A0DB00 #define CKM_NSS_TLS_PRF_GENERAL 0x80000373 +/* additional PKCS #12 PBE key derivation algorithms defined in NSS v3.29 */ +#define CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 29) +#define CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 30) +#define CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 31) +#define CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN (CKA_NETSCAPE_BASE + 32) + /* Define the PKCS#11 functions to include and exclude. Reduces the size @@ -265,6 +272,7 @@ void printDebug(const char *format, ...); #define CLASS_PBE_PARAMS "sun/security/pkcs11/wrapper/CK_PBE_PARAMS" #define PBE_INIT_VECTOR_SIZE 8 #define CLASS_PKCS5_PBKD2_PARAMS "sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS" +#define CLASS_PKCS5_PBKD2_PARAMS2 "sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS2" #define CLASS_EXTRACT_PARAMS "sun/security/pkcs11/wrapper/CK_EXTRACT_PARAMS" #define CLASS_ECDH1_DERIVE_PARAMS "sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS" @@ -378,7 +386,7 @@ CK_VOID_PTR jMechParamToCKMechParamPtr(JNIEnv *env, jobject jParam, CK_MECHANISM CK_RSA_PKCS_OAEP_PARAMS_PTR jRsaPkcsOaepParamToCKRsaPkcsOaepParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_PBE_PARAMS_PTR jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); -CK_PKCS5_PBKD2_PARAMS_PTR jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); +CK_VOID_PTR jPkcs5Pbkd2ParamToCKPkcs5Pbkd2ParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR jSsl3MasterKeyDeriveParamToCKSsl3MasterKeyDeriveParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_SSL3_KEY_MAT_PARAMS_PTR jSsl3KeyMatParamToCKSsl3KeyMatParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_KEY_DERIVATION_STRING_DATA jKeyDerivationStringDataToCKKeyDerivationStringData(JNIEnv *env, jobject jParam); @@ -388,6 +396,31 @@ CK_ECDH2_DERIVE_PARAMS_PTR jEcdh2DeriveParamToCKEcdh2DeriveParamPtr(JNIEnv *env, CK_X9_42_DH1_DERIVE_PARAMS_PTR jX942Dh1DeriveParamToCKX942Dh1DeriveParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); CK_X9_42_DH2_DERIVE_PARAMS_PTR jX942Dh2DeriveParamToCKX942Dh2DeriveParamPtr(JNIEnv *env, jobject jParam, CK_ULONG* pLength); +/* handling of CK_PKCS5_PBKD2_PARAMS and CK_PKCS5_PBKD2_PARAMS2 */ +typedef enum {PARAMS=0, PARAMS2} ParamVersion; + +typedef struct { + union { + CK_PKCS5_PBKD2_PARAMS v1; + CK_PKCS5_PBKD2_PARAMS2 v2; + } params; + ParamVersion version; +} VersionedPbkd2Params, *VersionedPbkd2ParamsPtr; + +#define FREE_VERSIONED_PBKD2_MEMBERS(verParamsPtr) \ + do { \ + if ((verParamsPtr)->version == PARAMS) { \ + free((verParamsPtr)->params.v1.pSaltSourceData); \ + free((verParamsPtr)->params.v1.pPrfData); \ + free((verParamsPtr)->params.v1.pPassword); \ + free((verParamsPtr)->params.v1.ulPasswordLen); \ + } else { \ + free((verParamsPtr)->params.v2.pSaltSourceData); \ + free((verParamsPtr)->params.v2.pPrfData); \ + free((verParamsPtr)->params.v2.pPassword); \ + } \ + } while(0) + /* functions to copy the returned values inside CK-mechanism back to Java object */ void copyBackPBEInitializationVector(JNIEnv *env, CK_MECHANISM *ckMechanism, jobject jMechanism); --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java @@ -38,6 +38,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import jdk.internal.access.SharedSecrets; import sun.security.ec.ed.EdDSAAlgorithmParameters; import sun.security.ec.ed.EdDSAKeyFactory; import sun.security.ec.ed.EdDSAKeyPairGenerator; @@ -56,6 +57,10 @@ public final class SunEC extends Provider { private static final long serialVersionUID = -2279741672933606418L; + private static final boolean systemFipsEnabled = + SharedSecrets.getJavaSecuritySystemConfiguratorAccess() + .isSystemFipsEnabled(); + private static class ProviderServiceA extends ProviderService { ProviderServiceA(Provider p, String type, String algo, String cn, HashMap<String, String> attrs) { @@ -249,7 +254,7 @@ public final class SunEC extends Provider { putXDHEntries(); putEdDSAEntries(); - + if (!systemFipsEnabled) { /* * Signature engines */ @@ -329,6 +334,7 @@ public final class SunEC extends Provider { putService(new ProviderService(this, "KeyAgreement", "ECDH", "sun.security.ec.ECDHKeyAgreement", null, ATTRS)); } + } private void putXDHEntries() { @@ -344,6 +350,7 @@ public final class SunEC extends Provider { "X448", "sun.security.ec.XDHKeyFactory.X448", ATTRS)); + if (!systemFipsEnabled) { putService(new ProviderService(this, "KeyPairGenerator", "XDH", "sun.security.ec.XDHKeyPairGenerator", null, ATTRS)); putService(new ProviderServiceA(this, "KeyPairGenerator", @@ -362,6 +369,7 @@ public final class SunEC extends Provider { "X448", "sun.security.ec.XDHKeyAgreement.X448", ATTRS)); } + } private void putEdDSAEntries() { @@ -375,6 +383,7 @@ public final class SunEC extends Provider { putService(new ProviderServiceA(this, "KeyFactory", "Ed448", "sun.security.ec.ed.EdDSAKeyFactory.Ed448", ATTRS)); + if (!systemFipsEnabled) { putService(new ProviderService(this, "KeyPairGenerator", "EdDSA", "sun.security.ec.ed.EdDSAKeyPairGenerator", null, ATTRS)); putService(new ProviderServiceA(this, "KeyPairGenerator", @@ -390,6 +399,7 @@ public final class SunEC extends Provider { "Ed25519", "sun.security.ec.ed.EdDSASignature.Ed25519", ATTRS)); putService(new ProviderServiceA(this, "Signature", "Ed448", "sun.security.ec.ed.EdDSASignature.Ed448", ATTRS)); + } } } --- /dev/null +++ b/test/jdk/sun/security/pkcs11/Cipher/PBECipher.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Map; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.interfaces.PBEKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +/* + * @test + * @bug 9999999 + * @summary test password based encryption on SunPKCS11's Cipher service + * @requires (jdk.version.major >= 8) + * @library /test/lib .. + * @run main/othervm/timeout=30 PBECipher + */ + +public final class PBECipher { + public static void main(String[] args) throws Exception { + java.security.Security.getProviders(); + PBECipher2.main(args); + } +} + +final class PBECipher2 extends PKCS11Test { + private static final char[] password = "123456".toCharArray(); + private static final byte[] salt = "abcdefgh".getBytes(); + private static final byte[] iv = new byte[16]; + private static final int iterations = 1000; + private static final String plainText = "This is a know plain text!"; + private static final String sep = + "========================================================================="; + + private static enum Configuration { + // Provide salt and iterations through a PBEParameterSpec instance + PBEParameterSpec, + + // Provide salt and iterations through a AlgorithmParameters instance + AlgorithmParameters, + + // Provide salt and iterations through an anonymous class implementing + // the javax.crypto.interfaces.PBEKey interface + AnonymousPBEKey, + } + + private static Provider sunJCE = Security.getProvider("SunJCE"); + + // Generated with SunJCE + private static final Map<String, BigInteger> assertionData = Map.of( + "PBEWithHmacSHA1AndAES_128", new BigInteger("8eebe98a580fb09d026" + + "dbfe60b3733b079e0de9ea7b0b1ccba011a1652d1e257", 16), + "PBEWithHmacSHA224AndAES_128", new BigInteger("1cbabdeb5d483af4a" + + "841942f4b1095b7d6f60e46fabfd2609c015adc38cc227", 16), + "PBEWithHmacSHA256AndAES_128", new BigInteger("4d82f6591df3508d2" + + "4531f06cdc4f90f4bdab7aeb07fbb57a3712e999d5b6f59", 16), + "PBEWithHmacSHA384AndAES_128", new BigInteger("3a0ed0959d51f40b9" + + "ba9f506a5277f430521f2fbe1ba94bae368835f221b6cb9", 16), + "PBEWithHmacSHA512AndAES_128", new BigInteger("1388287a446009309" + + "1418f4eca3ba1735b1fa025423d74ced36ce578d8ebf9da", 16), + "PBEWithHmacSHA1AndAES_256", new BigInteger("80f8208daab27ed02dd" + + "8a354ef6f23ff7813c84dd1c8a1b081d6f4dee27182a2", 16), + "PBEWithHmacSHA224AndAES_256", new BigInteger("7e3b9ce20aec2e52f" + + "f6c781602d4f79a55a88495b5217f1e22e1a068268e6247", 16), + "PBEWithHmacSHA256AndAES_256", new BigInteger("9d6a8b6a351dfd0dd" + + "9e9f45924b2860dca7719c4c07e207a64ebc1acd16cc157", 16), + "PBEWithHmacSHA384AndAES_256", new BigInteger("6f1b386cee3a8e2d9" + + "8c2e81828da0467dec8b989d22258efeab5932580d01d53", 16), + "PBEWithHmacSHA512AndAES_256", new BigInteger("30aaa346b2edd394f" + + "50916187876ac32f1287b19d55c5eea6f7ef9b84aaf291e", 16) + ); + + private static final class NoRandom extends SecureRandom { + @Override + public void nextBytes(byte[] bytes) { + return; + } + } + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + for (Configuration conf : Configuration.values()) { + testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_128", conf); + testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_128", conf); + testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_128", conf); + testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_128", conf); + testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_128", conf); + testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_256", conf); + testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_256", conf); + testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_256", conf); + testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_256", conf); + testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", conf); + } + System.out.println("TEST PASS - OK"); + } + + private void testWith(Provider sunPKCS11, String algorithm, + Configuration conf) throws Exception { + System.out.println(sep + System.lineSeparator() + algorithm + + " (with " + conf.name() + ")"); + + Cipher pbeCipher = getCipher(sunPKCS11, algorithm, conf); + BigInteger cipherText = new BigInteger(1, pbeCipher.doFinal( + plainText.getBytes())); + printByteArray("Cipher Text", cipherText); + + BigInteger expectedCipherText = null; + if (sunJCE != null) { + Cipher c = getCipher(sunJCE, algorithm, conf); + if (c != null) { + expectedCipherText = new BigInteger(1, c.doFinal( + plainText.getBytes())); + } else { + // Move to assertionData as it's unlikely that any of + // the algorithms are available. + sunJCE = null; + } + } + if (expectedCipherText == null) { + // If SunJCE or the algorithm are not available, assertionData + // is used instead. + expectedCipherText = assertionData.get(algorithm); + } + + if (!cipherText.equals(expectedCipherText)) { + printByteArray("Expected Cipher Text", expectedCipherText); + throw new Exception("Expected Cipher Text did not match"); + } + } + + private Cipher getCipher(Provider p, String algorithm, + Configuration conf) throws Exception { + Cipher pbeCipher = null; + try { + pbeCipher = Cipher.getInstance(algorithm, p); + } catch (NoSuchAlgorithmException e) { + return null; + } + switch (conf) { + case PBEParameterSpec, AlgorithmParameters -> { + SecretKey key = getPasswordOnlyPBEKey(); + PBEParameterSpec paramSpec = new PBEParameterSpec( + salt, iterations, new IvParameterSpec(iv)); + switch (conf) { + case PBEParameterSpec -> { + pbeCipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); + } + case AlgorithmParameters -> { + AlgorithmParameters algoParams = + AlgorithmParameters.getInstance("PBES2"); + algoParams.init(paramSpec); + pbeCipher.init(Cipher.ENCRYPT_MODE, key, algoParams); + } + } + } + case AnonymousPBEKey -> { + SecretKey key = getPasswordSaltIterationsPBEKey(); + pbeCipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom()); + } + } + return pbeCipher; + } + + private static SecretKey getPasswordOnlyPBEKey() throws Exception { + PBEKeySpec keySpec = new PBEKeySpec(password); + SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); + SecretKey skey = skFac.generateSecret(keySpec); + keySpec.clearPassword(); + return skey; + } + + private static SecretKey getPasswordSaltIterationsPBEKey() { + return new PBEKey() { + public byte[] getSalt() { return salt.clone(); } + public int getIterationCount() { return iterations; } + public String getAlgorithm() { return "PBE"; } + public String getFormat() { return "RAW"; } + public char[] getPassword() { return null; } // unused in PBE Cipher + public byte[] getEncoded() { + byte[] passwdBytes = new byte[password.length]; + for (int i = 0; i < password.length; i++) + passwdBytes[i] = (byte) (password[i] & 0x7f); + return passwdBytes; + } + }; + } + + private static void printByteArray(String title, BigInteger b) { + String repr = (b == null) ? "buffer is null" : b.toString(16); + System.out.println(title + ": " + repr + System.lineSeparator()); + } + + public static void main(String[] args) throws Exception { + PBECipher2 test = new PBECipher2(); + Provider p = Security.getProvider("SunPKCS11-NSS-FIPS"); + if (p != null) { + test.main(p); + } else { + main(test); + } + } +} --- /dev/null +++ b/test/jdk/sun/security/pkcs11/KeyStore/ImportKeyToP12.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.Provider; +import java.security.Security; + +import javax.crypto.spec.SecretKeySpec; + +/* + * @test + * @bug 9999999 + * @summary test SunPKCS11's password based privacy and integrity + * applied to PKCS#12 keystores + * @requires (jdk.version.major >= 8) + * @library /test/lib .. + * @modules java.base/sun.security.util + * @run main/othervm/timeout=30 -Dcom.suse.fips=false -DNO_DEFAULT=true ImportKeyToP12 + */ + +public final class ImportKeyToP12 { + public static void main(String[] args) throws Exception { + java.security.Security.getProviders(); + ImportKeyToP122.main(args); + } +} + +final class ImportKeyToP122 extends PKCS11Test { + private static final String alias = "alias"; + private static final char[] password = "123456".toCharArray(); + private static final Key key = new SecretKeySpec(new byte[] { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }, "AES"); + private static final String[] pbeCipherAlgs = new String[] { + "PBEWithHmacSHA1AndAES_128", "PBEWithHmacSHA224AndAES_128", + "PBEWithHmacSHA256AndAES_128", "PBEWithHmacSHA384AndAES_128", + "PBEWithHmacSHA512AndAES_128", "PBEWithHmacSHA1AndAES_256", + "PBEWithHmacSHA224AndAES_256", "PBEWithHmacSHA256AndAES_256", + "PBEWithHmacSHA384AndAES_256", "PBEWithHmacSHA512AndAES_256" + }; + private static final String[] pbeMacAlgs = new String[] { + "HmacPBESHA1", "HmacPBESHA224", "HmacPBESHA256", + "HmacPBESHA384", "HmacPBESHA512" + }; + private static final KeyStore p12; + private static final String sep = + "========================================================================="; + + static { + KeyStore tP12 = null; + try { + tP12 = KeyStore.getInstance("PKCS12"); + } catch (KeyStoreException e) {} + p12 = tP12; + } + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + // Test all privacy PBE algorithms with an integrity algorithm fixed + for (String pbeCipherAlg : pbeCipherAlgs) { + testWith(sunPKCS11, pbeCipherAlg, pbeMacAlgs[0]); + } + // Test all integrity PBE algorithms with a privacy algorithm fixed + for (String pbeMacAlg : pbeMacAlgs) { + testWith(sunPKCS11, pbeCipherAlgs[0], pbeMacAlg); + } + System.out.println("TEST PASS - OK"); + } + + /* + * Consistency test: 1) store a secret key in a PKCS#12 keystore using + * PBE algorithms from SunPKCS11 and, 2) read the secret key from the + * PKCS#12 keystore using PBE algorithms from other security providers + * such as SunJCE. + */ + private void testWith(Provider sunPKCS11, String pbeCipherAlg, + String pbeMacAlg) throws Exception { + System.out.println(sep + System.lineSeparator() + + "Cipher PBE: " + pbeCipherAlg + System.lineSeparator() + + "Mac PBE: " + pbeMacAlg); + + System.setProperty("keystore.pkcs12.macAlgorithm", pbeMacAlg); + System.setProperty("keystore.pkcs12.keyProtectionAlgorithm", + pbeCipherAlg); + + // Create an empty PKCS#12 keystore + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + p12.load(null, password); + + // Use PBE privacy and integrity algorithms from SunPKCS11 to store + // the secret key + Security.insertProviderAt(sunPKCS11, 1); + p12.setKeyEntry(alias, key, password, null); + p12.store(baos, password); + + // Use PBE privacy and integrity algorithms from other security + // providers, such as SunJCE, to read the secret key + Security.removeProvider(sunPKCS11.getName()); + p12.load(new ByteArrayInputStream(baos.toByteArray()), password); + Key k = p12.getKey(alias, password); + + if (!MessageDigest.isEqual(key.getEncoded(), k.getEncoded())) { + throw new Exception("Keys differ. Consistency check failed."); + } + System.out.println("Secret key import successful" + System.lineSeparator() + sep); + } + + public static void main(String[] args) throws Exception { + main(new ImportKeyToP122()); + } +} --- /dev/null +++ b/test/jdk/sun/security/pkcs11/Mac/PBAMac.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.Map; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.interfaces.PBEKey; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +/* + * @test + * @bug 9999999 + * @summary test password based authentication on SunPKCS11's Mac service + * @requires (jdk.version.major >= 8) + * @library /test/lib .. + * @run main/othervm/timeout=30 PBAMac + */ + +public final class PBAMac { + public static void main(String[] args) throws Exception { + java.security.Security.getProviders(); + PBAMac2.main(args); + } +} + +final class PBAMac2 extends PKCS11Test { + private static final char[] password = "123456".toCharArray(); + private static final byte[] salt = "abcdefgh".getBytes(); + private static final int iterations = 1000; + private static final String plainText = "This is a know plain text!"; + private static final String sep = + "========================================================================="; + + private static enum Configuration { + // Provide salt & iterations through a PBEParameterSpec instance + PBEParameterSpec, + + // Provide salt & iterations through an anonymous class implementing + // the javax.crypto.interfaces.PBEKey interface + AnonymousPBEKey, + } + + // Generated with SunJCE + private static final Map<String, BigInteger> assertionData = Map.of( + "HmacPBESHA1", new BigInteger("febd26da5d63ce819770a2af1fc2857e" + + "e2c9c41c", 16), + "HmacPBESHA224", new BigInteger("aa6a3a1c35a4b266fea62d1a871508" + + "bd45f8ec326bcf16e09699063", 16), + "HmacPBESHA256", new BigInteger("af4d71121fd4e9d52eb42944d99b77" + + "8ff64376fcf6af8d1dca3ec688dfada5c8", 16), + "HmacPBESHA384", new BigInteger("5d6d37764205985ffca7e4a6222752" + + "a8bbd0520858da08ecafdc57e6246894675e375b9ba084f9ce7142" + + "35f202cc3452", 16), + "HmacPBESHA512", new BigInteger("f586c2006cc2de73fd5743e5cca701" + + "c942d3741a7a54a2a649ea36898996cf3c483f2d734179b47751db" + + "e8373c980b4072136d2e2810f4e7276024a3e9081cc1", 16) + ); + + private static Provider sunJCE = Security.getProvider("SunJCE"); + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + for (Configuration conf : Configuration.values()) { + testWith(sunPKCS11, "HmacPBESHA1", conf); + testWith(sunPKCS11, "HmacPBESHA224", conf); + testWith(sunPKCS11, "HmacPBESHA256", conf); + testWith(sunPKCS11, "HmacPBESHA384", conf); + testWith(sunPKCS11, "HmacPBESHA512", conf); + } + System.out.println("TEST PASS - OK"); + } + + private void testWith(Provider sunPKCS11, String algorithm, + Configuration conf) throws Exception { + System.out.println(sep + System.lineSeparator() + algorithm + + " (with " + conf.name() + ")"); + + BigInteger macResult = computeMac(sunPKCS11, algorithm, conf); + printByteArray("HMAC Result", macResult); + + BigInteger expectedMacResult = computeExpectedMac(algorithm, conf); + + if (!macResult.equals(expectedMacResult)) { + printByteArray("Expected HMAC Result", expectedMacResult); + throw new Exception("Expected HMAC Result did not match"); + } + } + + private BigInteger computeMac(Provider p, String algorithm, + Configuration conf) throws Exception { + Mac pbaMac; + try { + pbaMac = Mac.getInstance(algorithm, p); + } catch (NoSuchAlgorithmException e) { + return null; + } + switch (conf) { + case PBEParameterSpec -> { + SecretKey key = getPasswordOnlyPBEKey(); + pbaMac.init(key, new PBEParameterSpec(salt, iterations)); + } + case AnonymousPBEKey -> { + SecretKey key = getPasswordSaltIterationsPBEKey(); + pbaMac.init(key); + } + } + return new BigInteger(1, pbaMac.doFinal(plainText.getBytes())); + } + + private BigInteger computeExpectedMac(String algorithm, Configuration conf) + throws Exception { + if (sunJCE != null) { + BigInteger macResult = computeMac(sunJCE, algorithm, conf); + if (macResult != null) { + return macResult; + } + // Move to assertionData as it's unlikely that any of + // the algorithms are available. + sunJCE = null; + } + // If SunJCE or the algorithm are not available, assertionData + // is used instead. + return assertionData.get(algorithm); + } + + private static SecretKey getPasswordOnlyPBEKey() throws Exception { + PBEKeySpec keySpec = new PBEKeySpec(password); + SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); + SecretKey skey = skFac.generateSecret(keySpec); + keySpec.clearPassword(); + return skey; + } + + private static SecretKey getPasswordSaltIterationsPBEKey() { + return new PBEKey() { + public byte[] getSalt() { return salt.clone(); } + public int getIterationCount() { return iterations; } + public String getAlgorithm() { return "PBE"; } + public String getFormat() { return "RAW"; } + public char[] getPassword() { return password.clone(); } + public byte[] getEncoded() { return null; } // unused in PBA Mac + }; + } + + private static void printByteArray(String title, BigInteger b) { + String repr = (b == null) ? "buffer is null" : b.toString(16); + System.out.println(title + ": " + repr + System.lineSeparator()); + } + + public static void main(String[] args) throws Exception { + PBAMac2 test = new PBAMac2(); + Provider p = Security.getProvider("SunPKCS11-NSS-FIPS"); + if (p != null) { + test.main(p); + } else { + main(test); + } + } +} --- /dev/null +++ b/test/jdk/sun/security/pkcs11/SecretKeyFactory/TestPBKD.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +/* + * @test + * @bug 9999999 + * @summary test key derivation on SunPKCS11's SecretKeyFactory service + * @requires (jdk.version.major >= 8) + * @library /test/lib .. + * @modules java.base/com.sun.crypto.provider:open + * @run main/othervm/timeout=30 TestPBKD + */ + +public final class TestPBKD { + public static void main(String[] args) throws Exception { + java.security.Security.getProviders(); + TestPBKD2.main(args); + } +} + +final class TestPBKD2 extends PKCS11Test { + private static final char[] password = "123456".toCharArray(); + private static final byte[] salt = "abcdefgh".getBytes(); + private static final int iterations = 1000; + private static final String sep = + "========================================================================="; + + private static Provider sunJCE = Security.getProvider("SunJCE"); + + // Generated with SunJCE + private static final Map<String, BigInteger> assertionData = + new HashMap<>() {{ + put("HmacPBESHA1", new BigInteger("5f7d1c360d1703cede76f47db" + + "2fa3facc62e7694", 16)); + put("HmacPBESHA224", new BigInteger("289563f799b708f522ab2a3" + + "8d283d0afa8fc1d3d227fcb9236c3a035", 16)); + put("HmacPBESHA256", new BigInteger("888defcf4ef37eb0647014a" + + "d172dd6fa3b3e9d024b962dba47608eea9b9c4b79", 16)); + put("HmacPBESHA384", new BigInteger("f5464b34253fadab8838d0d" + + "b11980c1787a99bf6f6304f2d8c942e30bada523494f9d5a0f3" + + "741e411de21add8b5718a8", 16)); + put("HmacPBESHA512", new BigInteger("18ae94337b132c68c611bc2" + + "e723ac24dcd44a46d900dae2dd6170380d4c34f90fef7bdeb5f" + + "6fddeb0d2230003e329b7a7eefcd35810d364ba95d31b68bb61" + + "e52", 16)); + put("PBEWithHmacSHA1AndAES_128", new BigInteger("fdb3dcc2e81" + + "244d4d56bf7ec8dd61dd7", 16)); + put("PBEWithHmacSHA224AndAES_128", new BigInteger("5ef9e5c6f" + + "df7c355f3b424233a9f24c2", 16)); + put("PBEWithHmacSHA256AndAES_128", new BigInteger("c5af597b0" + + "1b4f6baac8f62ff6f22bfb1", 16)); + put("PBEWithHmacSHA384AndAES_128", new BigInteger("c3208ebc5" + + "d6db88858988ec00153847d", 16)); + put("PBEWithHmacSHA512AndAES_128", new BigInteger("b27e8f7fb" + + "6a4bd5ebea892cd9a7f5043", 16)); + put("PBEWithHmacSHA1AndAES_256", new BigInteger("fdb3dcc2e81" + + "244d4d56bf7ec8dd61dd78a1b6fb3ad11d9ebd7f62027a2ccde" + + "98", 16)); + put("PBEWithHmacSHA224AndAES_256", new BigInteger("5ef9e5c6f" + + "df7c355f3b424233a9f24c2c9c41793cb0948b8ea3aac240b8d" + + "f64d", 16)); + put("PBEWithHmacSHA256AndAES_256", new BigInteger("c5af597b0" + + "1b4f6baac8f62ff6f22bfb1f319c3278c8b31cc616294716d4e" + + "ab08", 16)); + put("PBEWithHmacSHA384AndAES_256", new BigInteger("c3208ebc5" + + "d6db88858988ec00153847d5b1b7a8723640a022dc332bcaefe" + + "b356", 16)); + put("PBEWithHmacSHA512AndAES_256", new BigInteger("b27e8f7fb" + + "6a4bd5ebea892cd9a7f5043cefff9c38b07e599721e8d116189" + + "5482", 16)); + put("PBKDF2WithHmacSHA1", new BigInteger("fdb3dcc2e81244d4d5" + + "6bf7ec8dd61dd78a1b6fb3ad11d9ebd7f62027a2cc", 16)); + put("PBKDF2WithHmacSHA224", new BigInteger("5ef9e5c6fdf7c355" + + "f3b424233a9f24c2c9c41793cb0948b8ea3aac240b8df64d1a0" + + "736ec1c69eef1c7b2", 16)); + put("PBKDF2WithHmacSHA256", new BigInteger("c5af597b01b4f6ba" + + "ac8f62ff6f22bfb1f319c3278c8b31cc616294716d4eab080b9" + + "add9db34a42ceb2fea8d27adc00f4", 16)); + put("PBKDF2WithHmacSHA384", new BigInteger("c3208ebc5d6db888" + + "58988ec00153847d5b1b7a8723640a022dc332bcaefeb356995" + + "d076a949d35c42c7e1e1ca936c12f8dc918e497edf279a522b7" + + "c99580e2613846b3919af637da", 16)); + put("PBKDF2WithHmacSHA512", new BigInteger("b27e8f7fb6a4bd5e" + + "bea892cd9a7f5043cefff9c38b07e599721e8d1161895482da2" + + "55746844cc1030be37ba1969df10ff59554d1ac5468fa9b7297" + + "7bb7fd52103a0a7b488cdb8957616c3e23a16bca92120982180" + + "c6c11a4f14649b50d0ade3a", 16)); + }}; + + static interface AssertData { + BigInteger derive(String pbAlgo, PBEKeySpec keySpec) throws Exception; + } + + static final class P12PBKDAssertData implements AssertData { + private final int outLen; + private final String kdfAlgo; + private final int blockLen; + + P12PBKDAssertData(int outLen, String kdfAlgo, int blockLen) { + this.outLen = outLen; + this.kdfAlgo = kdfAlgo; + this.blockLen = blockLen; + } + + @Override + public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) + throws Exception { + // Since we need to access an internal SunJCE API, we use reflection + Class<?> PKCS12PBECipherCore = Class.forName( + "com.sun.crypto.provider.PKCS12PBECipherCore"); + + Field macKeyField = PKCS12PBECipherCore.getDeclaredField("MAC_KEY"); + macKeyField.setAccessible(true); + int MAC_KEY = (int) macKeyField.get(null); + + Method deriveMethod = PKCS12PBECipherCore.getDeclaredMethod( + "derive", char[].class, byte[].class, int.class, + int.class, int.class, String.class, int.class); + deriveMethod.setAccessible(true); + + return new BigInteger(1, (byte[]) deriveMethod.invoke(null, + keySpec.getPassword(), keySpec.getSalt(), + keySpec.getIterationCount(), this.outLen, + MAC_KEY, this.kdfAlgo, this.blockLen)); + } + } + + static final class PBKD2AssertData implements AssertData { + private final String kdfAlgo; + private final int keyLen; + + PBKD2AssertData(String kdfAlgo, int keyLen) { + // Key length is pinned by the algorithm name (not kdfAlgo, + // but the algorithm under test: PBEWithHmacSHA*AndAES_*) + this.kdfAlgo = kdfAlgo; + this.keyLen = keyLen; + } + + PBKD2AssertData(String kdfAlgo) { + // Key length is variable for the algorithm under test + // (kdfAlgo is the algorithm under test: PBKDF2WithHmacSHA*) + this(kdfAlgo, -1); + } + + @Override + public BigInteger derive(String pbAlgo, PBEKeySpec keySpec) + throws Exception { + if (this.keyLen != -1) { + keySpec = new PBEKeySpec( + keySpec.getPassword(), keySpec.getSalt(), + keySpec.getIterationCount(), this.keyLen); + } + if (sunJCE != null) { + try { + return new BigInteger(1, SecretKeyFactory.getInstance( + this.kdfAlgo, sunJCE).generateSecret(keySpec) + .getEncoded()); + } catch (NoSuchAlgorithmException e) { + // Move to assertionData as it's unlikely that any of + // the algorithms are available. + sunJCE = null; + } + } + // If SunJCE or the algorithm are not available, assertionData + // is used instead. + return assertionData.get(pbAlgo); + } + } + + public void main(Provider sunPKCS11) throws Exception { + System.out.println("SunPKCS11: " + sunPKCS11.getName()); + testWith(sunPKCS11, "HmacPBESHA1", + new P12PBKDAssertData(20, "SHA-1", 64)); + testWith(sunPKCS11, "HmacPBESHA224", + new P12PBKDAssertData(28, "SHA-224", 64)); + testWith(sunPKCS11, "HmacPBESHA256", + new P12PBKDAssertData(32, "SHA-256", 64)); + testWith(sunPKCS11, "HmacPBESHA384", + new P12PBKDAssertData(48, "SHA-384", 128)); + testWith(sunPKCS11, "HmacPBESHA512", + new P12PBKDAssertData(64, "SHA-512", 128)); + + testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA1", 128)); + testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA224", 128)); + testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA256", 128)); + testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA384", 128)); + testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_128", + new PBKD2AssertData("PBKDF2WithHmacSHA512", 128)); + testWith(sunPKCS11, "PBEWithHmacSHA1AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA1", 256)); + testWith(sunPKCS11, "PBEWithHmacSHA224AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA224", 256)); + testWith(sunPKCS11, "PBEWithHmacSHA256AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA256", 256)); + testWith(sunPKCS11, "PBEWithHmacSHA384AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA384", 256)); + testWith(sunPKCS11, "PBEWithHmacSHA512AndAES_256", + new PBKD2AssertData("PBKDF2WithHmacSHA512", 256)); + + // Use 1,5 * digest size as the testing derived key length (in bits) + testWith(sunPKCS11, "PBKDF2WithHmacSHA1", 240, + new PBKD2AssertData("PBKDF2WithHmacSHA1")); + testWith(sunPKCS11, "PBKDF2WithHmacSHA224", 336, + new PBKD2AssertData("PBKDF2WithHmacSHA224")); + testWith(sunPKCS11, "PBKDF2WithHmacSHA256", 384, + new PBKD2AssertData("PBKDF2WithHmacSHA256")); + testWith(sunPKCS11, "PBKDF2WithHmacSHA384", 576, + new PBKD2AssertData("PBKDF2WithHmacSHA384")); + testWith(sunPKCS11, "PBKDF2WithHmacSHA512", 768, + new PBKD2AssertData("PBKDF2WithHmacSHA512")); + + System.out.println("TEST PASS - OK"); + } + + private static void testWith(Provider sunPKCS11, String algorithm, + AssertData assertData) throws Exception { + PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations); + testWith(sunPKCS11, algorithm, keySpec, assertData); + } + + private static void testWith(Provider sunPKCS11, String algorithm, + int keyLen, AssertData assertData) throws Exception { + PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen); + testWith(sunPKCS11, algorithm, keySpec, assertData); + } + + private static void testWith(Provider sunPKCS11, String algorithm, + PBEKeySpec keySpec, AssertData assertData) throws Exception { + System.out.println(sep + System.lineSeparator() + algorithm); + + SecretKeyFactory skFac = SecretKeyFactory.getInstance( + algorithm, sunPKCS11); + BigInteger derivedKey = new BigInteger(1, + skFac.generateSecret(keySpec).getEncoded()); + printByteArray("Derived Key", derivedKey); + + BigInteger expectedDerivedKey = assertData.derive(algorithm, keySpec); + + if (!derivedKey.equals(expectedDerivedKey)) { + printByteArray("Expected Derived Key", expectedDerivedKey); + throw new Exception("Expected Derived Key did not match"); + } + } + + private static void printByteArray(String title, BigInteger b) { + String repr = (b == null) ? "buffer is null" : b.toString(16); + System.out.println(title + ": " + repr + System.lineSeparator()); + } + + public static void main(String[] args) throws Exception { + TestPBKD2 test = new TestPBKD2(); + Provider p = Security.getProvider("SunPKCS11-NSS-FIPS"); + if (p != null) { + test.main(p); + } else { + main(test); + } + } +} --- /dev/null +++ b/test/jdk/sun/security/pkcs11/fips/NssdbPin.java @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.List; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import jdk.test.lib.process.Proc; +import jdk.test.lib.util.FileUtils; + +/* + * @test + * @bug 9999999 + * @summary + * Test that the fips.nssdb.path and fips.nssdb.pin properties can be used + * for a successful login into an NSS DB. Some additional unitary testing + * is then performed. This test depends on NSS modutil and must be run in + * FIPS mode (the SunPKCS11-NSS-FIPS security provider has to be available). + * @modules jdk.crypto.cryptoki/sun.security.pkcs11:+open + * @library /test/lib + * @requires (jdk.version.major >= 8) + * @run main/othervm/timeout=600 NssdbPin + * @author Martin Balao (mbalao@redhat.com) + */ + +public final class NssdbPin { + + // Public properties and names + private static final String FIPS_NSSDB_PATH_PROP = "fips.nssdb.path"; + private static final String FIPS_NSSDB_PIN_PROP = "fips.nssdb.pin"; + private static final String FIPS_PROVIDER_NAME = "SunPKCS11-NSS-FIPS"; + private static final String NSSDB_TOKEN_NAME = + "NSS FIPS 140-2 Certificate DB"; + + // Data to be tested + private static final String[] PINS_TO_TEST = + new String[] { + "", + "1234567890abcdef1234567890ABCDEF\uA4F7" + }; + private static enum PropType { SYSTEM, SECURITY } + private static enum LoginType { IMPLICIT, EXPLICIT } + + // Internal test fields + private static final boolean DEBUG = true; + private static class TestContext { + String pin; + PropType propType; + Path workspace; + String nssdbPath; + Path nssdbPinFile; + LoginType loginType; + TestContext(String pin, Path workspace) { + this.pin = pin; + this.workspace = workspace; + this.nssdbPath = "sql:" + workspace; + this.loginType = LoginType.IMPLICIT; + } + } + + public static void main(String[] args) throws Throwable { + if (args.length == 3) { + // Executed by a child process. + mainChild(args[0], args[1], LoginType.valueOf(args[2])); + } else if (args.length == 0) { + // Executed by the parent process. + mainLauncher(); + // Test defaults + mainChild("sql:/etc/pki/nssdb", "", LoginType.IMPLICIT); + System.out.println("TEST PASS - OK"); + } else { + throw new Exception("Unexpected number of arguments."); + } + } + + private static void mainChild(String expectedPath, String expectedPin, + LoginType loginType) throws Throwable { + if (DEBUG) { + for (String prop : Arrays.asList(FIPS_NSSDB_PATH_PROP, + FIPS_NSSDB_PIN_PROP)) { + System.out.println(prop + " (System): " + + System.getProperty(prop)); + System.out.println(prop + " (Security): " + + Security.getProperty(prop)); + } + } + + /* + * Functional cross-test against an NSS DB generated by modutil + * with the same PIN. Check that we can perform a crypto operation + * that requires a login. The login might be explicit or implicit. + */ + Provider p = Security.getProvider(FIPS_PROVIDER_NAME); + if (DEBUG) { + System.out.println(FIPS_PROVIDER_NAME + ": " + p); + } + if (p == null) { + throw new Exception(FIPS_PROVIDER_NAME + " initialization failed."); + } + if (DEBUG) { + System.out.println("Login type: " + loginType); + } + if (loginType == LoginType.EXPLICIT) { + // Do the expansion to account for truncation, so C_Login in + // the NSS Software Token gets a UTF-8 encoded PIN. + byte[] pinUtf8 = expectedPin.getBytes(StandardCharsets.UTF_8); + char[] pinChar = new char[pinUtf8.length]; + for (int i = 0; i < pinChar.length; i++) { + pinChar[i] = (char)(pinUtf8[i] & 0xFF); + } + KeyStore.getInstance("PKCS11", p).load(null, pinChar); + if (DEBUG) { + System.out.println("Explicit login succeeded."); + } + } + if (DEBUG) { + System.out.println("Trying a crypto operation..."); + } + final int blockSize = 16; + Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding", p); + cipher.init(Cipher.ENCRYPT_MODE, + new SecretKeySpec(new byte[blockSize], "AES")); + if (cipher.doFinal(new byte[blockSize]).length != blockSize) { + throw new Exception("Could not perform a crypto operation."); + } + if (DEBUG) { + if (loginType == LoginType.IMPLICIT) { + System.out.println("Implicit login succeeded."); + } + System.out.println("Crypto operation after login succeeded."); + } + + if (loginType == LoginType.IMPLICIT) { + /* + * Additional unitary testing. Expected to succeed at this point. + */ + if (DEBUG) { + System.out.println("Trying unitary test..."); + } + String sysPathProp = System.getProperty(FIPS_NSSDB_PATH_PROP); + if (DEBUG) { + System.out.println("Path value (as a System property): " + + sysPathProp); + } + if (!expectedPath.equals(sysPathProp)) { + throw new Exception("Path is different than expected: " + + sysPathProp + " (actual) vs " + expectedPath + + " (expected)."); + } + Class<?> c = Class + .forName("sun.security.pkcs11.FIPSTokenLoginHandler"); + Method m = c.getDeclaredMethod("getFipsNssdbPin"); + m.setAccessible(true); + String pin = null; + char[] pinChar = (char[]) m.invoke(c); + if (pinChar != null) { + byte[] pinUtf8 = new byte[pinChar.length]; + for (int i = 0; i < pinUtf8.length; i++) { + pinUtf8[i] = (byte) pinChar[i]; + } + pin = new String(pinUtf8, StandardCharsets.UTF_8); + } + if (!expectedPin.isEmpty() && !expectedPin.equals(pin) || + expectedPin.isEmpty() && pin != null) { + throw new Exception("PIN is different than expected: '" + pin + + "' (actual) vs '" + expectedPin + "' (expected)."); + } + if (DEBUG) { + System.out.println("PIN value: " + pin); + System.out.println("Unitary test succeeded."); + } + } + } + + private static void mainLauncher() throws Throwable { + for (String pin : PINS_TO_TEST) { + Path workspace = Files.createTempDirectory(null); + try { + TestContext ctx = new TestContext(pin, workspace); + createNSSDB(ctx); + { + ctx.loginType = LoginType.IMPLICIT; + for (PropType propType : PropType.values()) { + ctx.propType = propType; + pinLauncher(ctx); + envLauncher(ctx); + fileLauncher(ctx); + } + } + explicitLoginLauncher(ctx); + } finally { + FileUtils.deleteFileTreeWithRetry(workspace); + } + } + } + + private static void pinLauncher(TestContext ctx) throws Throwable { + launchTest(p -> {}, "pin:" + ctx.pin, ctx); + } + + private static void envLauncher(TestContext ctx) throws Throwable { + final String NSSDB_PIN_ENV_VAR = "NSSDB_PIN_ENV_VAR"; + launchTest(p -> p.env(NSSDB_PIN_ENV_VAR, ctx.pin), + "env:" + NSSDB_PIN_ENV_VAR, ctx); + } + + private static void fileLauncher(TestContext ctx) throws Throwable { + // The file containing the PIN (ctx.nssdbPinFile) was created by the + // generatePinFile method, called from createNSSDB. + launchTest(p -> {}, "file:" + ctx.nssdbPinFile, ctx); + } + + private static void explicitLoginLauncher(TestContext ctx) + throws Throwable { + ctx.loginType = LoginType.EXPLICIT; + ctx.propType = PropType.SYSTEM; + launchTest(p -> {}, "Invalid PIN, must be ignored", ctx); + } + + private static void launchTest(Consumer<Proc> procCb, String pinPropVal, + TestContext ctx) throws Throwable { + if (DEBUG) { + System.out.println("Launching JVM with " + FIPS_NSSDB_PATH_PROP + + "=" + ctx.nssdbPath + " and " + FIPS_NSSDB_PIN_PROP + + "=" + pinPropVal); + } + Proc p = Proc.create(NssdbPin.class.getName()) + .args(ctx.nssdbPath, ctx.pin, ctx.loginType.name()); + if (ctx.propType == PropType.SYSTEM) { + p.prop(FIPS_NSSDB_PATH_PROP, ctx.nssdbPath); + p.prop(FIPS_NSSDB_PIN_PROP, pinPropVal); + // Make sure that Security properties defaults are not used. + p.secprop(FIPS_NSSDB_PATH_PROP, ""); + p.secprop(FIPS_NSSDB_PIN_PROP, ""); + } else if (ctx.propType == PropType.SECURITY) { + p.secprop(FIPS_NSSDB_PATH_PROP, ctx.nssdbPath); + pinPropVal = escapeForPropsFile(pinPropVal); + p.secprop(FIPS_NSSDB_PIN_PROP, pinPropVal); + } else { + throw new Exception("Unsupported property type."); + } + if (DEBUG) { + p.inheritIO(); + p.prop("java.security.debug", "sunpkcs11"); + p.debug(NssdbPin.class.getName()); + + // Need the launched process to connect to a debugger? + //System.setProperty("test.vm.opts", "-Xdebug -Xrunjdwp:" + + // "transport=dt_socket,address=localhost:8000,suspend=y"); + } else { + p.nodump(); + } + procCb.accept(p); + p.start().waitFor(0); + } + + private static String escapeForPropsFile(String str) throws Throwable { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < str.length(); i++) { + int cp = str.codePointAt(i); + if (Character.UnicodeBlock.of(cp) + == Character.UnicodeBlock.BASIC_LATIN) { + sb.append(Character.toChars(cp)); + } else { + sb.append("\\u").append(String.format("%04X", cp)); + } + } + return sb.toString(); + } + + private static void createNSSDB(TestContext ctx) throws Throwable { + ProcessBuilder pb = getModutilPB(ctx, "-create"); + if (DEBUG) { + System.out.println("Creating an NSS DB in " + ctx.workspace + + "..."); + System.out.println("cmd: " + String.join(" ", pb.command())); + } + if (pb.start().waitFor() != 0) { + throw new Exception("NSS DB creation failed."); + } + generatePinFile(ctx); + pb = getModutilPB(ctx, "-changepw", NSSDB_TOKEN_NAME, + "-newpwfile", ctx.nssdbPinFile.toString()); + if (DEBUG) { + System.out.println("NSS DB created."); + System.out.println("Changing NSS DB PIN..."); + System.out.println("cmd: " + String.join(" ", pb.command())); + } + if (pb.start().waitFor() != 0) { + throw new Exception("NSS DB PIN change failed."); + } + if (DEBUG) { + System.out.println("NSS DB PIN changed."); + } + } + + private static ProcessBuilder getModutilPB(TestContext ctx, String... args) + throws Throwable { + ProcessBuilder pb = new ProcessBuilder("modutil", "-force"); + List<String> pbCommand = pb.command(); + if (args != null) { + pbCommand.addAll(Arrays.asList(args)); + } + pbCommand.add("-dbdir"); + pbCommand.add(ctx.nssdbPath); + if (DEBUG) { + pb.inheritIO(); + } else { + pb.redirectError(ProcessBuilder.Redirect.INHERIT); + } + return pb; + } + + private static void generatePinFile(TestContext ctx) throws Throwable { + ctx.nssdbPinFile = Files.createTempFile(ctx.workspace, null, null); + Files.writeString(ctx.nssdbPinFile, ctx.pin + System.lineSeparator() + + "2nd line with garbage"); + } +} --- /dev/null +++ b/test/jdk/sun/security/pkcs11/fips/VerifyMissingAttributes.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Provider; +import java.security.Security; + +/* + * @test + * @bug 9999999 + * @requires (jdk.version.major >= 8) + * @run main/othervm/timeout=30 VerifyMissingAttributes + * @author Martin Balao (mbalao@redhat.com) + */ + +public final class VerifyMissingAttributes { + + private static final String[] svcAlgImplementedIn = { + "AlgorithmParameterGenerator.DSA", + "AlgorithmParameters.DSA", + "CertificateFactory.X.509", + "KeyStore.JKS", + "KeyStore.CaseExactJKS", + "KeyStore.DKS", + "CertStore.Collection", + "CertStore.com.sun.security.IndexedCollection" + }; + + public static void main(String[] args) throws Throwable { + Provider sunProvider = Security.getProvider("SUN"); + for (String svcAlg : svcAlgImplementedIn) { + String filter = svcAlg + " ImplementedIn:Software"; + doQuery(sunProvider, filter); + } + if (Double.parseDouble( + System.getProperty("java.specification.version")) >= 17) { + String filter = "KeyFactory.RSASSA-PSS SupportedKeyClasses:" + + "java.security.interfaces.RSAPublicKey" + + "|java.security.interfaces.RSAPrivateKey"; + doQuery(Security.getProvider("SunRsaSign"), filter); + } + System.out.println("TEST PASS - OK"); + } + + private static void doQuery(Provider expectedProvider, String filter) + throws Exception { + if (expectedProvider == null) { + throw new Exception("Provider not found."); + } + Provider[] providers = Security.getProviders(filter); + if (providers == null || providers.length != 1 || + providers[0] != expectedProvider) { + throw new Exception("Failure retrieving the provider with this" + + " query: " + filter); + } + } +} -- 2.45.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor