|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 sun.nio.ch.DirectBuffer; |
|
import sun.security.jca.JCAUtil; |
|
import sun.security.pkcs11.wrapper.*; |
|
import static sun.security.pkcs11.wrapper.PKCS11Constants.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class P11AEADCipher extends CipherSpi { |
|
|
|
|
|
private static final int MODE_GCM = 10; |
|
|
|
|
|
private static final int GCM_DEFAULT_TAG_LEN = 16; |
|
private static final int GCM_DEFAULT_IV_LEN = 16; |
|
|
|
private static final String ALGO = "AES"; |
|
|
|
|
|
private final Token token; |
|
|
|
|
|
private final long mechanism; |
|
|
|
|
|
private final int blockMode; |
|
|
|
|
|
private final int fixedKeySize; |
|
|
|
|
|
private Session session = null; |
|
|
|
|
|
private P11Key p11Key = null; |
|
|
|
|
|
private boolean initialized = false; |
|
|
|
|
|
private boolean encrypt = true; |
|
|
|
|
|
private byte[] iv = null; |
|
private int tagLen = -1; |
|
private SecureRandom random = JCAUtil.getSecureRandom(); |
|
|
|
|
|
private ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream(); |
|
|
|
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream(); |
|
private boolean updateCalled = false; |
|
|
|
private boolean requireReinit = false; |
|
private P11Key lastEncKey = null; |
|
private byte[] lastEncIv = null; |
|
|
|
P11AEADCipher(Token token, String algorithm, long mechanism) |
|
throws PKCS11Exception, NoSuchAlgorithmException { |
|
super(); |
|
this.token = token; |
|
this.mechanism = mechanism; |
|
|
|
String[] algoParts = algorithm.split("/"); |
|
if (algoParts.length != 3) { |
|
throw new ProviderException("Unsupported Transformation format: " + |
|
algorithm); |
|
} |
|
if (!algoParts[0].startsWith("AES")) { |
|
throw new ProviderException("Only support AES for AEAD cipher mode"); |
|
} |
|
int index = algoParts[0].indexOf('_'); |
|
if (index != -1) { |
|
|
|
fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1)) >> 3; |
|
} else { |
|
fixedKeySize = -1; |
|
} |
|
this.blockMode = parseMode(algoParts[1]); |
|
if (!algoParts[2].equals("NoPadding")) { |
|
throw new ProviderException("Only NoPadding is supported for AEAD cipher mode"); |
|
} |
|
} |
|
|
|
protected void engineSetMode(String mode) throws NoSuchAlgorithmException { |
|
// Disallow change of mode for now since currently it's explicitly |
|
|
|
throw new NoSuchAlgorithmException("Unsupported mode " + mode); |
|
} |
|
|
|
private int parseMode(String mode) throws NoSuchAlgorithmException { |
|
mode = mode.toUpperCase(Locale.ENGLISH); |
|
int result; |
|
if (mode.equals("GCM")) { |
|
result = MODE_GCM; |
|
} else { |
|
throw new NoSuchAlgorithmException("Unsupported mode " + mode); |
|
} |
|
return result; |
|
} |
|
|
|
|
|
protected void engineSetPadding(String padding) |
|
throws NoSuchPaddingException { |
|
// Disallow change of padding for now since currently it's explicitly |
|
|
|
throw new NoSuchPaddingException("Unsupported padding " + padding); |
|
} |
|
|
|
|
|
protected int engineGetBlockSize() { |
|
return 16; |
|
} |
|
|
|
|
|
protected int engineGetOutputSize(int inputLen) { |
|
return doFinalLength(inputLen); |
|
} |
|
|
|
|
|
protected byte[] engineGetIV() { |
|
return (iv == null) ? null : iv.clone(); |
|
} |
|
|
|
|
|
protected AlgorithmParameters engineGetParameters() { |
|
if (encrypt && iv == null && tagLen == -1) { |
|
switch (blockMode) { |
|
case MODE_GCM: |
|
iv = new byte[GCM_DEFAULT_IV_LEN]; |
|
tagLen = GCM_DEFAULT_TAG_LEN; |
|
break; |
|
default: |
|
throw new ProviderException("Unsupported mode"); |
|
} |
|
random.nextBytes(iv); |
|
} |
|
try { |
|
AlgorithmParameterSpec spec; |
|
String apAlgo; |
|
switch (blockMode) { |
|
case MODE_GCM: |
|
apAlgo = "GCM"; |
|
spec = new GCMParameterSpec(tagLen << 3, iv); |
|
break; |
|
default: |
|
throw new ProviderException("Unsupported mode"); |
|
} |
|
AlgorithmParameters params = |
|
AlgorithmParameters.getInstance(apAlgo); |
|
params.init(spec); |
|
return params; |
|
} catch (GeneralSecurityException e) { |
|
// NoSuchAlgorithmException, NoSuchProviderException |
|
|
|
throw new ProviderException("Could not encode parameters", e); |
|
} |
|
} |
|
|
|
|
|
protected void engineInit(int opmode, Key key, SecureRandom sr) |
|
throws InvalidKeyException { |
|
if (opmode == Cipher.DECRYPT_MODE) { |
|
throw new InvalidKeyException("Parameters required for decryption"); |
|
} |
|
updateCalled = false; |
|
try { |
|
implInit(opmode, key, null, -1, sr); |
|
} catch (InvalidAlgorithmParameterException e) { |
|
throw new InvalidKeyException("init() failed", e); |
|
} |
|
} |
|
|
|
|
|
protected void engineInit(int opmode, Key key, |
|
AlgorithmParameterSpec params, SecureRandom sr) |
|
throws InvalidKeyException, InvalidAlgorithmParameterException { |
|
if (opmode == Cipher.DECRYPT_MODE && params == null) { |
|
throw new InvalidAlgorithmParameterException |
|
("Parameters required for decryption"); |
|
} |
|
updateCalled = false; |
|
byte[] ivValue = null; |
|
int tagLen = -1; |
|
if (params != null) { |
|
switch (blockMode) { |
|
case MODE_GCM: |
|
if (!(params instanceof GCMParameterSpec)) { |
|
throw new InvalidAlgorithmParameterException |
|
("Only GCMParameterSpec is supported"); |
|
} |
|
ivValue = ((GCMParameterSpec) params).getIV(); |
|
tagLen = ((GCMParameterSpec) params).getTLen() >> 3; |
|
break; |
|
default: |
|
throw new ProviderException("Unsupported mode"); |
|
} |
|
} |
|
implInit(opmode, key, ivValue, tagLen, sr); |
|
} |
|
|
|
|
|
protected void engineInit(int opmode, Key key, AlgorithmParameters params, |
|
SecureRandom sr) |
|
throws InvalidKeyException, InvalidAlgorithmParameterException { |
|
if (opmode == Cipher.DECRYPT_MODE && params == null) { |
|
throw new InvalidAlgorithmParameterException |
|
("Parameters required for decryption"); |
|
} |
|
updateCalled = false; |
|
try { |
|
AlgorithmParameterSpec paramSpec = null; |
|
if (params != null) { |
|
switch (blockMode) { |
|
case MODE_GCM: |
|
paramSpec = |
|
params.getParameterSpec(GCMParameterSpec.class); |
|
break; |
|
default: |
|
throw new ProviderException("Unsupported mode"); |
|
} |
|
} |
|
engineInit(opmode, key, paramSpec, sr); |
|
} catch (InvalidParameterSpecException ex) { |
|
throw new InvalidAlgorithmParameterException(ex); |
|
} |
|
} |
|
|
|
|
|
private void implInit(int opmode, Key key, byte[] iv, int tagLen, |
|
SecureRandom sr) |
|
throws InvalidKeyException, InvalidAlgorithmParameterException { |
|
reset(true); |
|
if (fixedKeySize != -1 && |
|
((key instanceof P11Key) ? ((P11Key) key).length() >> 3 : |
|
key.getEncoded().length) != fixedKeySize) { |
|
throw new InvalidKeyException("Key size is invalid"); |
|
} |
|
P11Key newKey = P11SecretKeyFactory.convertKey(token, key, ALGO); |
|
switch (opmode) { |
|
case Cipher.ENCRYPT_MODE: |
|
encrypt = true; |
|
requireReinit = Arrays.equals(iv, lastEncIv) && |
|
(newKey == lastEncKey); |
|
if (requireReinit) { |
|
throw new InvalidAlgorithmParameterException |
|
("Cannot reuse iv for GCM encryption"); |
|
} |
|
break; |
|
case Cipher.DECRYPT_MODE: |
|
encrypt = false; |
|
requireReinit = false; |
|
break; |
|
default: |
|
throw new InvalidAlgorithmParameterException |
|
("Unsupported mode: " + opmode); |
|
} |
|
|
|
|
|
if (sr != null) { |
|
this.random = sr; |
|
} |
|
if (iv == null && tagLen == -1) { |
|
|
|
switch (blockMode) { |
|
case MODE_GCM: |
|
iv = new byte[GCM_DEFAULT_IV_LEN]; |
|
this.random.nextBytes(iv); |
|
tagLen = GCM_DEFAULT_TAG_LEN; |
|
break; |
|
default: |
|
throw new ProviderException("Unsupported mode"); |
|
} |
|
} |
|
this.iv = iv; |
|
this.tagLen = tagLen; |
|
this.p11Key = newKey; |
|
try { |
|
initialize(); |
|
} catch (PKCS11Exception e) { |
|
throw new InvalidKeyException("Could not initialize cipher", e); |
|
} |
|
} |
|
|
|
private void cancelOperation() { |
|
// cancel operation by finishing it; avoid killSession as some |
|
|
|
int bufLen = doFinalLength(0); |
|
byte[] buffer = new byte[bufLen]; |
|
byte[] in = dataBuffer.toByteArray(); |
|
int inLen = in.length; |
|
try { |
|
if (encrypt) { |
|
token.p11.C_Encrypt(session.id(), 0, in, 0, inLen, |
|
0, buffer, 0, bufLen); |
|
} else { |
|
token.p11.C_Decrypt(session.id(), 0, in, 0, inLen, |
|
0, buffer, 0, bufLen); |
|
} |
|
} catch (PKCS11Exception e) { |
|
if (e.getErrorCode() == 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 |
|
|
|
return; |
|
} |
|
if (encrypt) { |
|
throw new ProviderException("Cancel failed", e); |
|
} |
|
// ignore failure for decryption |
|
} |
|
} |
|
|
|
private void ensureInitialized() throws PKCS11Exception { |
|
if (initialized && aadBuffer.size() > 0) { |
|
|
|
reset(true); |
|
} |
|
if (!initialized) { |
|
initialize(); |
|
} |
|
} |
|
|
|
private void initialize() throws PKCS11Exception { |
|
if (p11Key == null) { |
|
throw new ProviderException( |
|
"Operation cannot be performed without" |
|
+ " calling engineInit first"); |
|
} |
|
if (requireReinit) { |
|
throw new IllegalStateException |
|
("Must use either different key or iv for GCM encryption"); |
|
} |
|
|
|
token.ensureValid(); |
|
|
|
byte[] aad = (aadBuffer.size() > 0? aadBuffer.toByteArray() : null); |
|
|
|
long p11KeyID = p11Key.getKeyID(); |
|
try { |
|
if (session == null) { |
|
session = token.getOpSession(); |
|
} |
|
CK_MECHANISM mechWithParams; |
|
switch (blockMode) { |
|
case MODE_GCM: |
|
mechWithParams = new CK_MECHANISM(mechanism, |
|
new CK_GCM_PARAMS(tagLen << 3, iv, aad)); |
|
break; |
|
default: |
|
throw new ProviderException("Unsupported mode: " + blockMode); |
|
} |
|
if (encrypt) { |
|
token.p11.C_EncryptInit(session.id(), mechWithParams, |
|
p11KeyID); |
|
} else { |
|
token.p11.C_DecryptInit(session.id(), mechWithParams, |
|
p11KeyID); |
|
} |
|
} catch (PKCS11Exception e) { |
|
|
|
p11Key.releaseKeyID(); |
|
session = token.releaseSession(session); |
|
throw e; |
|
} finally { |
|
dataBuffer.reset(); |
|
aadBuffer.reset(); |
|
} |
|
initialized = true; |
|
} |
|
|
|
|
|
private int doFinalLength(int inLen) { |
|
if (inLen < 0) { |
|
throw new ProviderException("Invalid negative input length"); |
|
} |
|
|
|
int result = inLen + dataBuffer.size(); |
|
if (encrypt) { |
|
result += tagLen; |
|
} else { |
|
// PKCS11Exception: CKR_BUFFER_TOO_SMALL |
|
//result -= tagLen; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
private void reset(boolean doCancel) { |
|
if (!initialized) { |
|
return; |
|
} |
|
initialized = false; |
|
|
|
try { |
|
if (session == null) { |
|
return; |
|
} |
|
|
|
if (doCancel && token.explicitCancel) { |
|
cancelOperation(); |
|
} |
|
} finally { |
|
p11Key.releaseKeyID(); |
|
session = token.releaseSession(session); |
|
dataBuffer.reset(); |
|
} |
|
} |
|
|
|
|
|
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { |
|
updateCalled = true; |
|
int n = implUpdate(in, inOfs, inLen); |
|
return new byte[0]; |
|
} |
|
|
|
|
|
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out, |
|
int outOfs) throws ShortBufferException { |
|
updateCalled = true; |
|
implUpdate(in, inOfs, inLen); |
|
return 0; |
|
} |
|
|
|
|
|
@Override |
|
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer) |
|
throws ShortBufferException { |
|
updateCalled = true; |
|
implUpdate(inBuffer); |
|
return 0; |
|
} |
|
|
|
|
|
@Override |
|
protected synchronized void engineUpdateAAD(byte[] src, int srcOfs, int srcLen) |
|
throws IllegalStateException { |
|
if ((src == null) || (srcOfs < 0) || (srcOfs + srcLen > src.length)) { |
|
throw new IllegalArgumentException("Invalid AAD"); |
|
} |
|
if (requireReinit) { |
|
throw new IllegalStateException |
|
("Must use either different key or iv for GCM encryption"); |
|
} |
|
if (p11Key == null) { |
|
throw new IllegalStateException("Need to initialize Cipher first"); |
|
} |
|
if (updateCalled) { |
|
throw new IllegalStateException |
|
("Update has been called; no more AAD data"); |
|
} |
|
aadBuffer.write(src, srcOfs, srcLen); |
|
} |
|
|
|
|
|
@Override |
|
protected void engineUpdateAAD(ByteBuffer src) |
|
throws IllegalStateException { |
|
if (src == null) { |
|
throw new IllegalArgumentException("Invalid AAD"); |
|
} |
|
byte[] srcBytes = new byte[src.remaining()]; |
|
src.get(srcBytes); |
|
engineUpdateAAD(srcBytes, 0, srcBytes.length); |
|
} |
|
|
|
|
|
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) |
|
throws IllegalBlockSizeException, BadPaddingException { |
|
int minOutLen = doFinalLength(inLen); |
|
try { |
|
byte[] out = new byte[minOutLen]; |
|
int n = engineDoFinal(in, inOfs, inLen, out, 0); |
|
return P11Util.convert(out, 0, n); |
|
} catch (ShortBufferException e) { |
|
|
|
throw new ProviderException(e); |
|
} finally { |
|
updateCalled = false; |
|
} |
|
} |
|
|
|
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, |
|
int outOfs) throws ShortBufferException, IllegalBlockSizeException, |
|
BadPaddingException { |
|
try { |
|
return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs); |
|
} finally { |
|
updateCalled = false; |
|
} |
|
} |
|
|
|
|
|
@Override |
|
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer) |
|
throws ShortBufferException, IllegalBlockSizeException, |
|
BadPaddingException { |
|
try { |
|
return implDoFinal(inBuffer, outBuffer); |
|
} finally { |
|
updateCalled = false; |
|
} |
|
} |
|
|
|
private int implUpdate(byte[] in, int inOfs, int inLen) { |
|
if (inLen > 0) { |
|
updateCalled = true; |
|
try { |
|
ensureInitialized(); |
|
} catch (PKCS11Exception e) { |
|
|
|
reset(false); |
|
throw new ProviderException("update() failed", e); |
|
} |
|
dataBuffer.write(in, inOfs, inLen); |
|
} |
|
|
|
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); |
|
} |
|
|
|
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; |
|
try { |
|
ensureInitialized(); |
|
if (dataBuffer.size() > 0) { |
|
if (in != null && inOfs > 0 && inLen > 0 && |
|
inOfs < (in.length - inLen)) { |
|
dataBuffer.write(in, inOfs, inLen); |
|
} |
|
in = dataBuffer.toByteArray(); |
|
inOfs = 0; |
|
inLen = in.length; |
|
} |
|
int k = 0; |
|
if (encrypt) { |
|
k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen, |
|
0, out, outOfs, outLen); |
|
doCancel = false; |
|
} else { |
|
|
|
if (inLen == 0) { |
|
return 0; |
|
} |
|
k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen, |
|
0, out, outOfs, outLen); |
|
doCancel = false; |
|
} |
|
return 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. |
|
|
|
doCancel = false; |
|
handleException(e); |
|
throw new ProviderException("doFinal() failed", e); |
|
} finally { |
|
if (encrypt) { |
|
lastEncKey = this.p11Key; |
|
lastEncIv = this.iv; |
|
requireReinit = true; |
|
} |
|
reset(doCancel); |
|
} |
|
} |
|
|
|
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; |
|
try { |
|
ensureInitialized(); |
|
|
|
long inAddr = 0; |
|
byte[] in = null; |
|
int inOfs = 0; |
|
if (dataBuffer.size() > 0) { |
|
if (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 = ((DirectBuffer) inBuffer).address(); |
|
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 = ((DirectBuffer) outBuffer).address(); |
|
outOfs = outBuffer.position(); |
|
} else { |
|
if (outBuffer.hasArray()) { |
|
outArray = outBuffer.array(); |
|
outOfs = outBuffer.position() + outBuffer.arrayOffset(); |
|
} else { |
|
outArray = new byte[outLen]; |
|
} |
|
} |
|
|
|
int k = 0; |
|
if (encrypt) { |
|
k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen, |
|
outAddr, outArray, outOfs, outLen); |
|
doCancel = false; |
|
} else { |
|
|
|
if (inLen == 0) { |
|
return 0; |
|
} |
|
k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen, |
|
outAddr, outArray, outOfs, outLen); |
|
doCancel = false; |
|
} |
|
outBuffer.position(outBuffer.position() + k); |
|
return 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. |
|
|
|
doCancel = false; |
|
handleException(e); |
|
throw new ProviderException("doFinal() failed", e); |
|
} finally { |
|
if (encrypt) { |
|
lastEncKey = this.p11Key; |
|
lastEncIv = this.iv; |
|
requireReinit = true; |
|
} |
|
reset(doCancel); |
|
} |
|
} |
|
|
|
private void handleException(PKCS11Exception e) |
|
throws ShortBufferException, IllegalBlockSizeException, |
|
BadPaddingException { |
|
long errorCode = e.getErrorCode(); |
|
if (errorCode == CKR_BUFFER_TOO_SMALL) { |
|
throw (ShortBufferException) |
|
(new ShortBufferException().initCause(e)); |
|
} else if (errorCode == CKR_DATA_LEN_RANGE || |
|
errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) { |
|
throw (IllegalBlockSizeException) |
|
(new IllegalBlockSizeException(e.toString()).initCause(e)); |
|
} else if (errorCode == CKR_ENCRYPTED_DATA_INVALID) { |
|
throw (BadPaddingException) |
|
(new BadPaddingException(e.toString()).initCause(e)); |
|
} |
|
} |
|
|
|
|
|
protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, |
|
InvalidKeyException { |
|
|
|
throw new UnsupportedOperationException("engineWrap()"); |
|
} |
|
|
|
|
|
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, |
|
int wrappedKeyType) |
|
throws InvalidKeyException, NoSuchAlgorithmException { |
|
|
|
throw new UnsupportedOperationException("engineUnwrap()"); |
|
} |
|
|
|
|
|
@Override |
|
protected int engineGetKeySize(Key key) throws InvalidKeyException { |
|
int n = P11SecretKeyFactory.convertKey |
|
(token, key, ALGO).length(); |
|
return n; |
|
} |
|
} |
|
|