8301553: Support Password-Based Cryptography in SunPKCS11

Co-authored-by: Francisco Ferrari Bihurriet <fferrari@redhat.com>
Co-authored-by: Martin Balao <mbalao@openjdk.org>
Reviewed-by: valeriep
This commit is contained in:
Martin Balao 2023-06-06 19:39:34 +00:00
parent 0a4f9ad637
commit 4a75fd462c
30 changed files with 3013 additions and 452 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,13 +25,14 @@
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 java.util.Arrays;
import jdk.internal.access.SharedSecrets;
import sun.security.util.PBEUtil;
/**
* This is an implementation of the HMAC algorithms as defined
@ -108,81 +109,30 @@ 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");
}
byte[] derivedKey;
char[] password = null;
byte[] derivedKey = null;
SecretKeySpec cipherKey = null;
PBEKeySpec keySpec = PBEUtil.getPBAKeySpec(key, params);
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);
password = keySpec.getPassword();
derivedKey = PKCS12PBECipherCore.derive(
password, keySpec.getSalt(),
keySpec.getIterationCount(), engineGetMacLength(),
PKCS12PBECipherCore.MAC_KEY, algorithm, bl);
cipherKey = new SecretKeySpec(derivedKey, "HmacSHA1");
super.engineInit(cipherKey, null);
} finally {
Arrays.fill(passwdChars, '\0');
if (cipherKey != null) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(cipherKey);
}
if (derivedKey != null) {
Arrays.fill(derivedKey, (byte) 0);
}
if (password != null) {
Arrays.fill(password, '\0');
}
keySpec.clearPassword();
}
SecretKey cipherKey = new SecretKeySpec(derivedKey, "HmacSHA1");
super.engineInit(cipherKey, null);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,6 +31,9 @@ import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
import jdk.internal.access.SharedSecrets;
import sun.security.util.PBEUtil;
/**
* This class represents password-based encryption as defined by the PKCS #5
* standard.
@ -43,10 +46,6 @@ import javax.crypto.spec.*;
* @see javax.crypto.Cipher
*/
abstract class PBES2Core extends CipherSpi {
private static final int DEFAULT_SALT_LENGTH = 20;
private static final int DEFAULT_COUNT = 4096;
// the encapsulated cipher
private final CipherCore cipher;
private final int keyLength; // in bits
@ -54,9 +53,7 @@ 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.PBES2Params pbes2Params = new PBEUtil.PBES2Params();
/**
* Creates an instance of PBE Scheme 2 according to the selected
@ -135,32 +132,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 pbes2Params.getAlgorithmParameters(
blkSize, pbeAlgo, SunJCE.getInstance(), SunJCE.getRandom());
}
protected void engineInit(int opmode, Key key, SecureRandom random)
@ -172,132 +145,46 @@ abstract class PBES2Core extends CipherSpi {
}
}
private static byte[] check(byte[] salt)
throws InvalidAlgorithmParameterException {
if (salt != null && salt.length < 8) {
throw new InvalidAlgorithmParameterException(
"Salt must be at least 8 bytes long");
}
return salt;
}
private static int check(int iCount)
throws InvalidAlgorithmParameterException {
if (iCount < 0) {
throw new InvalidAlgorithmParameterException(
"Iteration count must be a positive number");
}
return iCount == 0 ? DEFAULT_COUNT : iCount;
}
protected void engineInit(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;
salt = null;
iCount = 0;
ivSpec = null;
PBEKeySpec pbeSpec;
try {
if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
throw new InvalidKeyException("Missing password");
}
boolean doEncrypt = ((opmode == Cipher.ENCRYPT_MODE) ||
(opmode == Cipher.WRAP_MODE));
// Extract from the supplied PBE params, if present
if (params instanceof PBEParameterSpec pbeParams) {
// salt should be non-null per PBEParameterSpec
salt = check(pbeParams.getSalt());
iCount = check(pbeParams.getIterationCount());
AlgorithmParameterSpec ivParams = pbeParams.getParameterSpec();
if (ivParams instanceof IvParameterSpec iv) {
ivSpec = iv;
} else if (ivParams == null && doEncrypt) {
// generate random IV
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
} else {
throw new InvalidAlgorithmParameterException(
"Wrong parameter type: IV expected");
}
} else if (params == null && doEncrypt) {
// Try extracting from the key if present. If unspecified,
// PBEKey returns null and 0 respectively.
if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) {
salt = check(pbeKey.getSalt());
iCount = check(pbeKey.getIterationCount());
}
if (salt == null) {
// generate random salt
salt = new byte[DEFAULT_SALT_LENGTH];
random.nextBytes(salt);
}
if (iCount == 0) {
// use default iteration count
iCount = DEFAULT_COUNT;
}
// generate random IV
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
} else {
throw new InvalidAlgorithmParameterException
("Wrong parameter type: PBE 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);
} finally {
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
if (passwdChars != null) Arrays.fill(passwdChars, '\0');
if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00);
}
PBKDF2KeyImpl s;
PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec(blkSize, keyLength,
opmode, key, params, random);
PBKDF2KeyImpl s = null;
byte[] derivedKey;
try {
s = (PBKDF2KeyImpl)kdf.engineGenerateSecret(pbeSpec);
derivedKey = s.getEncoded();
} catch (InvalidKeySpecException ikse) {
throw new InvalidKeyException("Cannot construct PBE key", ikse);
} finally {
if (s != null) {
s.clear();
}
pbeSpec.clearPassword();
}
byte[] derivedKey = s.getEncoded();
s.clearPassword();
SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
// initialize the underlying cipher
cipher.init(opmode, cipherKey, ivSpec, random);
SecretKeySpec cipherKey = null;
try {
cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
// initialize the underlying cipher
cipher.init(opmode, cipherKey, pbes2Params.getIvSpec(), random);
} finally {
if (cipherKey != null) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(cipherKey);
}
Arrays.fill(derivedKey, (byte) 0);
}
}
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.PBES2Params.getParameterSpec(params),
random);
}
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,7 @@
package com.sun.crypto.provider;
import java.io.ObjectStreamException;
import java.lang.ref.Reference;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Arrays;
@ -64,9 +64,9 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
private int iterCount;
private byte[] key;
@SuppressWarnings("serial") // Type of field is not Serializable;
// see writeReplace method
private Mac prf;
// The following fields are not Serializable. See writeReplace method.
private transient Mac prf;
private transient Cleaner.Cleanable cleaner;
private static byte[] getPasswordBytes(char[] passwd) {
CharBuffer cb = CharBuffer.wrap(passwd);
@ -88,17 +88,9 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
*/
PBKDF2KeyImpl(PBEKeySpec keySpec, String prfAlgo)
throws InvalidKeySpecException {
char[] passwd = keySpec.getPassword();
if (passwd == null) {
// Should allow an empty password.
this.passwd = new char[0];
} else {
this.passwd = passwd.clone();
}
this.passwd = keySpec.getPassword();
// Convert the password from char[] to byte[]
byte[] passwdBytes = getPasswordBytes(this.passwd);
// remove local copy
if (passwd != null) Arrays.fill(passwd, '\0');
try {
this.salt = keySpec.getSalt();
@ -124,16 +116,18 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
throw new InvalidKeySpecException(nsae);
} finally {
Arrays.fill(passwdBytes, (byte) 0x00);
// Use the cleaner to zero the key when no longer referenced
final byte[] k = this.key;
final char[] p = this.passwd;
CleanerFactory.cleaner().register(this,
() -> {
Arrays.fill(k, (byte) 0x00);
Arrays.fill(p, '\0');
});
if (key == null) {
Arrays.fill(passwd, '\0');
}
}
// Use the cleaner to zero the key when no longer referenced
final byte[] k = this.key;
final char[] p = this.passwd;
cleaner = CleanerFactory.cleaner().register(this,
() -> {
Arrays.fill(k, (byte) 0x00);
Arrays.fill(p, '\0');
});
}
private static byte[] deriveKey(final Mac prf, final byte[] password,
@ -211,11 +205,7 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
}
public byte[] getEncoded() {
// The key is zeroized by finalize()
// The reachability fence ensures finalize() isn't called early
byte[] result = key.clone();
Reference.reachabilityFence(this);
return result;
return key.clone();
}
public String getAlgorithm() {
@ -226,16 +216,12 @@ final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
return iterCount;
}
public void clearPassword() {
Arrays.fill(passwd, (char)0);
public void clear() {
cleaner.clean();
}
public char[] getPassword() {
// The password is zeroized by finalize()
// The reachability fence ensures finalize() isn't called early
char[] result = passwd.clone();
Reference.reachabilityFence(this);
return result;
return passwd.clone();
}
public byte[] getSalt() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,8 @@
package com.sun.crypto.provider;
import jdk.internal.access.SharedSecrets;
import java.util.Arrays;
import javax.crypto.SecretKey;
@ -179,23 +181,29 @@ abstract class PBMAC1Core extends HmacCore {
}
PBKDF2KeyImpl s = null;
PBKDF2Core kdf = getKDFImpl(kdfAlgo);
byte[] derivedKey;
byte[] derivedKey = null;
SecretKeySpec cipherKey = null;
try {
PBKDF2Core kdf = getKDFImpl(kdfAlgo);
s = (PBKDF2KeyImpl)kdf.engineGenerateSecret(pbeSpec);
derivedKey = s.getEncoded();
cipherKey = new SecretKeySpec(derivedKey, kdfAlgo);
super.engineInit(cipherKey, null);
} catch (InvalidKeySpecException ikse) {
throw new InvalidKeyException("Cannot construct PBE key", ikse);
} finally {
pbeSpec.clearPassword();
if (s != null) {
s.clearPassword();
if (cipherKey != null) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(cipherKey);
}
if (derivedKey != null) {
Arrays.fill(derivedKey, (byte) 0);
}
if (s != null) {
s.clear();
}
pbeSpec.clearPassword();
}
SecretKey cipherKey = new SecretKeySpec(derivedKey, kdfAlgo);
Arrays.fill(derivedKey, (byte)0);
super.engineInit(cipherKey, null);
}
public static final class HmacSHA1 extends PBMAC1Core {

View File

@ -0,0 +1,355 @@
/*
* Copyright (c) 2023, 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 {
/*
* PBES2Params is an auxiliary class that represents the state needed for
* PBES2 operations (iterations count, salt and IV) and its (re)
* initialization logic. Users of this class are CipherSpi implementors that
* support PBES2 cryptography (RFC #8018), such as PBES2Core (SunJCE) and
* P11PBECipher (SunPKCS11).
*
* CipherSpi implementors must call ::getPBEKeySpec in every engine
* initialization (CipherSpi::engineInit override) to reset the state and
* get new values in a PBEKeySpec instance. These new values are taken
* from parameters, defaults or generated randomly.
*
* After engine initialization, values in effect can be extracted with
* ::getAlgorithmParameters (as AlgorithmParameters) or ::getIvSpec (as
* IvParameterSpec).
*/
public static final class PBES2Params {
private static final int DEFAULT_SALT_LENGTH = 20;
private static final int DEFAULT_ITERATIONS = 4096;
private int iCount;
private byte[] salt;
private IvParameterSpec ivSpec;
/*
* Initialize a PBES2Params instance. May generate random salt and
* IV if not passed and the operation is encryption. If initialization
* fails, values are reset. Used by PBES2Params and P11PBECipher
* (SunPKCS11).
*/
public void initialize(int blkSize, int opmode, int iCount, byte[] salt,
AlgorithmParameterSpec ivSpec, SecureRandom random)
throws InvalidAlgorithmParameterException {
try {
boolean doEncrypt = opmode == Cipher.ENCRYPT_MODE ||
opmode == Cipher.WRAP_MODE;
if (ivSpec instanceof IvParameterSpec iv) {
this.ivSpec = iv;
} else if (ivSpec == null && doEncrypt) {
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
this.ivSpec = new IvParameterSpec(ivBytes);
} else {
throw new InvalidAlgorithmParameterException("Wrong " +
"parameter type: IvParameterSpec " +
(doEncrypt ? "or null " : "") + "expected");
}
this.iCount = iCount == 0 ? DEFAULT_ITERATIONS : iCount;
if (salt == null) {
if (doEncrypt) {
salt = new byte[DEFAULT_SALT_LENGTH];
random.nextBytes(salt);
} else {
throw new InvalidAlgorithmParameterException("Salt " +
"needed for decryption");
}
}
this.salt = salt;
} catch (InvalidAlgorithmParameterException e) {
this.ivSpec = null;
this.iCount = 0;
this.salt = null;
throw e;
}
}
/*
* Obtain an IvParameterSpec for Cipher services. This method returns
* null when the state is not initialized. Used by PBES2Core (SunJCE)
* and P11PBECipher (SunPKCS11).
*/
public IvParameterSpec getIvSpec() {
return ivSpec;
}
/*
* Obtain AlgorithmParameters for Cipher services. This method will
* initialize PBES2Params if needed, generating new values randomly or
* assigning from defaults. If PBES2Params is initialized, existing
* values will be returned. Used by PBES2Core (SunJCE) and
* P11PBECipher (SunPKCS11).
*/
public AlgorithmParameters getAlgorithmParameters(int blkSize,
String pbeAlgo, Provider algParamsProv, SecureRandom random) {
AlgorithmParameters params;
try {
if (iCount == 0 && salt == null && ivSpec == null) {
initialize(blkSize, Cipher.ENCRYPT_MODE, 0, null, null,
random);
}
params = AlgorithmParameters.getInstance(pbeAlgo,
algParamsProv);
params.init(new PBEParameterSpec(salt, iCount, ivSpec));
} 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");
} catch (InvalidAlgorithmParameterException iape) {
// should never happen
throw new RuntimeException("Error initializing PBES2Params");
}
return params;
}
/*
* Initialize PBES2Params and obtain a PBEKeySpec for Cipher services.
* Data from the key, parameters, defaults or random may be used for
* initialization. Used by PBES2Core (SunJCE) and P11PBECipher
* (SunPKCS11).
*/
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;
char[] passwdChars = null;
if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) ||
(passwdBytes = key.getEncoded()) == null) {
throw new InvalidKeyException("Missing password");
}
try {
int iCountInit;
byte[] saltInit;
AlgorithmParameterSpec ivSpecInit;
// Extract from the supplied PBE params, if present
if (params instanceof PBEParameterSpec pbeParams) {
// salt should be non-null per PBEParameterSpec
iCountInit = check(pbeParams.getIterationCount());
saltInit = check(pbeParams.getSalt());
ivSpecInit = pbeParams.getParameterSpec();
} else if (params == null) {
// Try extracting from the key if present. If unspecified,
// PBEKey returns 0 and null respectively.
if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) {
iCountInit = check(pbeKey.getIterationCount());
saltInit = check(pbeKey.getSalt());
} else {
iCountInit = 0;
saltInit = null;
}
ivSpecInit = null;
} else {
throw new InvalidAlgorithmParameterException(
"Wrong parameter type: PBE expected");
}
initialize(blkSize, opmode, iCountInit, saltInit, ivSpecInit,
random);
passwdChars = new char[passwdBytes.length];
for (int i = 0; i < passwdChars.length; i++) {
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
}
return new PBEKeySpec(passwdChars, salt, iCount, keyLength);
} finally {
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
if (passwdChars != null) Arrays.fill(passwdChars, '\0');
if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00);
}
}
/*
* Obtain an AlgorithmParameterSpec from an AlgorithmParameters
* instance, for Cipher services. Used by PBES2Core (SunJCE) and
* P11PBECipher (SunPKCS11).
*/
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;
}
private static byte[] check(byte[] salt)
throws InvalidAlgorithmParameterException {
if (salt != null && salt.length < 8) {
throw new InvalidAlgorithmParameterException(
"Salt must be at least 8 bytes long");
}
return salt;
}
private static int check(int iCount)
throws InvalidAlgorithmParameterException {
if (iCount < 0) {
throw new InvalidAlgorithmParameterException(
"Iteration count must be a positive number");
}
return iCount;
}
}
/*
* Obtain a PBEKeySpec for Mac services, after key and parameters
* validation. Used by HmacPKCS12PBECore (SunJCE) and P11Mac (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 pbeKey) {
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');
}
}
/*
* Check that the key implements the PBEKey interface. If params is an
* instance of PBEParameterSpec, validate consistency with the key's
* derivation data. Used by P11Mac and P11PBECipher (SunPKCS11).
*/
public static void checkKeyAndParams(Key key,
AlgorithmParameterSpec params, String algorithm)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) {
if (params instanceof PBEParameterSpec pbeParams) {
if (pbeParams.getIterationCount() !=
pbeKey.getIterationCount() ||
!Arrays.equals(pbeParams.getSalt(), pbeKey.getSalt())) {
throw new InvalidAlgorithmParameterException(
"Salt or iteration count parameters are " +
"not consistent with PBE key");
}
}
} else {
throw new InvalidKeyException(
"Cannot use a " + algorithm + " service with a key that " +
"does not implement javax.crypto.interfaces.PBEKey");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -262,10 +262,8 @@ final class P11Cipher extends CipherSpi {
// no native padding support; use our own padding impl
paddingObj = new PKCS5Padding(blockSize);
padBuffer = new byte[blockSize];
char[] tokenLabel = token.tokenInfo.label;
// NSS requires block-sized updates in multi-part operations.
reqBlockUpdates = ((tokenLabel[0] == 'N' && tokenLabel[1] == 'S'
&& tokenLabel[2] == 'S') ? true : false);
reqBlockUpdates = P11Util.isNSS(token);
}
} else {
throw new NoSuchPaddingException("Unsupported padding " + padding);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -141,9 +141,7 @@ abstract class P11Key implements Key, Length {
this.tokenObject = tokenObject;
this.sensitive = sensitive;
this.extractable = extractable;
char[] tokenLabel = this.token.tokenInfo.label;
isNSS = (tokenLabel[0] == 'N' && tokenLabel[1] == 'S'
&& tokenLabel[2] == 'S');
isNSS = P11Util.isNSS(this.token);
boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS &&
extractable && !tokenObject);
this.keyIDHolder = new NativeKeyHolder(this, keyID, session,
@ -354,6 +352,18 @@ abstract class P11Key implements Key, Length {
return new P11SecretKey(session, keyID, algorithm, keyLength, attrs);
}
static SecretKey pbeKey(Session session, long keyID, String algorithm,
int keyLength, CK_ATTRIBUTE[] attrs, char[] password, byte[] salt,
int iterationCount) {
attrs = getAttributes(session, keyID, attrs, new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_TOKEN),
new CK_ATTRIBUTE(CKA_SENSITIVE),
new CK_ATTRIBUTE(CKA_EXTRACTABLE),
});
return new P11PBEKey(session, keyID, algorithm, keyLength,
attrs, password, salt, iterationCount);
}
static SecretKey masterSecretKey(Session session, long keyID,
String algorithm, int keyLength, CK_ATTRIBUTE[] attrs,
int major, int minor) {
@ -487,6 +497,45 @@ abstract class P11Key implements Key, Length {
}
}
static final class P11PBEKey extends P11SecretKey
implements PBEKey {
private static final long serialVersionUID = 6847576994253634876L;
private char[] password;
private final byte[] salt;
private final int iterationCount;
P11PBEKey(Session session, long keyID, String algorithm,
int keyLength, CK_ATTRIBUTE[] attributes,
char[] password, byte[] salt, int iterationCount) {
super(session, keyID, algorithm, keyLength, attributes);
this.password = password.clone();
this.salt = salt.clone();
this.iterationCount = iterationCount;
}
@Override
public char[] getPassword() {
if (password == null) {
throw new IllegalStateException("password has been cleared");
}
return password.clone();
}
@Override
public byte[] getSalt() {
return salt.clone();
}
@Override
public int getIterationCount() {
return iterationCount;
}
void clearPassword() {
Arrays.fill(password, '\0');
password = null;
}
}
@SuppressWarnings("deprecation")
private static class P11TlsMasterSecretKey extends P11SecretKey
implements TlsMasterSecret {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,8 +29,11 @@ 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 javax.crypto.spec.PBEParameterSpec;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
@ -39,6 +42,7 @@ import sun.nio.ch.DirectBuffer;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
import sun.security.util.PBEUtil;
/**
* MAC implementation class. This class currently supports HMAC using
@ -65,6 +69,9 @@ final class P11Mac extends MacSpi {
// algorithm name
private final String algorithm;
// PBEKeyInfo if algorithm is PBE-related, otherwise null
private final P11SecretKeyFactory.PBEKeyInfo svcPbeKi;
// mechanism object
private final CK_MECHANISM ckMechanism;
@ -88,6 +95,7 @@ final class P11Mac extends MacSpi {
super();
this.token = token;
this.algorithm = algorithm;
this.svcPbeKi = P11SecretKeyFactory.getPBEKeyInfo(algorithm);
Long params = null;
macLength = switch ((int) mechanism) {
case (int) CKM_MD5_HMAC -> 16;
@ -192,12 +200,53 @@ final class P11Mac extends MacSpi {
// see JCE spec
protected void engineInit(Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
reset(true);
p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm);
p11Key = null;
if (svcPbeKi != null) {
if (key instanceof P11Key) {
// If the key is a P11Key, it must come from a PBE derivation
// because this is a PBE Mac service. In addition to checking
// the key, check that params (if passed) are consistent.
PBEUtil.checkKeyAndParams(key, params, algorithm);
} else {
// If the key is not a P11Key, a derivation is needed. Data for
// derivation has to be carried either as part of the key or
// params. Use SunPKCS11 PBE key derivation to obtain a P11Key.
// Assign the derived key to p11Key because conversion is never
// needed for this case.
PBEKeySpec pbeKeySpec = PBEUtil.getPBAKeySpec(key, params);
try {
P11Key.P11PBEKey p11PBEKey =
P11SecretKeyFactory.derivePBEKey(token,
pbeKeySpec, svcPbeKi);
// This Mac service uses the token where the derived key
// lives so there won't be any need to re-derive and use
// the password. The p11Key cannot be accessed out of this
// class.
p11PBEKey.clearPassword();
p11Key = p11PBEKey;
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException(e);
} finally {
pbeKeySpec.clearPassword();
}
}
if (params instanceof PBEParameterSpec pbeParams) {
// For PBE services, reassign params to the underlying
// service params. Notice that Mac services expect this
// value to be null.
params = pbeParams.getParameterSpec();
}
}
if (params != null) {
throw new InvalidAlgorithmParameterException(
"Parameters not supported");
}
// In non-PBE cases and PBE cases where we didn't derive,
// a key conversion might be needed.
if (p11Key == null) {
p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm);
}
try {
initialize();
} catch (PKCS11Exception e) {

View File

@ -0,0 +1,228 @@
/*
* Copyright (c) 2023, 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.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
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 javax.crypto.spec.PBEParameterSpec;
import sun.security.jca.JCAUtil;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import sun.security.pkcs11.wrapper.PKCS11Exception;
import sun.security.util.PBEUtil;
final class P11PBECipher extends CipherSpi {
private final Token token;
private final String pbeAlg;
private final P11Cipher cipher;
private final int blkSize;
private final P11SecretKeyFactory.PBEKeyInfo svcPbeKi;
private final PBEUtil.PBES2Params pbes2Params = new PBEUtil.PBES2Params();
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();
this.pbeAlg = pbeAlg;
svcPbeKi = P11SecretKeyFactory.getPBEKeyInfo(pbeAlg);
assert svcPbeKi != null : "algorithm must be in KeyInfo map";
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 pbes2Params.getAlgorithmParameters(blkSize, pbeAlg,
P11Util.getSunJceProvider(), 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 {
if (key instanceof P11Key) {
// If the key is a P11Key, it must come from a PBE derivation
// because this is a PBE Cipher service. In addition to checking the
// key, check that params (if passed) are consistent.
PBEUtil.checkKeyAndParams(key, params, pbeAlg);
// At this point, we know that the key is a P11PBEKey.
P11Key.P11PBEKey p11PBEKey = (P11Key.P11PBEKey) key;
// PBE services require a PBE key of the same algorithm and the
// underlying service (non-PBE) won't check it.
if (!pbeAlg.equals(p11PBEKey.getAlgorithm())) {
throw new InvalidKeyException("Cannot use a " +
p11PBEKey.getAlgorithm() + " key for a " + pbeAlg +
" service");
}
if (params instanceof PBEParameterSpec pbeParams) {
params = pbeParams.getParameterSpec();
}
pbes2Params.initialize(blkSize, opmode,
p11PBEKey.getIterationCount(), p11PBEKey.getSalt(), params,
random);
} else {
// If the key is not a P11Key, a derivation is needed. Data for
// derivation has to be carried either as part of the key or params.
// Use SunPKCS11 PBE key derivation to obtain a P11Key.
PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec(
blkSize, svcPbeKi.keyLen, opmode, key, params, random);
try {
P11Key.P11PBEKey p11PBEKey = P11SecretKeyFactory.derivePBEKey(
token, pbeSpec, svcPbeKi);
// The internal Cipher service uses the token where the
// derived key lives so there won't be any need to re-derive
// and use the password. The key cannot be accessed out of this
// class.
p11PBEKey.clearPassword();
key = p11PBEKey;
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException(e);
} finally {
pbeSpec.clearPassword();
}
}
cipher.engineInit(opmode, key, pbes2Params.getIvSpec(), random);
}
// see JCE spec
@Override
protected void engineInit(int opmode, Key key,
AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException,
InvalidAlgorithmParameterException {
engineInit(opmode, key, PBEUtil.PBES2Params.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) {
// It's guaranteed that when engineInit succeeds, the key length
// for the underlying cipher is equal to the PBE service key length.
// Otherwise, initialization fails.
return svcPbeKi.keyLen;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,15 +25,19 @@
package sun.security.pkcs11;
import java.nio.charset.StandardCharsets;
import java.util.*;
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.*;
import jdk.internal.access.SharedSecrets;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
@ -52,44 +56,190 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
// algorithm name
private final String algorithm;
// PBEKeyInfo if algorithm is PBE-related, otherwise null
private final PBEKeyInfo svcPbeKi;
P11SecretKeyFactory(Token token, String algorithm) {
super();
this.token = token;
this.algorithm = algorithm;
this.svcPbeKi = getPBEKeyInfo(algorithm);
}
private static final Map<String,Long> keyTypes;
private static final Map<String, KeyInfo> keyInfo = new HashMap<>();
private static final KeyInfo HMAC = new KeyInfo("HMAC", PCKK_HMAC);
private static final KeyInfo SSLMAC = new KeyInfo("SSLMAC", PCKK_SSLMAC);
static KeyInfo getKeyInfo(String algo) {
KeyInfo ki = keyInfo.get(algo);
if (ki == null) {
String algoUpper = algo.toUpperCase(Locale.ENGLISH);
ki = keyInfo.get(algoUpper);
if (ki == null) {
if (algoUpper.startsWith("HMAC")) {
return HMAC;
} else if (algoUpper.startsWith("SSLMAC")) {
return SSLMAC;
}
}
}
return ki;
}
static PBEKeyInfo getPBEKeyInfo(String algo) {
if (getKeyInfo(algo) instanceof PBEKeyInfo pbeKi) {
return pbeKi;
}
return null;
}
private static void putKeyInfo(KeyInfo ki) {
keyInfo.put(ki.algo, ki);
keyInfo.put(ki.algo.toUpperCase(Locale.ENGLISH), ki);
}
static sealed class KeyInfo permits PBEKeyInfo {
public final String algo;
public final long keyType;
KeyInfo(String algo, long keyType) {
this.algo = algo;
this.keyType = keyType;
}
// The P11SecretKeyFactory::convertKey method needs to know if a service
// type and a key are compatible. Trivial cases such as having the same
// algorithm names are handled directly. KeyInfo::checkUse helps with
// cases that require to retrieve the key's KeyInfo (ki), in addition to
// the service's KeyInfo (si), to make a decision.
static boolean checkUse(KeyInfo ki, KeyInfo si) {
if (si instanceof PBEKeyInfo && !si.algo.equals(ki.algo)) {
// PBE services require a PBE key of the same algorithm.
return false;
}
if (ki instanceof PBKDF2KeyInfo) {
// We cannot tell what the PBE key was derived for,
// so any service is allowed in principle.
return true;
}
// This path handles non-PBE cases where aliases are used (i.e:
// RC4 vs ARCFOUR) and mixed PBE - non-PBE cases (i.e.: a
// PBE-derived AES key used in an AES Cipher service).
return ki.keyType == si.keyType;
}
}
static abstract sealed class PBEKeyInfo extends KeyInfo
permits AESPBEKeyInfo, PBKDF2KeyInfo, P12MacPBEKeyInfo {
public static final long INVALID_PRF = -1;
public final long kdfMech;
public final long prfMech;
public final int keyLen;
public final CK_ATTRIBUTE[] extraAttrs;
protected PBEKeyInfo(String algo, long kdfMech, long prfMech,
long keyType, int keyLen, CK_ATTRIBUTE[] extraAttrs) {
super(algo, keyType);
this.kdfMech = kdfMech;
this.prfMech = prfMech;
this.keyLen = keyLen;
this.extraAttrs = extraAttrs;
}
}
static final class AESPBEKeyInfo extends PBEKeyInfo {
private static final CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[] {
CK_ATTRIBUTE.ENCRYPT_TRUE};
AESPBEKeyInfo(String algo, long prfMech, int keyLen) {
super(algo, CKM_PKCS5_PBKD2, prfMech, CKK_AES, keyLen, attr);
}
}
static final class PBKDF2KeyInfo extends PBEKeyInfo {
private static final CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[] {
CK_ATTRIBUTE.ENCRYPT_TRUE, CK_ATTRIBUTE.SIGN_TRUE};
PBKDF2KeyInfo(String algo, long prfMech) {
super(algo, CKM_PKCS5_PBKD2, prfMech, CKK_GENERIC_SECRET, -1, attr);
}
}
static final class P12MacPBEKeyInfo extends PBEKeyInfo {
private static final CK_ATTRIBUTE[] attr = new CK_ATTRIBUTE[] {
CK_ATTRIBUTE.SIGN_TRUE};
P12MacPBEKeyInfo(String algo, long kdfMech, int keyLen) {
super(algo, kdfMech, PBEKeyInfo.INVALID_PRF,
CKK_GENERIC_SECRET, keyLen, attr);
}
}
static {
keyTypes = new HashMap<String,Long>();
addKeyType("RC4", CKK_RC4);
addKeyType("ARCFOUR", CKK_RC4);
addKeyType("DES", CKK_DES);
addKeyType("DESede", CKK_DES3);
addKeyType("AES", CKK_AES);
addKeyType("Blowfish", CKK_BLOWFISH);
addKeyType("ChaCha20", CKK_CHACHA20);
addKeyType("ChaCha20-Poly1305", CKK_CHACHA20);
putKeyInfo(new KeyInfo("RC4", CKK_RC4));
putKeyInfo(new KeyInfo("ARCFOUR", CKK_RC4));
putKeyInfo(new KeyInfo("DES", CKK_DES));
putKeyInfo(new KeyInfo("DESede", CKK_DES3));
putKeyInfo(new KeyInfo("AES", CKK_AES));
putKeyInfo(new KeyInfo("Blowfish", CKK_BLOWFISH));
putKeyInfo(new KeyInfo("ChaCha20", CKK_CHACHA20));
putKeyInfo(new KeyInfo("ChaCha20-Poly1305", CKK_CHACHA20));
// we don't implement RC2 or IDEA, but we want to be able to generate
// keys for those SSL/TLS ciphersuites.
addKeyType("RC2", CKK_RC2);
addKeyType("IDEA", CKK_IDEA);
putKeyInfo(new KeyInfo("RC2", CKK_RC2));
putKeyInfo(new KeyInfo("IDEA", CKK_IDEA));
addKeyType("TlsPremasterSecret", PCKK_TLSPREMASTER);
addKeyType("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER);
addKeyType("TlsMasterSecret", PCKK_TLSMASTER);
addKeyType("Generic", CKK_GENERIC_SECRET);
putKeyInfo(new KeyInfo("TlsPremasterSecret", PCKK_TLSPREMASTER));
putKeyInfo(new KeyInfo("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER));
putKeyInfo(new KeyInfo("TlsMasterSecret", PCKK_TLSMASTER));
putKeyInfo(new KeyInfo("Generic", CKK_GENERIC_SECRET));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA1AndAES_128",
CKP_PKCS5_PBKD2_HMAC_SHA1, 128));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA224AndAES_128",
CKP_PKCS5_PBKD2_HMAC_SHA224, 128));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA256AndAES_128",
CKP_PKCS5_PBKD2_HMAC_SHA256, 128));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA384AndAES_128",
CKP_PKCS5_PBKD2_HMAC_SHA384, 128));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA512AndAES_128",
CKP_PKCS5_PBKD2_HMAC_SHA512, 128));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA1AndAES_256",
CKP_PKCS5_PBKD2_HMAC_SHA1, 256));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA224AndAES_256",
CKP_PKCS5_PBKD2_HMAC_SHA224, 256));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA256AndAES_256",
CKP_PKCS5_PBKD2_HMAC_SHA256, 256));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA384AndAES_256",
CKP_PKCS5_PBKD2_HMAC_SHA384, 256));
putKeyInfo(new AESPBEKeyInfo("PBEWithHmacSHA512AndAES_256",
CKP_PKCS5_PBKD2_HMAC_SHA512, 256));
putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA1",
CKP_PKCS5_PBKD2_HMAC_SHA1));
putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA224",
CKP_PKCS5_PBKD2_HMAC_SHA224));
putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA256",
CKP_PKCS5_PBKD2_HMAC_SHA256));
putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA384",
CKP_PKCS5_PBKD2_HMAC_SHA384));
putKeyInfo(new PBKDF2KeyInfo("PBKDF2WithHmacSHA512",
CKP_PKCS5_PBKD2_HMAC_SHA512));
putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA1",
CKM_PBA_SHA1_WITH_SHA1_HMAC, 160));
putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA224",
CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, 224));
putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA256",
CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, 256));
putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA384",
CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, 384));
putKeyInfo(new P12MacPBEKeyInfo("HmacPBESHA512",
CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, 512));
}
private static void addKeyType(String name, long id) {
Long l = Long.valueOf(id);
keyTypes.put(name, l);
keyTypes.put(name.toUpperCase(Locale.ENGLISH), l);
}
// returns the PKCS11 key type of the specified algorithm
// no pseudo KeyTypes
// No pseudo key types
static long getPKCS11KeyType(String algorithm) {
long kt = getKeyType(algorithm);
if (kt == -1 || kt > PCKK_ANY) {
@ -98,30 +248,18 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
return kt;
}
// returns direct lookup result of keyTypes using algorithm
static long getKeyType(String algorithm) {
Long l = keyTypes.get(algorithm);
if (l == null) {
algorithm = algorithm.toUpperCase(Locale.ENGLISH);
l = keyTypes.get(algorithm);
if (l == null) {
if (algorithm.startsWith("HMAC")) {
return PCKK_HMAC;
} else if (algorithm.startsWith("SSLMAC")) {
return PCKK_SSLMAC;
}
}
}
return (l != null) ? l.longValue() : -1;
KeyInfo ki = getKeyInfo(algorithm);
return ki == null ? -1 : ki.keyType;
}
/**
* Convert an arbitrary key of algorithm into a P11Key of provider.
* Used in engineTranslateKey(), P11Cipher.init(), and P11Mac.init().
*/
static P11Key convertKey(Token token, Key key, String algo)
static P11Key convertKey(Token token, Key key, String svcAlgo)
throws InvalidKeyException {
return convertKey(token, key, algo, null);
return convertKey(token, key, svcAlgo, null);
}
/**
@ -129,30 +267,32 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
* P11Key of provider.
* Used in P11KeyStore.storeSkey.
*/
static P11Key convertKey(Token token, Key key, String algo,
CK_ATTRIBUTE[] extraAttrs)
throws InvalidKeyException {
static P11Key convertKey(Token token, Key key, String svcAlgo,
CK_ATTRIBUTE[] extraAttrs) throws InvalidKeyException {
token.ensureValid();
if (key == null) {
throw new InvalidKeyException("Key must not be null");
}
if (!(key instanceof SecretKey)) {
throw new InvalidKeyException("Key must be a SecretKey");
}
long algoType;
if (algo == null) {
algo = key.getAlgorithm();
algoType = getKeyType(algo);
} else {
algoType = getKeyType(algo);
long keyAlgorithmType = getKeyType(key.getAlgorithm());
if (algoType != keyAlgorithmType) {
if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) {
// ignore key algorithm for MACs
} else {
throw new InvalidKeyException
("Key algorithm must be " + algo);
}
final String keyAlgo = key.getAlgorithm();
if (keyAlgo == null) {
throw new InvalidKeyException("Key must specify its algorithm");
}
if (svcAlgo == null) {
svcAlgo = keyAlgo;
}
KeyInfo ki = null;
KeyInfo si = getKeyInfo(svcAlgo);
if (si == null) {
throw new InvalidKeyException("Unknown algorithm " + svcAlgo);
}
// Check if the key can be used for the service.
// Any key can be used for a MAC service.
if (svcAlgo != keyAlgo &&
si.keyType != PCKK_HMAC && si.keyType != PCKK_SSLMAC) {
ki = getKeyInfo(keyAlgo);
if (ki == null || !KeyInfo.checkUse(ki, si)) {
throw new InvalidKeyException("Cannot use a " + keyAlgo +
" key for a " + svcAlgo + " service");
}
}
if (key instanceof P11Key p11Key) {
@ -184,15 +324,159 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
if (p11Key != null) {
return p11Key;
}
if (!"RAW".equalsIgnoreCase(key.getFormat())) {
throw new InvalidKeyException("Encoded format must be RAW");
if (key instanceof PBEKey pbeKey) {
ki = ki == null ? getKeyInfo(keyAlgo) : ki;
if (ki instanceof PBEKeyInfo pbeKi) {
PBEKeySpec keySpec = getPbeKeySpec(pbeKey);
try {
p11Key = derivePBEKey(token, keySpec, pbeKi);
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException(e);
} finally {
keySpec.clearPassword();
}
} else {
throw new InvalidKeyException("Cannot derive unknown " +
keyAlgo + " algorithm");
}
} else {
if (si instanceof PBEKeyInfo) {
throw new InvalidKeyException("PBE service requires a PBE key");
}
if (!"RAW".equalsIgnoreCase(key.getFormat())) {
throw new InvalidKeyException("Encoded format must be RAW");
}
byte[] encoded = key.getEncoded();
try {
p11Key = createKey(token, encoded, svcAlgo, si.keyType,
extraAttrs);
} finally {
Arrays.fill(encoded, (byte) 0);
}
}
byte[] encoded = key.getEncoded();
p11Key = createKey(token, encoded, algo, algoType, extraAttrs);
token.secretCache.put(key, p11Key);
return p11Key;
}
static P11Key.P11PBEKey derivePBEKey(Token token, PBEKeySpec keySpec,
PBEKeyInfo pbeKi) throws InvalidKeySpecException {
token.ensureValid();
if (keySpec == null) {
throw new InvalidKeySpecException("PBEKeySpec must not be null");
}
Session session = null;
char[] password = null;
char[] encPassword = null;
try {
session = token.getObjSession();
CK_MECHANISM ckMech;
password = keySpec.getPassword();
byte[] salt = keySpec.getSalt();
int itCount = keySpec.getIterationCount();
int keySize = keySpec.getKeyLength();
assert password != null :
"PBEKeySpec does not allow a null password";
if (salt == null) {
throw new InvalidKeySpecException("Salt not found");
}
assert salt.length > 0 : "PBEKeySpec does not allow an empty salt";
if (itCount < 1) {
throw new InvalidKeySpecException("Iteration count must be " +
"a non-zero positive integer");
}
if (pbeKi.keyLen > 0) {
if (keySize == 0) {
keySize = pbeKi.keyLen;
} else if (keySize != pbeKi.keyLen) {
throw new InvalidKeySpecException(
"Key length is invalid for " + pbeKi.algo + " (" +
"expecting " + pbeKi.keyLen + " but was " +
keySize + ")");
}
}
if (keySize < 1 || keySize % 8 != 0) {
throw new InvalidKeySpecException("Key length must be " +
"multiple of 8 and greater than zero");
}
if (pbeKi.kdfMech == CKM_PKCS5_PBKD2) {
encPassword = P11Util.encodePassword(password,
StandardCharsets.UTF_8, 0);
CK_VERSION p11Ver = token.p11.getVersion();
if (P11Util.isNSS(token) || p11Ver.major < 2 ||
p11Ver.major == 2 && p11Ver.minor < 40) {
// NSS keeps using the old structure beyond PKCS #11 v2.40.
ckMech = new CK_MECHANISM(pbeKi.kdfMech,
new CK_PKCS5_PBKD2_PARAMS(encPassword, salt,
itCount, pbeKi.prfMech));
} else {
ckMech = new CK_MECHANISM(pbeKi.kdfMech,
new CK_PKCS5_PBKD2_PARAMS2(encPassword, salt,
itCount, pbeKi.prfMech));
}
} else {
/*
* PKCS #12 "General Method" PBKD (RFC 7292, Appendix B.2).
*
* According to PKCS #11, "password" in CK_PBE_PARAMS is of
* CK_UTF8CHAR_PTR type. While this suggests a UTF-8 encoding,
* RFC 7292 Appendix B.1 indicates that the password has to be
* encoded as a BMPString with a 2-bytes NULL terminator.
*/
encPassword = P11Util.encodePassword(password,
StandardCharsets.UTF_16BE, 2);
ckMech = new CK_MECHANISM(pbeKi.kdfMech,
new CK_PBE_PARAMS(encPassword, salt, itCount));
}
CK_ATTRIBUTE[] attrs =
new CK_ATTRIBUTE[3 + pbeKi.extraAttrs.length];
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, pbeKi.keyType);
System.arraycopy(pbeKi.extraAttrs, 0, attrs, 3,
pbeKi.extraAttrs.length);
CK_ATTRIBUTE[] attr = token.getAttributes(
O_GENERATE, CKO_SECRET_KEY, pbeKi.keyType, attrs);
long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr);
return (P11Key.P11PBEKey) P11Key.pbeKey(session, keyID, pbeKi.algo,
keySize, attr, password, salt, itCount);
} catch (PKCS11Exception e) {
throw new InvalidKeySpecException("Could not create key", e);
} finally {
if (encPassword != null) {
Arrays.fill(encPassword, '\0');
}
if (password != null) {
Arrays.fill(password, '\0');
}
token.releaseSession(session);
}
}
private static PBEKeySpec getPbeKeySpec(PBEKey pbeKey) {
int keyLength = 0;
if ("RAW".equalsIgnoreCase(pbeKey.getFormat())) {
byte[] encoded = pbeKey.getEncoded();
if (encoded != null) {
keyLength = encoded.length << 3;
Arrays.fill(encoded, (byte) 0);
}
}
int ic = pbeKey.getIterationCount();
byte[] salt = pbeKey.getSalt();
char[] pwd = pbeKey.getPassword();
try {
return keyLength == 0 ?
new PBEKeySpec(pwd, salt, ic) :
new PBEKeySpec(pwd, salt, ic, keyLength);
} finally {
if (pwd != null) {
Arrays.fill(pwd, '\0');
}
}
}
static void fixDESParity(byte[] key, int offset) {
for (int i = 0; i < 8; i++) {
int b = key[offset] & 0xfe;
@ -286,36 +570,51 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
if (keySpec == null) {
throw new InvalidKeySpecException("KeySpec must not be null");
}
if (keySpec instanceof SecretKeySpec) {
if (keySpec instanceof SecretKeySpec secretKeySpec) {
try {
Key key = convertKey(token, (SecretKey)keySpec, algorithm);
Key key = convertKey(token, secretKeySpec, algorithm);
return (SecretKey)key;
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
} else if (keySpec instanceof PBEKeySpec pbeKeySpec &&
svcPbeKi != null) {
return derivePBEKey(token, pbeKeySpec, svcPbeKi);
} else if (algorithm.equalsIgnoreCase("DES")) {
if (keySpec instanceof DESKeySpec) {
byte[] keyBytes = ((DESKeySpec)keySpec).getKey();
keySpec = new SecretKeySpec(keyBytes, "DES");
return engineGenerateSecret(keySpec);
if (keySpec instanceof DESKeySpec desKeySpec) {
return generateDESSecret(desKeySpec.getKey(), "DES");
}
} else if (algorithm.equalsIgnoreCase("DESede")) {
if (keySpec instanceof DESedeKeySpec) {
byte[] keyBytes = ((DESedeKeySpec)keySpec).getKey();
keySpec = new SecretKeySpec(keyBytes, "DESede");
return engineGenerateSecret(keySpec);
if (keySpec instanceof DESedeKeySpec desEdeKeySpec) {
return generateDESSecret(desEdeKeySpec.getKey(), "DESede");
}
}
throw new InvalidKeySpecException
("Unsupported spec: " + keySpec.getClass().getName());
}
private SecretKey generateDESSecret(byte[] keyBytes, String desAlgo)
throws InvalidKeySpecException {
SecretKeySpec secretKeySpec = null;
try {
secretKeySpec = new SecretKeySpec(keyBytes, desAlgo);
return engineGenerateSecret(secretKeySpec);
} finally {
if (secretKeySpec != null) {
SharedSecrets.getJavaxCryptoSpecAccess()
.clearSecretKeySpec(secretKeySpec);
}
if (keyBytes != null) {
Arrays.fill(keyBytes, (byte) 0);
}
}
}
private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException {
try {
key = engineTranslateKey(key);
if (!"RAW".equalsIgnoreCase(key.getFormat())) {
throw new InvalidKeySpecException
("Could not obtain key bytes");
throw new InvalidKeySpecException("Could not obtain key bytes");
}
byte[] k = key.getEncoded();
return k;
@ -334,6 +633,9 @@ final class P11SecretKeyFactory extends SecretKeyFactorySpi {
}
if (keySpec.isAssignableFrom(SecretKeySpec.class)) {
return new SecretKeySpec(getKeyBytes(key), algorithm);
} else if (keySpec.isAssignableFrom(PBEKeySpec.class) &&
key instanceof PBEKey pbeKey && svcPbeKi != null) {
return getPbeKeySpec(pbeKey);
} else if (algorithm.equalsIgnoreCase("DES")) {
try {
if (keySpec.isAssignableFrom(DESKeySpec.class)) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,6 +27,9 @@ package sun.security.pkcs11;
import java.lang.ref.Cleaner;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.*;
import sun.security.pkcs11.wrapper.PKCS11Exception;
@ -51,6 +54,46 @@ public final class P11Util {
// empty
}
static boolean isNSS(Token token) {
char[] tokenLabel = token.tokenInfo.label;
if (tokenLabel != null && tokenLabel.length >= 3) {
return (tokenLabel[0] == 'N' && tokenLabel[1] == 'S'
&& tokenLabel[2] == 'S');
}
return false;
}
static char[] encodePassword(char[] password, Charset cs,
int nullTermBytes) {
/*
* When a Java char (2 bytes) is converted to CK_UTF8CHAR (1 byte) for
* a PKCS #11 (native) call, the high-order byte is discarded (see
* jCharArrayToCKUTF8CharArray in p11_util.c). In order to have an
* encoded string passed to C_GenerateKey, we need to account for
* truncation and expand beforehand: 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" that has to be encoded in
* UTF-16 BE:
* char[] password => [ 0x0061, 0x0000 ]
* / \ / \
* ByteBuffer passwordBytes => [ 0x00, 0x61, 0x00, 0x00 ]
* | | | |
* char[] encPassword => [0x0000, 0x0061, 0x0000, 0x0000]
* | | | |
* PKCS #11 call (bytes) => [ 0x00, 0x61, 0x00, 0x00 ]
*/
ByteBuffer passwordBytes = cs.encode(CharBuffer.wrap(password));
char[] encPassword =
new char[passwordBytes.remaining() + nullTermBytes];
int i = 0;
while (passwordBytes.hasRemaining()) {
encPassword[i] = (char) (passwordBytes.get() & 0xFF);
// Erase password bytes as we read during encoding.
passwordBytes.put(i++, (byte) 0);
}
return encPassword;
}
static Provider getSunProvider() {
Provider p = sun;
if (p == null) {

View File

@ -421,14 +421,23 @@ public final class SunPKCS11 extends AuthProvider {
final String className;
final List<String> aliases;
final int[] mechanisms;
final int[] requiredMechs;
private Descriptor(String type, String algorithm, String className,
List<String> aliases, int[] mechanisms) {
List<String> aliases, int[] mechanisms) {
this(type, algorithm, className, aliases, mechanisms, null);
}
// 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, 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
@ -470,12 +479,24 @@ public final class SunPKCS11 extends AuthProvider {
register(new Descriptor(type, algorithm, className, aliases, m));
}
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) {
register(new Descriptor(type, algorithm, className,
getAliases(algorithm), m));
}
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 register(Descriptor d) {
for (int i = 0; i < d.mechanisms.length; i++) {
int m = d.mechanisms[i];
@ -526,6 +547,7 @@ public final class SunPKCS11 extends AuthProvider {
String P11KeyWrapCipher = "sun.security.pkcs11.P11KeyWrapCipher";
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";
@ -588,6 +610,24 @@ public final class SunPKCS11 extends AuthProvider {
d(MAC, "SslMacSHA1", P11Mac,
m(CKM_SSL3_SHA1_MAC));
/*
* 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(KPG, "RSA", P11KeyPairGenerator,
getAliases("PKCS1"),
m(CKM_RSA_PKCS_KEY_PAIR_GEN));
@ -686,6 +726,60 @@ public final class SunPKCS11 extends AuthProvider {
d(SKF, "ChaCha20", P11SecretKeyFactory,
m(CKM_CHACHA20_POLY1305));
/*
* 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));
/*
* 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));
@ -779,6 +873,44 @@ public final class SunPKCS11 extends AuthProvider {
d(CIP, "RSA/ECB/NoPadding", P11RSACipher,
m(CKM_RSA_X_509));
/*
* 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));
@ -1169,9 +1301,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;
}
@ -1272,6 +1416,8 @@ public final class SunPKCS11 extends AuthProvider {
} else if (algorithm.contains("/KW/") ||
algorithm.contains("/KWP/")) {
return new P11KeyWrapCipher(token, algorithm, mechanism);
} else if (algorithm.startsWith("PBE")) {
return new P11PBECipher(token, algorithm, mechanism);
} else {
return new P11Cipher(token, algorithm, mechanism);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -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();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -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
@ -193,11 +205,12 @@ public class CK_MECHANISM {
sb.append(Constants.INDENT);
sb.append("mechanism: ");
sb.append(mechanism);
sb.append(Functions.getMechanismName(mechanism));
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
sb.append("pParameter: ");
sb.append("pParameter:");
sb.append(Constants.NEWLINE);
sb.append(pParameter.toString());
sb.append(Constants.NEWLINE);

View File

@ -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.
*

View File

@ -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.
*
@ -122,7 +142,7 @@ public class CK_PKCS5_PBKD2_PARAMS {
sb.append(Constants.INDENT);
sb.append("saltSource: ");
sb.append(saltSource);
sb.append(Functions.getParamSourcesName(saltSource));
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
@ -132,7 +152,7 @@ public class CK_PKCS5_PBKD2_PARAMS {
sb.append(Constants.INDENT);
sb.append("ulSaltSourceDataLen: ");
sb.append(pSaltSourceData.length);
sb.append(Functions.getLength(pSaltSourceData));
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
@ -142,7 +162,7 @@ public class CK_PKCS5_PBKD2_PARAMS {
sb.append(Constants.INDENT);
sb.append("prf: ");
sb.append(prf);
sb.append(Functions.getPrfName(prf));
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
@ -152,7 +172,7 @@ public class CK_PKCS5_PBKD2_PARAMS {
sb.append(Constants.INDENT);
sb.append("ulPrfDataLen: ");
sb.append(pPrfData.length);
sb.append(Functions.getLength(pPrfData));
//buffer.append(Constants.NEWLINE);
return sb.toString();

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2023, 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(Functions.getParamSourcesName(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(Functions.getLength(pSaltSourceData));
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(Functions.getPrfName(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(Functions.getLength(pPrfData));
return sb.toString();
}
}

View File

@ -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();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -104,6 +104,20 @@ public class Functions {
private static final Map<String,Integer> mgfIds =
new HashMap<String,Integer>();
// Pseudo-random functions (CKP_*)
private static final Map<Integer,String> prfNames =
new HashMap<Integer,String>();
private static final Map<String,Integer> prfIds =
new HashMap<String,Integer>();
// Salt/Encoding parameter sources (CKZ_*)
private static final Map<Integer,String> paramSourcesNames =
new HashMap<Integer,String>();
private static final Map<String,Integer> paramSourcesIds =
new HashMap<String,Integer>();
/**
* For converting numbers to their hex presentation.
*/
@ -208,6 +222,16 @@ public class Functions {
return helpBigInteger.toString(2);
}
/**
* get a byte array length as int
*
* @param value the byte array to get its length
* @return the byte array length as int or 0 if null
*/
public static int getLength(byte[] value) {
return value == null ? 0 : value.length;
}
private static class Flags {
private final long[] flagIds;
private final String[] flagNames;
@ -483,6 +507,22 @@ public class Functions {
return getId(mgfIds, name);
}
public static String getPrfName(long id) {
return getName(prfNames, id);
}
public static long getPrfId(String name) {
return getId(prfIds, name);
}
public static String getParamSourcesName(long id) {
return getName(paramSourcesNames, id);
}
public static long getParamSourcesId(String name) {
return getId(paramSourcesIds, name);
}
/**
* Check the given arrays for equalitiy. This method considers both arrays as
* equal, if both are <code>null</code> or both have the same length and
@ -638,6 +678,14 @@ public class Functions {
addMapping(mgfNames, mgfIds, id, name);
}
private static void addPrf(long id, String name) {
addMapping(prfNames, prfIds, id, name);
}
private static void addParamSources(long id, String name) {
addMapping(paramSourcesNames, paramSourcesIds, id, name);
}
// The ordering here follows the PKCS11Constants class
static {
addMech(CKM_RSA_PKCS_KEY_PAIR_GEN, "CKM_RSA_PKCS_KEY_PAIR_GEN");
@ -1098,6 +1146,14 @@ public class Functions {
addMech(CKM_VENDOR_DEFINED, "CKM_VENDOR_DEFINED");
addMech(CKM_NSS_TLS_PRF_GENERAL, "CKM_NSS_TLS_PRF_GENERAL");
addMech(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN,
"CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN");
addMech(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN,
"CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN");
addMech(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN,
"CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN");
addMech(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN,
"CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN");
addMech(PCKM_SECURERANDOM, "SecureRandom");
addMech(PCKM_KEYSTORE, "KeyStore");
@ -1352,6 +1408,20 @@ public class Functions {
addMGF(CKG_MGF1_SHA3_256, "CKG_MGF1_SHA3_256");
addMGF(CKG_MGF1_SHA3_384, "CKG_MGF1_SHA3_384");
addMGF(CKG_MGF1_SHA3_512, "CKG_MGF1_SHA3_512");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA1, "CKP_PKCS5_PBKD2_HMAC_SHA1");
addPrf(CKP_PKCS5_PBKD2_HMAC_GOSTR3411,
"CKP_PKCS5_PBKD2_HMAC_GOSTR3411");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA224, "CKP_PKCS5_PBKD2_HMAC_SHA224");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA256, "CKP_PKCS5_PBKD2_HMAC_SHA256");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA384, "CKP_PKCS5_PBKD2_HMAC_SHA384");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA512, "CKP_PKCS5_PBKD2_HMAC_SHA512");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA512_224,
"CKP_PKCS5_PBKD2_HMAC_SHA512_224");
addPrf(CKP_PKCS5_PBKD2_HMAC_SHA512_256,
"CKP_PKCS5_PBKD2_HMAC_SHA512_256");
addParamSources(CKZ_SALT_SPECIFIED, "CKZ_SALT_SPECIFIED");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 2002 Graz University of Technology. All rights reserved.
@ -999,6 +999,15 @@ public interface PKCS11Constants {
// NSS private
public static final long CKM_NSS_TLS_PRF_GENERAL = 0x80000373L;
// 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;
// internal ids for our pseudo mechanisms SecureRandom and KeyStore
public static final long PCKM_SECURERANDOM = 0x7FFFFF20L;
@ -1103,7 +1112,9 @@ public interface PKCS11Constants {
public static final long CKD_BLAKE2B_256_KDF = 0x00000018L;
public static final long CKD_BLAKE2B_384_KDF = 0x00000019L;
public static final long CKD_BLAKE2B_512_KDF = 0x0000001aL;
*/
// PBKDF2 support, used in P11Util
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;
@ -1115,6 +1126,7 @@ public interface PKCS11Constants {
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;
@ -1153,9 +1165,9 @@ public interface PKCS11Constants {
// 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;
// base number of NSS private attributes. CKA_NETSCAPE_BASE is now known as
// CKM_NSS = CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS = 0x80000000 | 0x4E534350
public static final long CKA_NETSCAPE_BASE = 0xCE534350L;
// object type for NSS trust
public static final long CKO_NETSCAPE_TRUST = 0xCE534353L;

View File

@ -1512,9 +1512,11 @@ CK_VOID_PTR jMechParamToCKMechParamPtrSlow(JNIEnv *env, jobject jParam,
case CKM_RSA_PKCS_OAEP:
ckpParamPtr = jRsaPkcsOaepParamToCKRsaPkcsOaepParamPtr(env, jParam, ckpLength);
break;
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 +1660,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 +1682,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;
}
@ -1699,6 +1701,9 @@ jPbeParamToCKPbeParamPtr(JNIEnv *env, jobject jParam, CK_ULONG *pLength)
return ckParamPtr;
cleanup:
free(ckParamPtr->pInitVector);
if (ckParamPtr->pPassword != NULL) {
memset(ckParamPtr->pPassword, 0, ckParamPtr->ulPasswordLen);
}
free(ckParamPtr->pPassword);
free(ckParamPtr->pSalt);
free(ckParamPtr);
@ -1767,31 +1772,60 @@ 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 {
p11ThrowPKCS11RuntimeException(env, "Unknown PBKD2 mechanism parameters class.");
return NULL;
}
fieldID = (*env)->GetFieldID(env, jPkcs5Pbkd2ParamsClass, "saltSource", "J");
if (fieldID == NULL) { return NULL; }
jSaltSource = (*env)->GetLongField(env, jParam, fieldID);
@ -1807,36 +1841,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) {
p11ThrowOutOfMemoryError(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) {
p11ThrowOutOfMemoryError(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;

View File

@ -408,11 +408,32 @@ 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:
TRACE0("[ CK_PBE_PARAMS ]\n");
free(((CK_PBE_PARAMS_PTR)tmp)->pInitVector);
if (((CK_PBE_PARAMS_PTR)tmp)->pPassword != NULL) {
memset(((CK_PBE_PARAMS_PTR)tmp)->pPassword, 0,
((CK_PBE_PARAMS_PTR)tmp)->ulPasswordLen);
}
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_*
@ -515,12 +536,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) {
p11ThrowOutOfMemoryError(env, 0);
@ -557,12 +577,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) {
p11ThrowOutOfMemoryError(env, 0);
@ -604,12 +623,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) {
p11ThrowOutOfMemoryError(env, 0);
@ -646,12 +664,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) {
p11ThrowOutOfMemoryError(env, 0);
@ -688,12 +705,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) {
p11ThrowOutOfMemoryError(env, 0);
@ -701,19 +717,20 @@ void jCharArrayToCKUTF8CharArray(JNIEnv *env, const jcharArray jArray, CK_UTF8CH
}
(*env)->GetCharArrayRegion(env, jArray, 0, *ckpLength, jTemp);
if ((*env)->ExceptionCheck(env)) {
free(jTemp);
return;
goto cleanup;
}
*ckpArray = (CK_UTF8CHAR_PTR) calloc(*ckpLength, sizeof(CK_UTF8CHAR));
if (*ckpArray == NULL) {
free(jTemp);
p11ThrowOutOfMemoryError(env, 0);
return;
goto cleanup;
}
for (i=0; i<(*ckpLength); i++) {
(*ckpArray)[i] = jCharToCKUTF8Char(jTemp[i]);
}
cleanup:
// Clean possible temporary copies of passwords.
memset(jTemp, 0, *ckpLength * sizeof(jchar));
free(jTemp);
}

View File

@ -67,15 +67,22 @@
/* extra PKCS#11 constants not in the standard include files */
/* CKA_NETSCAPE_BASE is now known as CKM_NSS (CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS) */
#define CKA_NETSCAPE_BASE (0x80000000 + 0x4E534350)
#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)
#define CKA_NETSCAPE_TRUST_CODE_SIGNING (CKA_NETSCAPE_TRUST_BASE + 10)
#define CKA_NETSCAPE_TRUST_CODE_SIGNING (CKA_NETSCAPE_TRUST_BASE + 10)
#define CKA_NETSCAPE_TRUST_EMAIL_PROTECTION (CKA_NETSCAPE_TRUST_BASE + 11)
#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
@ -271,6 +278,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"
@ -383,7 +391,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);
@ -393,6 +401,41 @@ 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); \
if ((verParamsPtr)->params.v1.pPassword != NULL && \
(verParamsPtr)->params.v1.ulPasswordLen \
!= NULL) { \
memset((verParamsPtr)->params.v1.pPassword, 0, \
*((verParamsPtr)->params.v1.ulPasswordLen)); \
} \
free((verParamsPtr)->params.v1.pPassword); \
free((verParamsPtr)->params.v1.ulPasswordLen); \
} else { \
free((verParamsPtr)->params.v2.pSaltSourceData); \
free((verParamsPtr)->params.v2.pPrfData); \
if ((verParamsPtr)->params.v2.pPassword != NULL) { \
memset((verParamsPtr)->params.v2.pPassword, 0, \
(verParamsPtr)->params.v2.ulPasswordLen); \
} \
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);

View File

@ -0,0 +1,259 @@
/*
* Copyright (c) 2023, 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.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
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 8301553
* @summary test password based encryption on SunPKCS11's Cipher service
* @library /test/lib ..
* @run main/othervm/timeout=30 PBECipher
*/
public final class PBECipher extends PKCS11Test {
private static final char[] password = "123456".toCharArray();
private static final byte[] salt = "abcdefgh".getBytes(
StandardCharsets.UTF_8);
private static final int iterations = 1000;
private static final int AES_BLOCK_SIZE = 16;
private static final PBEParameterSpec pbeSpec = new PBEParameterSpec(salt,
iterations, new IvParameterSpec(new byte[AES_BLOCK_SIZE]));
private static final String plainText = "This is a known plain text!";
private static final String sep = "======================================" +
"===================================";
private enum Configuration {
// Pass salt and iterations to a Cipher through a PBEParameterSpec.
PBEParameterSpec,
// Derive a key using SunPKCS11's SecretKeyFactory (wrapping password,
// salt and iterations in a PBEKeySpec), and pass it to a Cipher.
SecretKeyFactoryDerivedKey,
// Pass salt and iterations to a Cipher through an AlgorithmParameters.
AlgorithmParameters,
// Pass password, salt and iterations and iterations to
// a Cipher through an anonymous class implementing the
// javax.crypto.interfaces.PBEKey interface.
AnonymousPBEKey,
}
private static Provider sunJCE = Security.getProvider("SunJCE");
private record AssertionData(String pbeCipherAlgo, String cipherAlgo,
BigInteger expectedCiphertext) {}
private static AssertionData cipherAssertionData(String pbeCipherAlgo,
String cipherAlgo, String staticExpectedCiphertextString) {
BigInteger staticExpectedCiphertext =
new BigInteger(staticExpectedCiphertextString, 16);
BigInteger expectedCiphertext = null;
if (sunJCE != null) {
try {
expectedCiphertext = computeCipherText(sunJCE, pbeCipherAlgo,
pbeCipherAlgo, Configuration.PBEParameterSpec);
checkAssertionValues(expectedCiphertext,
staticExpectedCiphertext);
} catch (GeneralSecurityException e) {
// Move to staticExpectedCiphertext as it's unlikely
// that any of the algorithms are available.
sunJCE = null;
}
}
if (expectedCiphertext == null) {
expectedCiphertext = staticExpectedCiphertext;
}
return new AssertionData(pbeCipherAlgo, cipherAlgo, expectedCiphertext);
}
private static void checkAssertionValues(BigInteger expectedValue,
BigInteger staticExpectedValue) {
if (!expectedValue.equals(staticExpectedValue)) {
printHex("SunJCE value", expectedValue);
printHex("Static value", staticExpectedValue);
throw new Error("Static and SunJCE values do not match.");
}
}
// Generated with SunJCE.
private static final AssertionData[] assertionData = new AssertionData[]{
cipherAssertionData("PBEWithHmacSHA1AndAES_128",
"AES/CBC/PKCS5Padding", "ba1c9614d550912925d99e0bc8969032" +
"7ac6258b72117dcf750c19ee6ca73dd4"),
cipherAssertionData("PBEWithHmacSHA224AndAES_128",
"AES/CBC/PKCS5Padding", "41960c43ca99cf2184511aaf2f0508a9" +
"7da3762ee6c2b7e2027c8076811f2e52"),
cipherAssertionData("PBEWithHmacSHA256AndAES_128",
"AES/CBC/PKCS5Padding", "6bb6a3dc3834e81e5ca6b5e70073ff46" +
"903b188940a269ed26db2ffe622b8e16"),
cipherAssertionData("PBEWithHmacSHA384AndAES_128",
"AES/CBC/PKCS5Padding", "22aabf7a6a059415dc4ca7d985f3de06" +
"8f8300ca48d8de585d802670f4c1d9bd"),
cipherAssertionData("PBEWithHmacSHA512AndAES_128",
"AES/CBC/PKCS5Padding", "b523e7c462a0b7fd74e492b3a6550464" +
"ceebe81f08649ae163673afc242ad8a2"),
cipherAssertionData("PBEWithHmacSHA1AndAES_256",
"AES/CBC/PKCS5Padding", "1e7c25e166afae069cec68ef9affca61" +
"aea02ab1c3dc7471cb767ed7d6e37af0"),
cipherAssertionData("PBEWithHmacSHA224AndAES_256",
"AES/CBC/PKCS5Padding", "6701f1cc75b6494ec4bd27158aa2c15d" +
"7d10bc2f1fbb7d92d8277c7edfd1dd57"),
cipherAssertionData("PBEWithHmacSHA256AndAES_256",
"AES/CBC/PKCS5Padding", "f82eb2fc016505baeb23ecdf85163933" +
"5e8d6d48b48631185641febb75898a1d"),
cipherAssertionData("PBEWithHmacSHA384AndAES_256",
"AES/CBC/PKCS5Padding", "ee9528022e58cdd9be80cd88443e03b3" +
"de13376cf97c53d946d5c5dfc88097be"),
cipherAssertionData("PBEWithHmacSHA512AndAES_256",
"AES/CBC/PKCS5Padding", "18f472912ffaa31824e20a5486324e14" +
"0225e20cb158762e8647b1216fe0ab7e"),
};
private static final class NoRandom extends SecureRandom {
@Override
public void nextBytes(byte[] bytes) {}
}
public void main(Provider sunPKCS11) throws Exception {
System.out.println("SunPKCS11: " + sunPKCS11.getName());
for (Configuration conf : Configuration.values()) {
for (AssertionData data : assertionData) {
testWith(sunPKCS11, data, true, conf);
if (conf != Configuration.PBEParameterSpec &&
conf != Configuration.AlgorithmParameters) {
testWith(sunPKCS11, data, false, conf);
}
}
}
System.out.println("TEST PASS - OK");
}
private static void testWith(Provider sunPKCS11, AssertionData data,
boolean testPBEService, Configuration conf) throws Exception {
String svcAlgo = testPBEService ? data.pbeCipherAlgo : data.cipherAlgo;
System.out.println(sep + System.lineSeparator() + svcAlgo
+ " (with " + conf.name() + ")");
BigInteger cipherText = computeCipherText(sunPKCS11, svcAlgo,
data.pbeCipherAlgo, conf);
printHex("Cipher Text", cipherText);
if (!cipherText.equals(data.expectedCiphertext)) {
printHex("Expected Cipher Text", data.expectedCiphertext);
throw new Exception("Expected Cipher Text did not match");
}
}
private static BigInteger computeCipherText(Provider p, String svcAlgo,
String keyAlgo, Configuration conf)
throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance(svcAlgo, p);
switch (conf) {
case PBEParameterSpec, AlgorithmParameters -> {
SecretKey key = getPasswordOnlyPBEKey();
switch (conf) {
case PBEParameterSpec -> {
cipher.init(Cipher.ENCRYPT_MODE, key, pbeSpec);
}
case AlgorithmParameters -> {
AlgorithmParameters algoParams =
AlgorithmParameters.getInstance("PBES2");
algoParams.init(pbeSpec);
cipher.init(Cipher.ENCRYPT_MODE, key, algoParams);
}
}
}
case SecretKeyFactoryDerivedKey -> {
SecretKey key = getDerivedSecretKey(p, keyAlgo);
cipher.init(Cipher.ENCRYPT_MODE, key,
pbeSpec.getParameterSpec());
}
case AnonymousPBEKey -> {
SecretKey key = getAnonymousPBEKey(keyAlgo,
svcAlgo.equals(keyAlgo));
cipher.init(Cipher.ENCRYPT_MODE, key, new NoRandom());
}
}
return new BigInteger(1, cipher.doFinal(
plainText.getBytes(StandardCharsets.UTF_8)));
}
private static SecretKey getPasswordOnlyPBEKey()
throws GeneralSecurityException {
return SecretKeyFactory.getInstance("PBE")
.generateSecret(new PBEKeySpec(password));
}
private static SecretKey getDerivedSecretKey(Provider sunPKCS11,
String algorithm) throws GeneralSecurityException {
return SecretKeyFactory.getInstance(algorithm, sunPKCS11)
.generateSecret(new PBEKeySpec(password, salt, iterations));
}
private static SecretKey getAnonymousPBEKey(String algorithm,
boolean isPbeCipherSvc) {
return new PBEKey() {
public byte[] getSalt() { return salt.clone(); }
public int getIterationCount() { return iterations; }
public String getAlgorithm() { return algorithm; }
public String getFormat() { return "RAW"; }
public char[] getPassword() { return password.clone(); }
public byte[] getEncoded() {
byte[] encodedKey = null;
if (isPbeCipherSvc) {
encodedKey = new byte[password.length];
for (int i = 0; i < password.length; i++) {
encodedKey[i] = (byte) (password[i] & 0x7f);
}
}
return encodedKey;
}
};
}
private static void printHex(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 {
main(new PBECipher());
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2023, 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.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/*
* @test
* @bug 8301553
* @summary test SunPKCS11's password based privacy and integrity
* applied to PKCS #12 keystores
* @library /test/lib ..
* @modules java.base/sun.security.util
* @run main/othervm/timeout=30 ImportKeyToP12
*/
public final class ImportKeyToP12 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) {
// Make sure that SunPKCS11 implements the Cipher algorithm
Cipher.getInstance(pbeCipherAlg, sunPKCS11);
testWith(sunPKCS11, pbeCipherAlg, pbeMacAlgs[0]);
}
// Test all integrity PBE algorithms with a privacy algorithm fixed
for (String pbeMacAlg : pbeMacAlgs) {
// Make sure that SunPKCS11 implements the Mac algorithm
Mac.getInstance(pbeMacAlg, sunPKCS11);
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 ImportKeyToP12());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,12 +37,10 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class MacSameTest extends PKCS11Test {
@ -69,14 +67,10 @@ public class MacSameTest extends PKCS11Test {
public void main(Provider p) {
List<String> algorithms = getSupportedAlgorithms("Mac", "Hmac", p);
boolean success = true;
SecureRandom srdm = new SecureRandom();
for (String alg : algorithms) {
// first try w/ java secret key object
byte[] keyVal = new byte[KEY_SIZE];
srdm.nextBytes(keyVal);
SecretKey skey = new SecretKeySpec(keyVal, alg);
SecretKey skey = generateKey(alg, KEY_SIZE);
try {
doTest(alg, skey, p);
} catch (Exception e) {
@ -86,6 +80,7 @@ public class MacSameTest extends PKCS11Test {
}
try {
// No KeyGenerator support for PBE
KeyGenerator kg = KeyGenerator.getInstance(alg, p);
kg.init(KEY_SIZE);
skey = kg.generateKey();

View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2023, 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.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.Security;
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 8301553
* @summary test password based authentication on SunPKCS11's Mac service
* @library /test/lib ..
* @run main/othervm/timeout=30 PBAMac
*/
public final class PBAMac extends PKCS11Test {
private static final char[] password = "123456".toCharArray();
private static final byte[] salt = "abcdefgh".getBytes(
StandardCharsets.UTF_8);
private static final int iterations = 1000;
private static final String plainText = "This is a known plain text!";
private static final String sep = "======================================" +
"===================================";
private enum Configuration {
// Pass salt and iterations to a Mac through a PBEParameterSpec.
PBEParameterSpec,
// Derive a key using SunPKCS11's SecretKeyFactory (wrapping password,
// salt and iterations in a PBEKeySpec), and pass it to a Mac.
SecretKeyFactoryDerivedKey,
// Pass password, salt and iterations and iterations to
// a Mac through an anonymous class implementing the
// javax.crypto.interfaces.PBEKey interface.
AnonymousPBEKey,
}
private static Provider sunJCE = Security.getProvider("SunJCE");
private record AssertionData(String pbeHmacAlgo, String hmacAlgo,
BigInteger expectedMac) {}
private static AssertionData macAssertionData(String pbeHmacAlgo,
String hmacAlgo, String staticExpectedMacString) {
BigInteger staticExpectedMac = new BigInteger(staticExpectedMacString,
16);
BigInteger expectedMac = null;
if (sunJCE != null) {
try {
expectedMac = computeMac(sunJCE, pbeHmacAlgo,
pbeHmacAlgo, Configuration.PBEParameterSpec);
checkAssertionValues(expectedMac, staticExpectedMac);
} catch (GeneralSecurityException e) {
// Move to staticExpectedMac as it's unlikely
// that any of the algorithms are available.
sunJCE = null;
}
}
if (expectedMac == null) {
expectedMac = staticExpectedMac;
}
return new AssertionData(pbeHmacAlgo, hmacAlgo, expectedMac);
}
private static void checkAssertionValues(BigInteger expectedValue,
BigInteger staticExpectedValue) {
if (!expectedValue.equals(staticExpectedValue)) {
printHex("SunJCE value", expectedValue);
printHex("Static value", staticExpectedValue);
throw new Error("Static and SunJCE values do not match.");
}
}
// Generated with SunJCE.
private static final AssertionData[] assertionData = new AssertionData[]{
macAssertionData("HmacPBESHA1", "HmacSHA1",
"707606929395e4297adc63d520ac7d22f3f5fa66"),
macAssertionData("HmacPBESHA224", "HmacSHA224",
"4ffb5ad4974a7a9fca5a36ebe3e34dd443c07fb68c392f8b611657e6"),
macAssertionData("HmacPBESHA256", "HmacSHA256",
"9e8c102c212d2fd1334dc497acb4e002b04e84713b7eda5a63807af2" +
"989d3e50"),
macAssertionData("HmacPBESHA384", "HmacSHA384",
"77f31a785d4f2220251143a4ba80f5610d9d0aeaebb4a278b8a7535c" +
"8cea8e8211809ba450458e351c5b66d691839c23"),
macAssertionData("HmacPBESHA512", "HmacSHA512",
"a53f942a844b234a69c1f92cba20ef272c4394a3cf4024dc16d9dbac" +
"1969870b1c2b28b897149a1a3b9ad80a7ca8c547dfabf3ed5f144c6b" +
"593900b62e120c45"),
};
public void main(Provider sunPKCS11) throws Exception {
System.out.println("SunPKCS11: " + sunPKCS11.getName());
for (Configuration conf : Configuration.values()) {
for (AssertionData data : assertionData) {
testWith(sunPKCS11, data, true, conf);
if (conf != Configuration.PBEParameterSpec) {
testWith(sunPKCS11, data, false, conf);
}
}
}
System.out.println("TEST PASS - OK");
}
private static void testWith(Provider sunPKCS11, AssertionData data,
boolean testPBEService, Configuration conf) throws Exception {
String svcAlgo = testPBEService ? data.pbeHmacAlgo : data.hmacAlgo;
System.out.println(sep + System.lineSeparator() + svcAlgo
+ " (with " + conf.name() + ")");
BigInteger mac = computeMac(sunPKCS11, svcAlgo, data.pbeHmacAlgo, conf);
printHex("HMAC", mac);
if (!mac.equals(data.expectedMac)) {
printHex("Expected HMAC", data.expectedMac);
throw new Exception("Expected HMAC did not match");
}
}
private static BigInteger computeMac(Provider p, String svcAlgo,
String keyAlgo, Configuration conf)
throws GeneralSecurityException {
Mac mac = Mac.getInstance(svcAlgo, p);
switch (conf) {
case PBEParameterSpec -> {
SecretKey key = getPasswordOnlyPBEKey();
mac.init(key, new PBEParameterSpec(salt, iterations));
}
case SecretKeyFactoryDerivedKey -> {
SecretKey key = getDerivedSecretKey(p, keyAlgo);
mac.init(key);
}
case AnonymousPBEKey -> {
SecretKey key = getAnonymousPBEKey(keyAlgo);
mac.init(key);
}
}
return new BigInteger(1, mac.doFinal(
plainText.getBytes(StandardCharsets.UTF_8)));
}
private static SecretKey getPasswordOnlyPBEKey()
throws GeneralSecurityException {
return SecretKeyFactory.getInstance("PBE")
.generateSecret(new PBEKeySpec(password));
}
private static SecretKey getDerivedSecretKey(Provider sunPKCS11,
String algorithm) throws GeneralSecurityException {
return SecretKeyFactory.getInstance(algorithm, sunPKCS11)
.generateSecret(new PBEKeySpec(password, salt, iterations));
}
private static SecretKey getAnonymousPBEKey(String algorithm) {
return new PBEKey() {
public byte[] getSalt() { return salt.clone(); }
public int getIterationCount() { return iterations; }
public String getAlgorithm() { return algorithm; }
public String getFormat() { return "RAW"; }
public char[] getPassword() { return password.clone(); }
public byte[] getEncoded() { return null; }
};
}
private static void printHex(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 {
main(new PBAMac());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,7 +37,7 @@ import java.security.Provider;
import java.util.Random;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKey;
public class ReinitMac extends PKCS11Test {
@ -51,13 +51,12 @@ public class ReinitMac extends PKCS11Test {
Random random = new Random();
byte[] data = new byte[10 * 1024];
random.nextBytes(data);
byte[] keyVal = new byte[16];
random.nextBytes(keyVal);
boolean success = true;
for (String alg : algorithms) {
SecretKey skey = generateKey(alg, 16);
try {
doTest(alg, p, keyVal, data);
doTest(alg, p, skey, data);
} catch (Exception e) {
System.out.println("Unexpected exception: " + e);
e.printStackTrace();
@ -72,10 +71,9 @@ public class ReinitMac extends PKCS11Test {
}
}
private void doTest(String alg, Provider p, byte[] keyVal, byte[] data)
private void doTest(String alg, Provider p, SecretKey key, byte[] data)
throws Exception {
System.out.println("Testing " + alg);
SecretKeySpec key = new SecretKeySpec(keyVal, alg);
Mac mac = Mac.getInstance(alg, p);
mac.init(key);
mac.init(key);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -40,6 +40,7 @@ import java.security.NoSuchProviderException;
import java.security.Policy;
import java.security.Provider;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
@ -55,6 +56,9 @@ import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import jdk.test.lib.artifacts.Artifact;
import jdk.test.lib.artifacts.ArtifactResolver;
import jdk.test.lib.artifacts.ArtifactResolverException;
@ -828,6 +832,58 @@ public abstract class PKCS11Test {
return algorithms;
}
private static final SecureRandom srdm = new SecureRandom();
static SecretKey generateKey(String alg, int keySize) {
if (alg.contains("PBE")) {
return generateKeyPBE(alg, keySize);
} else {
return generateKeyNonPBE(alg, keySize);
}
}
private static SecretKey generateKeyNonPBE(String alg, int keySize) {
byte[] keyVal = new byte[keySize];
srdm.nextBytes(keyVal);
return new SecretKeySpec(keyVal, alg);
}
private static SecretKey generateKeyPBE(String alg, int keySize) {
char[] pass = new char[keySize];
for (int i = 0; i < pass.length; i++) {
pass[i] = (char) ('0' + srdm.nextInt(74));
}
byte[] salt = new byte[srdm.nextInt(8, 16)];
srdm.nextBytes(salt);
int iterations = srdm.nextInt(1, 1000);
return new javax.crypto.interfaces.PBEKey() {
@Override
public String getAlgorithm() {
return "PBE";
}
@Override
public String getFormat() {
return null;
}
@Override
public byte[] getEncoded() {
throw new RuntimeException("Should not be called");
}
@Override
public char[] getPassword() {
return pass;
}
@Override
public byte[] getSalt() {
return salt;
}
@Override
public int getIterationCount() {
return iterations;
}
};
}
static byte[] generateData(int length) {
byte data[] = new byte[length];
for (int i=0; i<data.length; i++) {

View File

@ -0,0 +1,517 @@
/*
* Copyright (c) 2023, 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.lang.ReflectiveOperationException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.interfaces.PBEKey;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/*
* @test
* @bug 8301553
* @summary test key derivation on a SunPKCS11 SecretKeyFactory service
* @library /test/lib ..
* @modules java.base/com.sun.crypto.provider:open
* @run main/othervm/timeout=30 TestPBKD
*/
public final class TestPBKD extends PKCS11Test {
private static final String sep = "======================================" +
"===================================";
private enum Configuration {
// Pass password, salt and iterations to a
// SecretKeyFactory through a PBEKeySpec.
PBEKeySpec,
// Pass password, salt and iterations and iterations to a
// SecretKeyFactory through an anonymous class implementing
// the javax.crypto.interfaces.PBEKey interface.
AnonymousPBEKey,
}
private static Provider sunJCE = Security.getProvider("SunJCE");
private static BigInteger i(byte[] data) {
return new BigInteger(1, data);
}
private record AssertionData(String algo, PBEKeySpec keySpec,
BigInteger expectedKey) {}
private static AssertionData p12PBKDAssertionData(String algo,
char[] password, int keyLen, String hashAlgo, int blockLen,
String staticExpectedKeyString) {
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen);
BigInteger staticExpectedKey = new BigInteger(staticExpectedKeyString,
16);
BigInteger expectedKey;
try {
// 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);
expectedKey = i((byte[]) deriveMethod.invoke(null,
keySpec.getPassword(), keySpec.getSalt(),
keySpec.getIterationCount(), keySpec.getKeyLength() / 8,
MAC_KEY, hashAlgo, blockLen));
checkAssertionValues(expectedKey, staticExpectedKey);
} catch (ReflectiveOperationException ignored) {
expectedKey = staticExpectedKey;
}
return new AssertionData(algo, keySpec, expectedKey);
}
private static AssertionData pbkd2AssertionData(String algo,
char[] password, int keyLen, String kdfAlgo,
String staticExpectedKeyString) {
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterations, keyLen);
BigInteger staticExpectedKey = new BigInteger(staticExpectedKeyString,
16);
BigInteger expectedKey = null;
if (sunJCE != null) {
try {
expectedKey = i(SecretKeyFactory.getInstance(kdfAlgo, sunJCE)
.generateSecret(keySpec).getEncoded());
checkAssertionValues(expectedKey, staticExpectedKey);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
// Move to staticExpectedKey as it's unlikely
// that any of the algorithms are available.
sunJCE = null;
}
}
if (expectedKey == null) {
expectedKey = staticExpectedKey;
}
return new AssertionData(algo, keySpec, expectedKey);
}
private static void checkAssertionValues(BigInteger expectedValue,
BigInteger staticExpectedValue) {
if (!expectedValue.equals(staticExpectedValue)) {
printHex("SunJCE value", expectedValue);
printHex("Static value", staticExpectedValue);
throw new Error("Static and SunJCE values do not match.");
}
}
private static final char[] pwd = "123456\uA4F7".toCharArray();
private static final char[] emptyPwd = new char[0];
private static final byte[] salt = "abcdefgh".getBytes(
StandardCharsets.UTF_8);
private static final int iterations = 1000;
// Generated with SunJCE. Keep a reference to some
// entries for tests executing invalid conditions.
private static final AssertionData hmacPBESHA1Data =
p12PBKDAssertionData("HmacPBESHA1", pwd, 160, "SHA-1", 64,
"13156c6bee8e13ef568231e0174651afa5a358b0");
private static final AssertionData hmacPBESHA224Data =
p12PBKDAssertionData("HmacPBESHA224", pwd, 224, "SHA-224", 64,
"d93acf4b3bea8a89d098e290928840c0b693a30cad0117f70ace50c2");
private static final AssertionData pbeWithHmacSHA512AndAES256Data =
pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", pwd, 256,
"PBKDF2WithHmacSHA512", "845560159e2f3f51dad8d6e0feccc898" +
"7e3077595f90b60ab96d4f29203927b0");
private static final AssertionData pbkdf2WithHmacSHA256Data =
pbkd2AssertionData("PBKDF2WithHmacSHA256", pwd, 384,
"PBKDF2WithHmacSHA256", "6851e387278dd5a3a0d05e4d742f59d8" +
"44984e3e9b619488a42b93dd6453f630ae3e2ad7ed809fa9e98a7921" +
"87d62e84");
private static final AssertionData[] assertionData = new AssertionData[]{
hmacPBESHA1Data,
hmacPBESHA224Data,
p12PBKDAssertionData("HmacPBESHA256", pwd, 256, "SHA-256", 64,
"1bb3ed1ffb784ed32f59b4d7515971699af99cf67a2e574000964c8e" +
"1eba1c45"),
p12PBKDAssertionData("HmacPBESHA384", pwd, 384, "SHA-384", 128,
"d4ce121d3cec88a8c8b0c6225f7f996b72d76017c2d91bc51fd47985" +
"86d1012d1ad03a39fdcd0fdc438d164ab50259fc"),
p12PBKDAssertionData("HmacPBESHA512", pwd, 512, "SHA-512", 128,
"5f80b350986e5156669193eaa42a107e7d6636d82fb550f67af5b2c2" +
"f546d977b70e52bbbcb6bb8976f9d3f0eaf9bfef5306c50ee5ccda3e" +
"e4c4c7c8421fe4d"),
pbkd2AssertionData("PBEWithHmacSHA1AndAES_128", pwd, 128,
"PBKDF2WithHmacSHA1", "29958f3f1c942e50903189eb7f1ba09d"),
pbkd2AssertionData("PBEWithHmacSHA224AndAES_128", pwd, 128,
"PBKDF2WithHmacSHA224", "e328140e31f4ffb15af806986c23ee4e"),
pbkd2AssertionData("PBEWithHmacSHA256AndAES_128", pwd, 128,
"PBKDF2WithHmacSHA256", "6851e387278dd5a3a0d05e4d742f59d8"),
pbkd2AssertionData("PBEWithHmacSHA384AndAES_128", pwd, 128,
"PBKDF2WithHmacSHA384", "5570e2fb1a664910f055b71643b52351"),
pbkd2AssertionData("PBEWithHmacSHA512AndAES_128", pwd, 128,
"PBKDF2WithHmacSHA512", "845560159e2f3f51dad8d6e0feccc898"),
pbkd2AssertionData("PBEWithHmacSHA1AndAES_256", pwd, 256,
"PBKDF2WithHmacSHA1", "29958f3f1c942e50903189eb7f1ba09d40" +
"b5552da5e645dad4b5911ce0f2f06b"),
pbkd2AssertionData("PBEWithHmacSHA224AndAES_256", pwd, 256,
"PBKDF2WithHmacSHA224", "e328140e31f4ffb15af806986c23ee4e" +
"7daa2119fee8c64aef7c1f4c1871724e"),
pbkd2AssertionData("PBEWithHmacSHA256AndAES_256", pwd, 256,
"PBKDF2WithHmacSHA256", "6851e387278dd5a3a0d05e4d742f59d8" +
"44984e3e9b619488a42b93dd6453f630"),
pbkd2AssertionData("PBEWithHmacSHA384AndAES_256", pwd, 256,
"PBKDF2WithHmacSHA384", "5570e2fb1a664910f055b71643b52351" +
"d7d0ad3a18912086f80d974f2acc2efb"),
pbeWithHmacSHA512AndAES256Data,
pbkd2AssertionData("PBKDF2WithHmacSHA1", pwd, 240,
"PBKDF2WithHmacSHA1", "29958f3f1c942e50903189eb7f1ba09d40" +
"b5552da5e645dad4b5911ce0f2"),
pbkd2AssertionData("PBKDF2WithHmacSHA224", pwd, 336,
"PBKDF2WithHmacSHA224", "e328140e31f4ffb15af806986c23ee4e" +
"7daa2119fee8c64aef7c1f4c1871724e0ea628577e0ab54fa7c6"),
pbkdf2WithHmacSHA256Data,
pbkd2AssertionData("PBKDF2WithHmacSHA384", pwd, 576,
"PBKDF2WithHmacSHA384", "5570e2fb1a664910f055b71643b52351" +
"d7d0ad3a18912086f80d974f2acc2efba52650d4bf872455820f24c8" +
"46742161da84a1b4c3f197f4347308e8841a8971cf686aef29107396"),
pbkd2AssertionData("PBKDF2WithHmacSHA512", pwd, 768,
"PBKDF2WithHmacSHA512", "845560159e2f3f51dad8d6e0feccc898" +
"7e3077595f90b60ab96d4f29203927b00aa1a11e4d19d4f275a7f453" +
"14be500dacc3c1de9f704827b396463ccaa8957344d41bd64d9d09ff" +
"474e776469d326b1ee6ee5a5d854b86d3d7a25084afd6d6f"),
p12PBKDAssertionData("HmacPBESHA512", emptyPwd, 512, "SHA-512",
128, "90b6e088490c6c5e6b6e81209bd769d27df3868cae79591577a" +
"c35b46e4c6ebcc4b90f4943e3cb165f9d1789d938235f4b35ba74df9" +
"e509fbbb7aa329a432445"),
pbkd2AssertionData("PBEWithHmacSHA512AndAES_256", emptyPwd, 256,
"PBKDF2WithHmacSHA512", "3a5c5fd11e4d381b32e11baa93d7b128" +
"09e016e48e0542c5d3453fc240a0fa76"),
};
public void main(Provider sunPKCS11) throws Exception {
System.out.println("SunPKCS11: " + sunPKCS11.getName());
// Test valid cases.
for (Configuration conf : Configuration.values()) {
for (AssertionData data : assertionData) {
testValidWith(sunPKCS11, data, conf);
}
}
// Test invalid cases.
testInvalidTranslateKey(sunPKCS11);
testInvalidGenerateSecret(sunPKCS11);
testInvalidGetKeySpec(sunPKCS11);
System.out.println("TEST PASS - OK");
}
private static void testValidWith(Provider sunPKCS11, AssertionData data,
Configuration conf) throws Exception {
System.out.println(sep + System.lineSeparator() + data.algo
+ " (with " + conf.name() + ")");
SecretKeyFactory skf = SecretKeyFactory.getInstance(data.algo,
sunPKCS11);
SecretKey derivedKey = switch (conf) {
case PBEKeySpec -> skf.generateSecret(data.keySpec);
case AnonymousPBEKey -> skf.translateKey(getAnonymousPBEKey(
data.algo, data.keySpec));
};
BigInteger derivedKeyValue = i(derivedKey.getEncoded());
printHex("Derived Key", derivedKeyValue);
if (!derivedKeyValue.equals(data.expectedKey)) {
printHex("Expected Derived Key", data.expectedKey);
throw new Exception("Expected Derived Key did not match");
}
if (skf.translateKey(derivedKey) != derivedKey) {
throw new Exception("SecretKeyFactory::translateKey must return " +
"the same key when a P11PBEKey from the same token is " +
"passed");
}
testGetKeySpec(data, skf, derivedKey);
if (sunJCE != null && data.algo.startsWith("PBKDF2")) {
testTranslateP11PBEKeyToSunJCE(data.algo, (PBEKey) derivedKey);
}
}
private static SecretKey getAnonymousPBEKey(String algorithm,
PBEKeySpec keySpec) {
return new PBEKey() {
public byte[] getSalt() { return keySpec.getSalt(); }
public int getIterationCount() {
return keySpec.getIterationCount();
}
public String getAlgorithm() { return algorithm; }
public String getFormat() { return "RAW"; }
public char[] getPassword() { return keySpec.getPassword(); }
public byte[] getEncoded() {
return new byte[keySpec.getKeyLength() / 8];
}
};
}
private static void printHex(String title, BigInteger b) {
String repr = (b == null) ? "buffer is null" : b.toString(16);
System.out.println(title + ": " + repr + System.lineSeparator());
}
private static void testGetKeySpec(AssertionData data,
SecretKeyFactory skf, SecretKey derivedKey) throws Exception {
System.out.println(sep + System.lineSeparator()
+ "SecretKeyFactory::getKeySpec() (for " + data.algo + ")");
KeySpec skfKeySpec = skf.getKeySpec(derivedKey, PBEKeySpec.class);
if (skfKeySpec instanceof PBEKeySpec skfPBEKeySpec) {
char[] specPassword = skfPBEKeySpec.getPassword();
byte[] specSalt = skfPBEKeySpec.getSalt();
int specIterations = skfPBEKeySpec.getIterationCount();
int specKeyLength = skfPBEKeySpec.getKeyLength();
System.out.println(" spec key length (bits): " + specKeyLength);
System.out.println(" spec password: "
+ String.valueOf(specPassword));
System.out.println(" spec iteration count: " + specIterations);
printHex(" spec salt", i(specSalt));
if (!Arrays.equals(specPassword, data.keySpec.getPassword())) {
throw new Exception("Password differs");
}
if (!Arrays.equals(specSalt, data.keySpec.getSalt())) {
throw new Exception("Salt differs");
}
if (specIterations != data.keySpec.getIterationCount()) {
throw new Exception("Iteration count differs");
}
if (specKeyLength != data.keySpec.getKeyLength()) {
throw new Exception("Key length differs");
}
} else {
throw new Exception("Invalid key spec type: " + skfKeySpec);
}
// Test extracting key bytes with a SecretKeySpec.
SecretKeySpec secretKeySpec = (SecretKeySpec)
skf.getKeySpec(derivedKey, SecretKeySpec.class);
if (!Arrays.equals(secretKeySpec.getEncoded(),
derivedKey.getEncoded())) {
throw new Exception("Unable to extract key bytes with a " +
"SecretKeySpec");
}
}
private static void testTranslateP11PBEKeyToSunJCE(String algorithm,
PBEKey p11PbeK) throws Exception {
System.out.println(sep + System.lineSeparator()
+ "Translate P11PBEKey to SunJCE (for " + algorithm + ")");
SecretKey jceK = SecretKeyFactory.getInstance(algorithm, sunJCE)
.translateKey(p11PbeK);
BigInteger jceEncoded = i(jceK.getEncoded());
printHex(" translated to SunJCE", jceEncoded);
if (jceK instanceof PBEKey jcePbeK) {
if (!Arrays.equals(jcePbeK.getPassword(), p11PbeK.getPassword())) {
throw new Exception("Password differs");
}
if (!Arrays.equals(jcePbeK.getSalt(), p11PbeK.getSalt())) {
throw new Exception("Salt differs");
}
if (jcePbeK.getIterationCount() != p11PbeK.getIterationCount()) {
throw new Exception("Iteration count differs");
}
if (!jceEncoded.equals(i(p11PbeK.getEncoded()))) {
throw new Exception("Encoded key differs");
}
} else {
throw new Exception("Unexpected key type for SunJCE key: "
+ jceK.getClass().getName());
}
}
@FunctionalInterface
private interface Action {
void run() throws Exception;
}
private static void assertThrows(Class<?> expectedExc, String expectedMsg,
Action action) throws Exception {
String shtExpected = "Should have thrown '"
+ expectedExc.getSimpleName() + ": " + expectedMsg + "'";
try {
action.run();
} catch (Exception e) {
if (expectedExc.isAssignableFrom(e.getClass()) &&
e.getMessage().equals(expectedMsg)) {
return;
}
e.printStackTrace();
throw new Exception(shtExpected + ", but threw '" +
e.getClass().getSimpleName() + ": " + e.getMessage() + "'");
}
throw new Exception(shtExpected + ", but it didn't throw");
}
private static void testInvalidTranslateKey(Provider sunPKCS11)
throws Exception {
System.out.println(sep + System.lineSeparator()
+ "Invalid SecretKeyFactory::translateKey tests");
SecretKeyFactory skf1 = SecretKeyFactory.getInstance(
hmacPBESHA1Data.algo, sunPKCS11);
SecretKeyFactory skf2 = SecretKeyFactory.getInstance("AES", sunPKCS11);
SecretKeyFactory skf3 = SecretKeyFactory.getInstance(
pbkdf2WithHmacSHA256Data.algo, sunPKCS11);
PBEKey p11PbeKey = (PBEKey) skf1.translateKey(getAnonymousPBEKey(
skf1.getAlgorithm(), hmacPBESHA1Data.keySpec));
Class<?> e = InvalidKeyException.class;
System.out.println(" * Non-PBEKey key to PBE SecretKeyFactory");
assertThrows(e, "PBE service requires a PBE key",
() -> skf1.translateKey(new SecretKeySpec(
new byte[10], hmacPBESHA1Data.algo)));
System.out.println(" * PBEKey key to PBE SecretKeyFactory of a " +
"different algorithm");
assertThrows(e, "Cannot use a " + hmacPBESHA1Data.algo + " key for a " +
hmacPBESHA224Data.algo + " service",
() -> SecretKeyFactory.getInstance(hmacPBESHA224Data.algo,
sunPKCS11).translateKey(p11PbeKey));
System.out.println(" * Non-AES PBEKey key to AES SecretKeyFactory");
assertThrows(e, "Cannot use a " + hmacPBESHA1Data.algo + " key for a " +
skf2.getAlgorithm() + " service",
() -> skf2.translateKey(p11PbeKey));
System.out.println(" * Inconsistent key length between key and " +
"algorithm");
PBEKeySpec kSpec1 = new PBEKeySpec(pwd, salt, 1, 16);
assertThrows(e, InvalidKeySpecException.class.getName() + ": Key " +
"length is invalid for " + skf1.getAlgorithm() + " (expecting" +
" " + hmacPBESHA1Data.keySpec.getKeyLength() + " but was " +
kSpec1.getKeyLength() + ")",
() -> skf1.translateKey(getAnonymousPBEKey(
skf1.getAlgorithm(), kSpec1)));
System.out.println(" * Invalid key length in bits");
PBEKeySpec kSpec2 = new PBEKeySpec(pwd, salt, 1);
assertThrows(e, InvalidKeySpecException.class.getName() + ": Key " +
"length must be multiple of 8 and greater than zero",
() -> skf3.translateKey(getAnonymousPBEKey(
skf3.getAlgorithm(), kSpec2)));
System.out.println();
}
private static void testInvalidGenerateSecret(Provider sunPKCS11)
throws Exception {
System.out.println(sep + System.lineSeparator()
+ "Invalid SecretKeyFactory::generateSecret tests");
SecretKeyFactory skf1 = SecretKeyFactory.getInstance(
hmacPBESHA1Data.algo, sunPKCS11);
SecretKeyFactory skf2 = SecretKeyFactory.getInstance(
pbeWithHmacSHA512AndAES256Data.algo, sunPKCS11);
SecretKeyFactory skf3 = SecretKeyFactory.getInstance(
"PBKDF2WithHmacSHA512", sunPKCS11);
SecretKeyFactory skf4 = SecretKeyFactory.getInstance("AES", sunPKCS11);
Class<?> e = InvalidKeySpecException.class;
System.out.println(" * Missing salt and iteration count");
assertThrows(e, "Salt not found",
() -> skf1.generateSecret(new PBEKeySpec(pwd)));
System.out.println(" * Inconsistent key length between spec and " +
"algorithm");
PBEKeySpec kSpec = new PBEKeySpec(pwd, salt, 1, 16);
assertThrows(e, "Key length is invalid for " + skf1.getAlgorithm() +
" (expecting " + hmacPBESHA1Data.keySpec.getKeyLength() +
" but was " + kSpec.getKeyLength() + ")",
() -> skf1.generateSecret(kSpec));
assertThrows(e, "Key length is invalid for " + skf2.getAlgorithm() +
" (expecting " + pbeWithHmacSHA512AndAES256Data.keySpec
.getKeyLength() + " but was " + kSpec.getKeyLength() + ")",
() -> skf2.generateSecret(kSpec));
System.out.println(" * Invalid key length in bits");
String msg = "Key length must be multiple of 8 and greater than zero";
assertThrows(e, msg,
() -> skf3.generateSecret(new PBEKeySpec(pwd, salt, 1)));
assertThrows(e, msg,
() -> skf3.generateSecret(new PBEKeySpec(pwd, salt, 1, 3)));
System.out.println(" * PBEKeySpec to non-PBE SecretKeyFactory");
assertThrows(e, "Unsupported spec: javax.crypto.spec.PBEKeySpec",
() -> skf4.generateSecret(kSpec));
System.out.println();
}
private static void testInvalidGetKeySpec(Provider sunPKCS11)
throws Exception {
System.out.println(sep + System.lineSeparator()
+ "Invalid SecretKeyFactory::getKeySpec tests");
SecretKeyFactory skf1 = SecretKeyFactory.getInstance(
hmacPBESHA1Data.algo, sunPKCS11);
SecretKeyFactory skf2 = SecretKeyFactory.getInstance(
"AES", sunPKCS11);
PBEKey p11PbeKey = (PBEKey) skf1.translateKey(getAnonymousPBEKey(
skf1.getAlgorithm(), hmacPBESHA1Data.keySpec));
Class<?> e = InvalidKeySpecException.class;
System.out.println(" * null KeySpec class");
assertThrows(e, "key and keySpec must not be null",
() -> skf1.getKeySpec(p11PbeKey, null));
System.out.println(" * Invalid key type for PBEKeySpec");
assertThrows(e, "Unsupported spec: " + PBEKeySpec.class.getName(),
() -> skf1.getKeySpec(new SecretKeySpec(new byte[16],
skf1.getAlgorithm()), PBEKeySpec.class));
System.out.println(" * Invalid PBE key and PBEKeySpec for " +
skf2.getAlgorithm() + " SecretKeyFactory");
assertThrows(e, "Unsupported spec: " + PBEKeySpec.class.getName(),
() -> skf2.getKeySpec(p11PbeKey, PBEKeySpec.class));
System.out.println();
}
public static void main(String[] args) throws Exception {
main(new TestPBKD());
}
}