Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java
41154 views
/*1* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.security.pkcs11;2526import java.io.ByteArrayOutputStream;27import java.nio.ByteBuffer;28import java.util.Arrays;29import java.util.Locale;3031import java.security.*;32import java.security.spec.*;3334import javax.crypto.*;35import javax.crypto.spec.*;3637import sun.nio.ch.DirectBuffer;38import sun.security.jca.JCAUtil;39import sun.security.pkcs11.wrapper.*;40import static sun.security.pkcs11.wrapper.PKCS11Constants.*;41import static sun.security.pkcs11.wrapper.PKCS11Exception.*;4243/**44* P11 AEAD Cipher implementation class. This class currently supports45* AES cipher in GCM mode and CHACHA20-POLY1305 cipher.46*47* Note that AEAD modes do not use padding, so this class does not have48* its own padding impl. In addition, some vendors such as NSS may not support49* multi-part encryption/decryption for AEAD cipher algorithms, thus the50* current impl uses PKCS#11 C_Encrypt/C_Decrypt calls and buffers data until51* doFinal is called.52*53* @since 1354*/55final class P11AEADCipher extends CipherSpi {5657// supported AEAD algorithms/transformations58private enum Transformation {59AES_GCM("AES", "GCM", "NOPADDING", 16, 16),60CHACHA20_POLY1305("CHACHA20", "NONE", "NOPADDING", 12, 16);6162final String keyAlgo;63final String mode;64final String padding;65final int defIvLen; // in bytes66final int defTagLen; // in bytes6768Transformation(String keyAlgo, String mode, String padding,69int defIvLen, int defTagLen) {70this.keyAlgo = keyAlgo;71this.mode = mode;72this.padding = padding;73this.defIvLen = defIvLen;74this.defTagLen = defTagLen;75}76}7778// token instance79private final Token token;8081// mechanism id82private final long mechanism;8384// type of this AEAD cipher, one of Transformation enum above85private final Transformation type;8687// acceptable key size in bytes, -1 if more than 1 key sizes are accepted88private final int fixedKeySize;8990// associated session, if any91private Session session = null;9293// key, if init() was called94private P11Key p11Key = null;9596// flag indicating whether an operation is initialized97private boolean initialized = false;9899// falg indicating encrypt or decrypt mode100private boolean encrypt = true;101102// parameters103private byte[] iv = null;104private int tagLen = -1;105private SecureRandom random = JCAUtil.getSecureRandom();106107// dataBuffer is cleared upon doFinal calls108private ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();109// aadBuffer is cleared upon successful init calls110private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();111private boolean updateCalled = false;112113private boolean requireReinit = false;114private P11Key lastEncKey = null;115private byte[] lastEncIv = null;116117P11AEADCipher(Token token, String algorithm, long mechanism)118throws PKCS11Exception, NoSuchAlgorithmException {119super();120this.token = token;121this.mechanism = mechanism;122123String[] algoParts = algorithm.split("/");124if (algoParts[0].startsWith("AES")) {125// for AES_GCM, need 3 parts126if (algoParts.length != 3) {127throw new AssertionError("Invalid Transformation format: " +128algorithm);129}130int index = algoParts[0].indexOf('_');131if (index != -1) {132// should be well-formed since we specify what we support133fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1)) >> 3;134} else {135fixedKeySize = -1;136}137this.type = Transformation.AES_GCM;138engineSetMode(algoParts[1]);139try {140engineSetPadding(algoParts[2]);141} catch (NoSuchPaddingException e) {142throw new NoSuchAlgorithmException();143}144} else if (algoParts[0].equals("ChaCha20-Poly1305")) {145fixedKeySize = 32;146this.type = Transformation.CHACHA20_POLY1305;147if (algoParts.length > 3) {148throw new AssertionError(149"Invalid Transformation format: " + algorithm);150} else {151if (algoParts.length > 1) {152engineSetMode(algoParts[1]);153}154try {155if (algoParts.length > 2) {156engineSetPadding(algoParts[2]);157}158} catch (NoSuchPaddingException e) {159throw new NoSuchAlgorithmException();160}161}162} else {163throw new AssertionError("Unsupported transformation " + algorithm);164}165}166167@Override168protected void engineSetMode(String mode) throws NoSuchAlgorithmException {169if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {170throw new NoSuchAlgorithmException("Unsupported mode " + mode);171}172}173174// see JCE spec175@Override176protected void engineSetPadding(String padding)177throws NoSuchPaddingException {178if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {179throw new NoSuchPaddingException("Unsupported padding " + padding);180}181}182183// see JCE spec184@Override185protected int engineGetBlockSize() {186return switch (type) {187case AES_GCM -> 16;188case CHACHA20_POLY1305 -> 0;189default -> throw new AssertionError("Unsupported type " + type);190};191}192193// see JCE spec194@Override195protected int engineGetOutputSize(int inputLen) {196return doFinalLength(inputLen);197}198199// see JCE spec200@Override201protected byte[] engineGetIV() {202return (iv == null) ? null : iv.clone();203}204205// see JCE spec206protected AlgorithmParameters engineGetParameters() {207String apAlgo;208AlgorithmParameterSpec spec = null;209switch (type) {210case AES_GCM:211apAlgo = "GCM";212if (encrypt && iv == null && tagLen == -1) {213iv = new byte[type.defIvLen];214tagLen = type.defTagLen;215random.nextBytes(iv);216}217if (iv != null) {218spec = new GCMParameterSpec(tagLen << 3, iv);219}220break;221case CHACHA20_POLY1305:222if (encrypt && iv == null) {223iv = new byte[type.defIvLen];224random.nextBytes(iv);225}226apAlgo = "ChaCha20-Poly1305";227if (iv != null) {228spec = new IvParameterSpec(iv);229}230break;231default:232throw new AssertionError("Unsupported type " + type);233}234if (spec != null) {235try {236AlgorithmParameters params =237AlgorithmParameters.getInstance(apAlgo);238params.init(spec);239return params;240} catch (GeneralSecurityException e) {241// NoSuchAlgorithmException, NoSuchProviderException242// InvalidParameterSpecException243throw new ProviderException("Could not encode parameters", e);244}245}246return null;247}248249// see JCE spec250protected void engineInit(int opmode, Key key, SecureRandom sr)251throws InvalidKeyException {252if (opmode == Cipher.DECRYPT_MODE) {253throw new InvalidKeyException("Parameters required for decryption");254}255updateCalled = false;256try {257implInit(opmode, key, null, -1, sr);258} catch (InvalidAlgorithmParameterException e) {259throw new InvalidKeyException("init() failed", e);260}261}262263// see JCE spec264protected void engineInit(int opmode, Key key,265AlgorithmParameterSpec params, SecureRandom sr)266throws InvalidKeyException, InvalidAlgorithmParameterException {267if (opmode == Cipher.DECRYPT_MODE && params == null) {268throw new InvalidAlgorithmParameterException269("Parameters required for decryption");270}271updateCalled = false;272byte[] ivValue = null;273int tagLen = -1;274switch (type) {275case AES_GCM:276if (params != null) {277if (!(params instanceof GCMParameterSpec)) {278throw new InvalidAlgorithmParameterException279("Only GCMParameterSpec is supported");280}281ivValue = ((GCMParameterSpec) params).getIV();282tagLen = ((GCMParameterSpec) params).getTLen() >> 3;283}284break;285case CHACHA20_POLY1305:286if (params != null) {287if (!(params instanceof IvParameterSpec)) {288throw new InvalidAlgorithmParameterException289("Only IvParameterSpec is supported");290}291ivValue = ((IvParameterSpec) params).getIV();292tagLen = type.defTagLen;293}294break;295default:296throw new AssertionError("Unsupported type " + type);297};298implInit(opmode, key, ivValue, tagLen, sr);299}300301// see JCE spec302protected void engineInit(int opmode, Key key, AlgorithmParameters params,303SecureRandom sr)304throws InvalidKeyException, InvalidAlgorithmParameterException {305if (opmode == Cipher.DECRYPT_MODE && params == null) {306throw new InvalidAlgorithmParameterException307("Parameters required for decryption");308}309updateCalled = false;310try {311AlgorithmParameterSpec paramSpec = null;312if (params != null) {313switch (type) {314case AES_GCM:315paramSpec =316params.getParameterSpec(GCMParameterSpec.class);317break;318case CHACHA20_POLY1305:319paramSpec =320params.getParameterSpec(IvParameterSpec.class);321break;322default:323throw new AssertionError("Unsupported type " + type);324}325}326engineInit(opmode, key, paramSpec, sr);327} catch (InvalidParameterSpecException ex) {328throw new InvalidAlgorithmParameterException(ex);329}330}331332// actual init() implementation333private void implInit(int opmode, Key key, byte[] iv, int tagLen,334SecureRandom sr)335throws InvalidKeyException, InvalidAlgorithmParameterException {336reset(true);337if (fixedKeySize != -1 &&338((key instanceof P11Key) ? ((P11Key) key).length() >> 3 :339key.getEncoded().length) != fixedKeySize) {340throw new InvalidKeyException("Key size is invalid");341}342P11Key newKey = P11SecretKeyFactory.convertKey(token, key,343type.keyAlgo);344switch (opmode) {345case Cipher.ENCRYPT_MODE:346encrypt = true;347requireReinit = Arrays.equals(iv, lastEncIv) &&348(newKey == lastEncKey);349if (requireReinit) {350throw new InvalidAlgorithmParameterException(351"Cannot reuse the same key and iv pair");352}353break;354case Cipher.DECRYPT_MODE:355encrypt = false;356requireReinit = false;357break;358default:359throw new InvalidAlgorithmParameterException360("Unsupported mode: " + opmode);361}362363// decryption without parameters is checked in all engineInit() calls364if (sr != null) {365this.random = sr;366}367368if (iv == null && tagLen == -1) {369// generate default values370switch (type) {371case AES_GCM:372iv = new byte[type.defIvLen];373this.random.nextBytes(iv);374tagLen = type.defTagLen;375break;376case CHACHA20_POLY1305:377iv = new byte[type.defIvLen];378this.random.nextBytes(iv);379tagLen = type.defTagLen;380break;381default:382throw new AssertionError("Unsupported type " + type);383}384}385this.iv = iv;386this.tagLen = tagLen;387this.p11Key = newKey;388try {389initialize();390} catch (PKCS11Exception e) {391if (e.getErrorCode() == CKR_MECHANISM_PARAM_INVALID) {392throw new InvalidAlgorithmParameterException("Bad params", e);393}394throw new InvalidKeyException("Could not initialize cipher", e);395}396}397398private void cancelOperation() {399// cancel operation by finishing it; avoid killSession as some400// hardware vendors may require re-login401int bufLen = doFinalLength(0);402byte[] buffer = new byte[bufLen];403byte[] in = dataBuffer.toByteArray();404int inLen = in.length;405try {406if (encrypt) {407token.p11.C_Encrypt(session.id(), 0, in, 0, inLen,4080, buffer, 0, bufLen);409} else {410token.p11.C_Decrypt(session.id(), 0, in, 0, inLen,4110, buffer, 0, bufLen);412}413} catch (PKCS11Exception e) {414if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {415// Cancel Operation may be invoked after an error on a PKCS#11416// call. If the operation inside the token was already cancelled,417// do not fail here. This is part of a defensive mechanism for418// PKCS#11 libraries that do not strictly follow the standard.419return;420}421if (encrypt) {422throw new ProviderException("Cancel failed", e);423}424// ignore failure for decryption425}426}427428private void ensureInitialized() throws PKCS11Exception {429if (initialized && aadBuffer.size() > 0) {430// need to cancel first to avoid CKR_OPERATION_ACTIVE431reset(true);432}433if (!initialized) {434initialize();435}436}437438private void initialize() throws PKCS11Exception {439if (p11Key == null) {440throw new ProviderException(441"Operation cannot be performed without"442+ " calling engineInit first");443}444if (requireReinit) {445throw new IllegalStateException446("Must use either different key or iv");447}448449token.ensureValid();450451byte[] aad = (aadBuffer.size() > 0? aadBuffer.toByteArray() : null);452453long p11KeyID = p11Key.getKeyID();454try {455CK_MECHANISM mechWithParams;456switch (type) {457case AES_GCM:458mechWithParams = new CK_MECHANISM(mechanism,459new CK_GCM_PARAMS(tagLen << 3, iv, aad));460break;461case CHACHA20_POLY1305:462mechWithParams = new CK_MECHANISM(mechanism,463new CK_SALSA20_CHACHA20_POLY1305_PARAMS(iv, aad));464break;465default:466throw new AssertionError("Unsupported type: " + type);467}468if (session == null) {469session = token.getOpSession();470}471if (encrypt) {472token.p11.C_EncryptInit(session.id(), mechWithParams,473p11KeyID);474} else {475token.p11.C_DecryptInit(session.id(), mechWithParams,476p11KeyID);477}478} catch (PKCS11Exception e) {479p11Key.releaseKeyID();480session = token.releaseSession(session);481throw e;482} finally {483dataBuffer.reset();484aadBuffer.reset();485}486initialized = true;487}488489// if doFinal(inLen) is called, how big does the output buffer have to be?490private int doFinalLength(int inLen) {491if (inLen < 0) {492throw new ProviderException("Invalid negative input length");493}494495int result = inLen + dataBuffer.size();496if (encrypt) {497result += tagLen;498} else {499// In earlier NSS versions, AES_GCM would report500// CKR_BUFFER_TOO_SMALL error if minus tagLen501if (type == Transformation.CHACHA20_POLY1305) {502result -= tagLen;503}504}505return (result > 0? result : 0);506}507508// reset the states to the pre-initialized values509private void reset(boolean doCancel) {510if (!initialized) {511return;512}513initialized = false;514515try {516if (session == null) {517return;518}519520if (doCancel && token.explicitCancel) {521cancelOperation();522}523} finally {524p11Key.releaseKeyID();525session = token.releaseSession(session);526dataBuffer.reset();527}528}529530// see JCE spec531protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {532updateCalled = true;533int n = implUpdate(in, inOfs, inLen);534return new byte[0];535}536537// see JCE spec538protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,539int outOfs) throws ShortBufferException {540updateCalled = true;541implUpdate(in, inOfs, inLen);542return 0;543}544545// see JCE spec546@Override547protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)548throws ShortBufferException {549updateCalled = true;550implUpdate(inBuffer);551return 0;552}553554// see JCE spec555@Override556protected synchronized void engineUpdateAAD(byte[] src, int srcOfs, int srcLen)557throws IllegalStateException {558if ((src == null) || (srcOfs < 0) || (srcOfs + srcLen > src.length)) {559throw new IllegalArgumentException("Invalid AAD");560}561if (requireReinit) {562throw new IllegalStateException563("Must use either different key or iv for encryption");564}565if (p11Key == null) {566throw new IllegalStateException("Need to initialize Cipher first");567}568if (updateCalled) {569throw new IllegalStateException570("Update has been called; no more AAD data");571}572aadBuffer.write(src, srcOfs, srcLen);573}574575// see JCE spec576@Override577protected void engineUpdateAAD(ByteBuffer src)578throws IllegalStateException {579if (src == null) {580throw new IllegalArgumentException("Invalid AAD");581}582byte[] srcBytes = new byte[src.remaining()];583src.get(srcBytes);584engineUpdateAAD(srcBytes, 0, srcBytes.length);585}586587// see JCE spec588protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)589throws IllegalBlockSizeException, BadPaddingException {590int minOutLen = doFinalLength(inLen);591try {592byte[] out = new byte[minOutLen];593int n = engineDoFinal(in, inOfs, inLen, out, 0);594return P11Util.convert(out, 0, n);595} catch (ShortBufferException e) {596// convert since the output length is calculated by doFinalLength()597throw new ProviderException(e);598} finally {599updateCalled = false;600}601}602// see JCE spec603protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,604int outOfs) throws ShortBufferException, IllegalBlockSizeException,605BadPaddingException {606try {607return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs);608} finally {609updateCalled = false;610}611}612613// see JCE spec614@Override615protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)616throws ShortBufferException, IllegalBlockSizeException,617BadPaddingException {618try {619return implDoFinal(inBuffer, outBuffer);620} finally {621updateCalled = false;622}623}624625private int implUpdate(byte[] in, int inOfs, int inLen) {626if (inLen > 0) {627updateCalled = true;628try {629ensureInitialized();630} catch (PKCS11Exception e) {631//e.printStackTrace();632reset(false);633throw new ProviderException("update() failed", e);634}635dataBuffer.write(in, inOfs, inLen);636}637// always 0 as NSS only supports single-part encryption/decryption638return 0;639}640641private int implUpdate(ByteBuffer inBuf) {642int inLen = inBuf.remaining();643if (inLen > 0) {644try {645ensureInitialized();646} catch (PKCS11Exception e) {647reset(false);648throw new ProviderException("update() failed", e);649}650byte[] data = new byte[inLen];651inBuf.get(data);652dataBuffer.write(data, 0, data.length);653}654// always 0 as NSS only supports single-part encryption/decryption655return 0;656}657658private int implDoFinal(byte[] in, int inOfs, int inLen,659byte[] out, int outOfs, int outLen)660throws ShortBufferException, IllegalBlockSizeException,661BadPaddingException {662int requiredOutLen = doFinalLength(inLen);663if (outLen < requiredOutLen) {664throw new ShortBufferException();665}666667boolean doCancel = true;668try {669ensureInitialized();670if (dataBuffer.size() > 0) {671if (in != null && inOfs > 0 && inLen > 0 &&672inOfs < (in.length - inLen)) {673dataBuffer.write(in, inOfs, inLen);674}675in = dataBuffer.toByteArray();676inOfs = 0;677inLen = in.length;678}679int k = 0;680if (encrypt) {681k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen,6820, out, outOfs, outLen);683doCancel = false;684} else {685// Special handling to match SunJCE provider behavior686if (inLen == 0) {687return 0;688}689k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen,6900, out, outOfs, outLen);691doCancel = false;692}693return k;694} catch (PKCS11Exception e) {695// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only696// keep the operation active on CKR_BUFFER_TOO_SMALL errors or697// successful calls to determine the output length. However,698// these cases are not expected here because the output length699// is checked in the OpenJDK side before making the PKCS#11 call.700// Thus, doCancel can safely be 'false'.701doCancel = false;702handleException(e);703throw new ProviderException("doFinal() failed", e);704} finally {705if (encrypt) {706lastEncKey = this.p11Key;707lastEncIv = this.iv;708requireReinit = true;709}710reset(doCancel);711}712}713714private int implDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)715throws ShortBufferException, IllegalBlockSizeException,716BadPaddingException {717int outLen = outBuffer.remaining();718int inLen = inBuffer.remaining();719720int requiredOutLen = doFinalLength(inLen);721if (outLen < requiredOutLen) {722throw new ShortBufferException();723}724725boolean doCancel = true;726try {727ensureInitialized();728729long inAddr = 0;730byte[] in = null;731int inOfs = 0;732if (dataBuffer.size() > 0) {733if (inLen > 0) {734byte[] temp = new byte[inLen];735inBuffer.get(temp);736dataBuffer.write(temp, 0, temp.length);737}738in = dataBuffer.toByteArray();739inOfs = 0;740inLen = in.length;741} else {742if (inBuffer instanceof DirectBuffer) {743inAddr = ((DirectBuffer) inBuffer).address();744inOfs = inBuffer.position();745} else {746if (inBuffer.hasArray()) {747in = inBuffer.array();748inOfs = inBuffer.position() + inBuffer.arrayOffset();749} else {750in = new byte[inLen];751inBuffer.get(in);752}753}754}755long outAddr = 0;756byte[] outArray = null;757int outOfs = 0;758if (outBuffer instanceof DirectBuffer) {759outAddr = ((DirectBuffer) outBuffer).address();760outOfs = outBuffer.position();761} else {762if (outBuffer.hasArray()) {763outArray = outBuffer.array();764outOfs = outBuffer.position() + outBuffer.arrayOffset();765} else {766outArray = new byte[outLen];767}768}769770int k = 0;771if (encrypt) {772k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen,773outAddr, outArray, outOfs, outLen);774doCancel = false;775} else {776// Special handling to match SunJCE provider behavior777if (inLen == 0) {778return 0;779}780k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen,781outAddr, outArray, outOfs, outLen);782doCancel = false;783}784inBuffer.position(inBuffer.limit());785outBuffer.position(outBuffer.position() + k);786return k;787} catch (PKCS11Exception e) {788// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only789// keep the operation active on CKR_BUFFER_TOO_SMALL errors or790// successful calls to determine the output length. However,791// these cases are not expected here because the output length792// is checked in the OpenJDK side before making the PKCS#11 call.793// Thus, doCancel can safely be 'false'.794doCancel = false;795handleException(e);796throw new ProviderException("doFinal() failed", e);797} finally {798if (encrypt) {799lastEncKey = this.p11Key;800lastEncIv = this.iv;801requireReinit = true;802}803reset(doCancel);804}805}806807private void handleException(PKCS11Exception e)808throws ShortBufferException, IllegalBlockSizeException,809BadPaddingException {810long errorCode = e.getErrorCode();811if (errorCode == CKR_BUFFER_TOO_SMALL) {812throw (ShortBufferException)813(new ShortBufferException().initCause(e));814} else if (errorCode == CKR_DATA_LEN_RANGE ||815errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {816throw (IllegalBlockSizeException)817(new IllegalBlockSizeException(e.toString()).initCause(e));818} else if (errorCode == CKR_ENCRYPTED_DATA_INVALID ||819// Solaris-specific820errorCode == CKR_GENERAL_ERROR) {821throw (AEADBadTagException)822(new AEADBadTagException(e.toString()).initCause(e));823}824}825826// see JCE spec827protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,828InvalidKeyException {829// XXX key wrapping830throw new UnsupportedOperationException("engineWrap()");831}832833// see JCE spec834protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,835int wrappedKeyType)836throws InvalidKeyException, NoSuchAlgorithmException {837// XXX key unwrapping838throw new UnsupportedOperationException("engineUnwrap()");839}840841// see JCE spec842@Override843protected int engineGetKeySize(Key key) throws InvalidKeyException {844int n = P11SecretKeyFactory.convertKey845(token, key, type.keyAlgo).length();846return n;847}848}849850851852