blob: d761a0637ade4224e6badab0820bfe284549a0e6 [file]
/*
* Copyright (c) 2021, 2025, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs11;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Locale;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.HexFormat;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import sun.nio.ch.DirectBuffer;
import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
import static sun.security.pkcs11.TemplateManager.*;
/**
* P11 KeyWrap Cipher implementation class for native impl which only support
* single part encryption/decryption through C_Encrypt/C_Decrypt() and
* key wrap/unwrap through C_WrapKey/C_UnwrapKey() calls.
* This class currently supports only AES cipher in KW and KWP modes.
*
* For multi-part encryption/decryption, this class has to buffer data until
* doFinal() is called.
*
* @since 18
*/
final class P11KeyWrapCipher extends CipherSpi {
private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
private static final int BLK_SIZE = 8;
// supported mode and padding with AES cipher
private enum KeyWrapType {
KW_NOPADDING("KW", "NOPADDING"),
KW_PKCS5PADDING("KW", "PKCS5PADDING"),
KWP_NOPADDING("KWP", "NOPADDING");
private final String mode;
private final String padding;
private final byte[] defIv;
KeyWrapType(String mode, String padding) {
this.mode = mode;
this.padding = padding;
if (mode.equalsIgnoreCase("KW")) {
this.defIv = new byte[] {
(byte)0xA6, (byte)0xA6, (byte)0xA6, (byte)0xA6,
(byte)0xA6, (byte)0xA6, (byte)0xA6, (byte)0xA6
};
} else {
this.defIv = new byte[] {
(byte)0xA6, (byte)0x59, (byte)0x59, (byte)0xA6
};
}
}
}
// token instance
private final Token token;
// mechanism id
private final long mechanism;
// type of this KeyWrap cipher, one of Transformation enum above
private final KeyWrapType type;
// acceptable key size in bytes, -1 if more than 1 key sizes are accepted
private final int fixedKeySize;
// associated session, if any
private Session session = null;
// key, if init() was called
private P11Key p11Key = null;
// flag indicating whether an operation is initialized
private boolean initialized = false;
private int opmode = Cipher.ENCRYPT_MODE;
// parameters
private byte[] iv = null;
private SecureRandom random = JCAUtil.getSecureRandom();
// dataBuffer for storing enc/dec data; cleared upon doFinal calls
private final ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
P11KeyWrapCipher(Token token, String algorithm, long mechanism)
throws PKCS11Exception, NoSuchAlgorithmException {
super();
this.token = token;
this.mechanism = mechanism;
// javax.crypto.Cipher ensures algoParts.length == 3
String[] algoParts = algorithm.split("/");
if (algoParts[0].startsWith("AES")) {
int index = algoParts[0].indexOf('_');
fixedKeySize = (index == -1 ? -1 :
// should be well-formed since we specify what we support
Integer.parseInt(algoParts[0].substring(index+1)) >> 3);
try {
this.type = KeyWrapType.valueOf(algoParts[1].toUpperCase() +
"_" + algoParts[2].toUpperCase());
} catch (IllegalArgumentException iae) {
throw new NoSuchAlgorithmException("Unsupported algorithm " +
algorithm);
}
} else {
throw new NoSuchAlgorithmException("Unsupported algorithm " +
algorithm);
}
}
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
// see JCE spec
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {
throw new NoSuchPaddingException("Unsupported padding " + padding);
}
}
// see JCE spec
@Override
protected int engineGetBlockSize() {
return BLK_SIZE;
}
// see JCE spec
@Override
protected int engineGetOutputSize(int inputLen) {
return doFinalLength(inputLen);
}
// see JCE spec
@Override
protected byte[] engineGetIV() {
return (iv == null) ? null : iv.clone();
}
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
// KW and KWP uses but not require parameters, return the default
// IV when no IV is supplied by caller
byte[] iv = (this.iv == null ? type.defIv : this.iv);
AlgorithmParameterSpec spec = new IvParameterSpec(iv);
try {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(spec);
return params;
} catch (GeneralSecurityException e) {
// NoSuchAlgorithmException, NoSuchProviderException
// InvalidParameterSpecException
throw new ProviderException("Could not encode parameters", e);
}
}
// see JCE spec
protected void engineInit(int opmode, Key key, SecureRandom sr)
throws InvalidKeyException {
try {
implInit(opmode, key, null, sr);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException("init() failed", e);
}
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null && !(params instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only IvParameterSpec is supported");
}
byte[] ivValue = (params == null ? null :
((IvParameterSpec)params).getIV());
implInit(opmode, key, ivValue, sr);
}
// see JCE spec
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
AlgorithmParameterSpec paramSpec = null;
if (params != null) {
try {
paramSpec = params.getParameterSpec(IvParameterSpec.class);
} catch (InvalidParameterSpecException ex) {
throw new InvalidAlgorithmParameterException(ex);
}
}
engineInit(opmode, key, paramSpec, sr);
}
// actual init() implementation
private void implInit(int opmode, Key key, byte[] iv, SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
reset(true);
if (fixedKeySize != -1) {
int keySize;
if (key instanceof P11Key) {
keySize = ((P11Key) key).length() >> 3;
} else {
byte[] encoding = key.getEncoded();
Arrays.fill(encoding, (byte) 0);
keySize = encoding.length;
}
if (keySize != fixedKeySize) {
throw new InvalidKeyException("Key size is invalid");
}
}
P11Key newKey = P11SecretKeyFactory.convertKey(token, key, "AES");
this.opmode = opmode;
if (iv == null) {
iv = type.defIv;
} else {
if (type == KeyWrapType.KWP_NOPADDING &&
!Arrays.equals(iv, type.defIv)) {
throw new InvalidAlgorithmParameterException
("For KWP mode, IV must has value 0x" +
HexFormat.of().withUpperCase().formatHex(type.defIv));
} else if (iv.length != type.defIv.length) {
throw new InvalidAlgorithmParameterException
("Wrong IV length, expected " + type.defIv.length +
" but got " + iv.length);
}
}
this.iv = iv;
this.p11Key = newKey;
if (sr != null) {
this.random = sr;
}
try {
initialize();
} catch (PKCS11Exception e) {
if (e.match(CKR_MECHANISM_PARAM_INVALID)) {
throw new InvalidAlgorithmParameterException("Bad params", e);
}
throw new InvalidKeyException("Could not initialize cipher", e);
}
}
private void cancelOperation() {
token.ensureValid();
if (P11Util.trySessionCancel(token, session,
(opmode == Cipher.ENCRYPT_MODE ? CKF_ENCRYPT : CKF_DECRYPT))) {
return;
}
// cancel by finishing operations; avoid killSession as some
// hardware vendors may require re-login
byte[] in = dataBuffer.toByteArray();
int inLen = in.length;
int bufLen = doFinalLength(0);
byte[] buffer = new byte[bufLen];
try {
if (opmode == Cipher.ENCRYPT_MODE) {
token.p11.C_Encrypt(session.id(), 0, in, 0, inLen,
0, buffer, 0, bufLen);
} else if (opmode == Cipher.DECRYPT_MODE) {
token.p11.C_Decrypt(session.id(), 0, in, 0, inLen,
0, buffer, 0, bufLen);
}
} catch (PKCS11Exception e) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already
// cancelled, do not fail here. This is part of a defensive
// mechanism for PKCS#11 libraries that do not strictly follow
// the standard.
return;
}
// ignore failure for en/decryption since it's likely to fail
// due to the minimum length requirement
}
}
private void ensureInitialized() throws PKCS11Exception {
if (!initialized) {
initialize();
}
}
private void initialize() throws PKCS11Exception {
if (p11Key == null) {
throw new ProviderException("Operation cannot be performed without"
+ " calling engineInit first");
}
token.ensureValid();
dataBuffer.reset();
if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.DECRYPT_MODE) {
long p11KeyID = p11Key.getKeyID();
try {
CK_MECHANISM mechWithParams = new CK_MECHANISM(mechanism, iv);
if (session == null) {
session = token.getOpSession();
}
switch (opmode) {
case Cipher.ENCRYPT_MODE -> token.p11.C_EncryptInit(session.id(), mechWithParams,
p11KeyID);
case Cipher.DECRYPT_MODE -> token.p11.C_DecryptInit(session.id(), mechWithParams,
p11KeyID);
}
} catch (PKCS11Exception e) {
session = token.releaseSession(session);
throw e;
} finally {
p11Key.releaseKeyID();
}
}
initialized = true;
}
// if doFinal(inLen) is called, how big does the output buffer have to be?
private int doFinalLength(int inLen) {
if (inLen < 0) {
throw new ProviderException("Invalid negative input length");
}
int result = inLen + dataBuffer.size();
boolean encrypt = (opmode == Cipher.ENCRYPT_MODE ||
opmode == Cipher.WRAP_MODE);
if (encrypt) {
if (type == KeyWrapType.KW_PKCS5PADDING) {
// add potential pad length, i.e. 1-8
result += (BLK_SIZE - (result & (BLK_SIZE - 1)));
} else if (type == KeyWrapType.KWP_NOPADDING &&
(result & (BLK_SIZE - 1)) != 0) {
// add potential pad length, i.e. 0-7
result += (BLK_SIZE - (result & (BLK_SIZE - 1)));
}
result += BLK_SIZE; // add the leading block including the ICV
} else {
result -= BLK_SIZE; // minus the leading block including the ICV
}
return (Math.max(result, 0));
}
// reset the states to the pre-initialized values
// set initialized to false, cancel operation, release session, and
// reset dataBuffer
private void reset(boolean doCancel) {
if (!initialized) {
return;
}
initialized = false;
try {
if (session == null) {
return;
}
if (doCancel && token.explicitCancel) {
cancelOperation();
}
} finally {
session = token.releaseSession(session);
dataBuffer.reset();
}
}
// see JCE spec
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
int n = implUpdate(in, inOfs, inLen);
return new byte[0];
}
// see JCE spec
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException {
implUpdate(in, inOfs, inLen);
return 0;
}
// see JCE spec
@Override
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException {
implUpdate(inBuffer);
return 0;
}
// see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
int maxOutLen = doFinalLength(inLen);
try {
byte[] out = new byte[maxOutLen];
int n = engineDoFinal(in, inOfs, inLen, out, 0);
return P11Util.convert(out, 0, n);
} catch (ShortBufferException e) {
// convert since the output length is calculated by doFinalLength()
throw new ProviderException(e);
}
}
// see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs);
}
// see JCE spec
@Override
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return implDoFinal(inBuffer, outBuffer);
}
private int implUpdate(byte[] in, int inOfs, int inLen) {
if (inLen > 0) {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("update() failed", e);
}
dataBuffer.write(in, inOfs, inLen);
}
// always 0 as NSS only supports single-part encryption/decryption
return 0;
}
private int implUpdate(ByteBuffer inBuf) {
int inLen = inBuf.remaining();
if (inLen > 0) {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("update() failed", e);
}
byte[] data = new byte[inLen];
inBuf.get(data);
dataBuffer.write(data, 0, data.length);
}
// always 0 as NSS only supports single-part encryption/decryption
return 0;
}
private int implDoFinal(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int requiredOutLen = doFinalLength(inLen);
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
int k = 0;
try {
ensureInitialized();
if (dataBuffer.size() > 0) {
if (in != null && inLen > 0) {
dataBuffer.write(in, inOfs, inLen);
}
in = dataBuffer.toByteArray();
inOfs = 0;
inLen = in.length;
}
if (opmode == Cipher.ENCRYPT_MODE) {
k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen,
0, out, outOfs, outLen);
doCancel = false;
} else {
// Special handling to match SunJCE provider behavior
if (inLen == 0) {
return 0;
}
k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen,
0, out, outOfs, outLen);
doCancel = false;
}
} catch (PKCS11Exception e) {
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
// successful calls to determine the output length. However,
// these cases are not expected here because the output length
// is checked in the OpenJDK side before making the PKCS#11 call.
// Thus, doCancel can safely be 'false'.
doCancel = false;
handleEncException("doFinal() failed", e);
} finally {
reset(doCancel);
}
return k;
}
private int implDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int outLen = outBuffer.remaining();
int inLen = inBuffer.remaining();
int requiredOutLen = doFinalLength(inLen);
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
int k = 0;
NIO_ACCESS.acquireSession(inBuffer);
try {
NIO_ACCESS.acquireSession(outBuffer);
try {
try {
ensureInitialized();
long inAddr = 0;
byte[] in = null;
int inOfs = 0;
if (dataBuffer.size() > 0) {
if (inBuffer != null && inLen > 0) {
byte[] temp = new byte[inLen];
inBuffer.get(temp);
dataBuffer.write(temp, 0, temp.length);
}
in = dataBuffer.toByteArray();
inOfs = 0;
inLen = in.length;
} else {
if (inBuffer instanceof DirectBuffer) {
inAddr = NIO_ACCESS.getBufferAddress(inBuffer);
inOfs = inBuffer.position();
} else {
if (inBuffer.hasArray()) {
in = inBuffer.array();
inOfs = inBuffer.position() + inBuffer.arrayOffset();
} else {
in = new byte[inLen];
inBuffer.get(in);
}
}
}
long outAddr = 0;
byte[] outArray = null;
int outOfs = 0;
if (outBuffer instanceof DirectBuffer) {
outAddr = NIO_ACCESS.getBufferAddress(outBuffer);
outOfs = outBuffer.position();
} else {
if (outBuffer.hasArray()) {
outArray = outBuffer.array();
outOfs = outBuffer.position() + outBuffer.arrayOffset();
} else {
outArray = new byte[outLen];
}
}
if (opmode == Cipher.ENCRYPT_MODE) {
k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen,
outAddr, outArray, outOfs, outLen);
doCancel = false;
} else {
// Special handling to match SunJCE provider behavior
if (inLen == 0) {
return 0;
}
k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen,
outAddr, outArray, outOfs, outLen);
doCancel = false;
}
inBuffer.position(inBuffer.limit());
outBuffer.position(outBuffer.position() + k);
} catch (PKCS11Exception e) {
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
// successful calls to determine the output length. However,
// these cases are not expected here because the output length
// is checked in the OpenJDK side before making the PKCS#11 call.
// Thus, doCancel can safely be 'false'.
doCancel = false;
handleEncException("doFinal() failed", e);
} finally {
reset(doCancel);
}
} finally {
NIO_ACCESS.releaseSession(outBuffer);
}
} finally {
NIO_ACCESS.releaseSession(inBuffer);
}
return k;
}
private void handleEncException(String msg, PKCS11Exception e)
throws IllegalBlockSizeException, ShortBufferException,
ProviderException {
if (e.match(CKR_DATA_LEN_RANGE) ||
e.match(CKR_ENCRYPTED_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(msg).initCause(e));
} else if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException(msg).initCause(e));
} else {
throw new ProviderException(msg, e);
}
}
// see JCE spec
protected byte[] engineWrap(Key tbwKey) throws IllegalBlockSizeException,
InvalidKeyException {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("wrap() failed", e);
}
// convert the specified key into P11Key handle
P11Key tbwP11Key = null;
if (!(tbwKey instanceof P11Key)) {
try {
tbwP11Key = (tbwKey instanceof SecretKey ?
P11SecretKeyFactory.convertKey(token, tbwKey,
tbwKey.getAlgorithm()) :
P11KeyFactory.convertKey(token, tbwKey,
tbwKey.getAlgorithm()));
} catch (ProviderException pe) {
throw new InvalidKeyException("Cannot convert to PKCS11 key",
pe);
} catch (InvalidKeyException ike) {
// could be algorithms NOT supported by PKCS11 library
// try single part encryption instead
}
} else {
tbwP11Key = (P11Key) tbwKey;
}
long p11KeyID = 0;
try {
if (session == null) {
session = token.getOpSession();
}
p11KeyID = p11Key.getKeyID();
CK_MECHANISM mechWithParams = new CK_MECHANISM(mechanism, iv);
if (tbwP11Key != null) {
long tbwP11KeyID = tbwP11Key.getKeyID();
try {
return token.p11.C_WrapKey(session.id(), mechWithParams,
p11KeyID, tbwP11KeyID);
} finally {
tbwP11Key.releaseKeyID();
}
} else {
byte[] in = tbwKey.getEncoded();
try {
token.p11.C_EncryptInit(session.id(), mechWithParams,
p11KeyID);
int bufLen = doFinalLength(in.length);
byte[] buffer = new byte[bufLen];
token.p11.C_Encrypt(session.id(), 0, in, 0, in.length,
0, buffer, 0, bufLen);
return buffer;
} finally {
Arrays.fill(in, (byte)0);
}
}
} catch (PKCS11Exception e) {
String msg = "wrap() failed";
if (e.match(CKR_KEY_SIZE_RANGE) || e.match(CKR_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(msg).initCause(e));
} else if (e.match(CKR_KEY_NOT_WRAPPABLE) ||
e.match(CKR_KEY_UNEXTRACTABLE) ||
e.match(CKR_KEY_HANDLE_INVALID)) {
throw new InvalidKeyException(msg, e);
} else if (e.match(CKR_MECHANISM_INVALID)) {
throw new UnsupportedOperationException(msg, e);
} else {
throw new ProviderException(msg, e);
}
} finally {
if (p11KeyID != 0) p11Key.releaseKeyID();
reset(false);
}
}
// see JCE spec
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgo,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("unwrap() failed", e);
}
long keyClass;
long keyType;
switch (wrappedKeyType) {
case Cipher.PRIVATE_KEY -> {
keyClass = CKO_PRIVATE_KEY;
keyType = P11KeyFactory.getPKCS11KeyType(wrappedKeyAlgo);
}
case Cipher.SECRET_KEY -> {
keyClass = CKO_SECRET_KEY;
keyType = P11SecretKeyFactory.getPKCS11KeyType(wrappedKeyAlgo);
}
case Cipher.PUBLIC_KEY -> throw new UnsupportedOperationException
("cannot unwrap public keys");
default -> // should never happen
throw new AssertionError();
}
CK_ATTRIBUTE[] attributes;
try {
attributes = new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_CLASS, keyClass),
new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
};
attributes = token.getAttributes
(O_IMPORT, keyClass, keyType, attributes);
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("unwrap() failed", e);
}
CK_MECHANISM mechParams = new CK_MECHANISM(mechanism, iv);
long p11KeyID = 0;
try {
if (session == null) {
session = token.getOpSession();
}
p11KeyID = p11Key.getKeyID();
long unwrappedKeyID = token.p11.C_UnwrapKey(session.id(),
mechParams, p11KeyID, wrappedKey, attributes);
return (switch(wrappedKeyType) {
case Cipher.PRIVATE_KEY -> P11Key.privateKey
(session, unwrappedKeyID, wrappedKeyAlgo, -1, attributes);
case Cipher.SECRET_KEY -> P11Key.secretKey
(session, unwrappedKeyID, wrappedKeyAlgo, -1, attributes);
default -> null;
});
} catch (PKCS11Exception e) {
String msg = "unwrap() failed";
if (e.match(CKR_UNWRAPPING_KEY_SIZE_RANGE) ||
e.match(CKR_WRAPPED_KEY_INVALID) ||
e.match(CKR_WRAPPED_KEY_LEN_RANGE) ||
e.match(CKR_UNWRAPPING_KEY_HANDLE_INVALID) ||
e.match(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT)) {
throw new InvalidKeyException(msg, e);
} else if (e.match(CKR_MECHANISM_INVALID)) {
throw new UnsupportedOperationException(msg, e);
} else {
throw new ProviderException(msg, e);
}
} finally {
if (p11KeyID != 0) p11Key.releaseKeyID();
reset(false);
}
}
// see JCE spec
@Override
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return P11SecretKeyFactory.convertKey(token, key, "AES").length();
}
}