--- /dev/null 2017-01-18 09:30:05.425422781 -0800 +++ new/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Signature.java 2017-01-18 23:07:16.383885575 -0800 @@ -0,0 +1,825 @@ +/* + * Copyright (c) 2003, 2016, 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.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.interfaces.*; +import sun.nio.ch.DirectBuffer; + +import sun.security.util.*; +import sun.security.x509.AlgorithmId; + +import sun.security.rsa.RSASignature; +import sun.security.rsa.RSAPadding; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import sun.security.util.KeyUtil; + +/** + * Signature implementation class. This class currently supports the + * following algorithms: + * + * . DSA + * . NONEwithDSA (RawDSA) + * . SHA1withDSA + * . NONEwithDSAinP1363Format (RawDSAinP1363Format) + * . SHA1withDSAinP1363Format + * . RSA: + * . MD2withRSA + * . MD5withRSA + * . SHA1withRSA + * . SHA224withRSA + * . SHA256withRSA + * . SHA384withRSA + * . SHA512withRSA + * . ECDSA + * . NONEwithECDSA + * . SHA1withECDSA + * . SHA224withECDSA + * . SHA256withECDSA + * . SHA384withECDSA + * . SHA512withECDSA + * . NONEwithECDSAinP1363Format + * . SHA1withECDSAinP1363Format + * . SHA224withECDSAinP1363Format + * . SHA256withECDSAinP1363Format + * . SHA384withECDSAinP1363Format + * . SHA512withECDSAinP1363Format + * + * Note that the underlying PKCS#11 token may support complete signature + * algorithm (e.g. CKM_DSA_SHA1, CKM_MD5_RSA_PKCS), or it may just + * implement the signature algorithm without hashing (e.g. CKM_DSA, CKM_PKCS), + * or it may only implement the raw public key operation (CKM_RSA_X_509). + * This class uses what is available and adds whatever extra processing + * is needed. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11Signature extends SignatureSpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // name of the key algorithm, currently either RSA or DSA + private final String keyAlgorithm; + + // mechanism id + private final long mechanism; + + // digest algorithm OID, if we encode RSA signature ourselves + private final ObjectIdentifier digestOID; + + // type, one of T_* below + private final int type; + + // key instance used, if init*() was called + private P11Key p11Key; + + // message digest, if we do the digesting ourselves + private final MessageDigest md; + + // associated session, if any + private Session session; + + // mode, one of M_* below + private int mode; + + // flag indicating whether an operation is initialized + private boolean initialized; + + // buffer, for update(byte) or DSA + private final byte[] buffer; + + // total number of bytes processed in current operation + private int bytesProcessed; + + // The format, to be used for DSA and ECDSA signatures. + // If true, the IEEE P1363 format will be used, the concatenation of + // r and s. If false (default), the signature will be formatted as a + // DER-encoded ASN.1 sequence of r and s. + private boolean p1363Format = false; + + // constant for signing mode + private final static int M_SIGN = 1; + // constant for verification mode + private final static int M_VERIFY = 2; + + // constant for type digesting, we do the hashing ourselves + private final static int T_DIGEST = 1; + // constant for type update, token does everything + private final static int T_UPDATE = 2; + // constant for type raw, used with RawDSA and NONEwithECDSA only + private final static int T_RAW = 3; + + // XXX PKCS#11 v2.20 says "should not be longer than 1024 bits", + // but this is a little arbitrary + private final static int RAW_ECDSA_MAX = 128; + + P11Signature(Token token, String algorithm, long mechanism) + throws NoSuchAlgorithmException, PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + byte[] buffer = null; + ObjectIdentifier digestOID = null; + MessageDigest md = null; + switch ((int)mechanism) { + case (int)CKM_MD2_RSA_PKCS: + case (int)CKM_MD5_RSA_PKCS: + case (int)CKM_SHA1_RSA_PKCS: + case (int)CKM_SHA224_RSA_PKCS: + case (int)CKM_SHA256_RSA_PKCS: + case (int)CKM_SHA384_RSA_PKCS: + case (int)CKM_SHA512_RSA_PKCS: + keyAlgorithm = "RSA"; + type = T_UPDATE; + buffer = new byte[1]; + break; + case (int)CKM_DSA_SHA1: + keyAlgorithm = "DSA"; + type = T_UPDATE; + buffer = new byte[1]; + break; + case (int)CKM_ECDSA_SHA1: + keyAlgorithm = "EC"; + type = T_UPDATE; + buffer = new byte[1]; + break; + case (int)CKM_DSA: + keyAlgorithm = "DSA"; + if (algorithm.equals("DSA") || + algorithm.equals("DSAinP1363Format")) { + type = T_DIGEST; + md = MessageDigest.getInstance("SHA-1"); + } else if (algorithm.equals("RawDSA") || + algorithm.equals("RawDSAinP1363Format")) { + type = T_RAW; + buffer = new byte[20]; + } else { + throw new ProviderException(algorithm); + } + break; + case (int)CKM_ECDSA: + keyAlgorithm = "EC"; + if (algorithm.equals("NONEwithECDSA") || + algorithm.equals("NONEwithECDSAinP1363Format")) { + type = T_RAW; + buffer = new byte[RAW_ECDSA_MAX]; + } else { + String digestAlg; + if (algorithm.equals("SHA1withECDSA") || + algorithm.equals("SHA1withECDSAinP1363Format")) { + digestAlg = "SHA-1"; + } else if (algorithm.equals("SHA224withECDSA") || + algorithm.equals("SHA224withECDSAinP1363Format")) { + digestAlg = "SHA-224"; + } else if (algorithm.equals("SHA256withECDSA") || + algorithm.equals("SHA256withECDSAinP1363Format")) { + digestAlg = "SHA-256"; + } else if (algorithm.equals("SHA384withECDSA") || + algorithm.equals("SHA384withECDSAinP1363Format")) { + digestAlg = "SHA-384"; + } else if (algorithm.equals("SHA512withECDSA") || + algorithm.equals("SHA512withECDSAinP1363Format")) { + digestAlg = "SHA-512"; + } else { + throw new ProviderException(algorithm); + } + type = T_DIGEST; + md = MessageDigest.getInstance(digestAlg); + } + break; + case (int)CKM_RSA_PKCS: + case (int)CKM_RSA_X_509: + keyAlgorithm = "RSA"; + type = T_DIGEST; + if (algorithm.equals("MD5withRSA")) { + md = MessageDigest.getInstance("MD5"); + digestOID = AlgorithmId.MD5_oid; + } else if (algorithm.equals("SHA1withRSA")) { + md = MessageDigest.getInstance("SHA-1"); + digestOID = AlgorithmId.SHA_oid; + } else if (algorithm.equals("MD2withRSA")) { + md = MessageDigest.getInstance("MD2"); + digestOID = AlgorithmId.MD2_oid; + } else if (algorithm.equals("SHA224withRSA")) { + md = MessageDigest.getInstance("SHA-224"); + digestOID = AlgorithmId.SHA224_oid; + } else if (algorithm.equals("SHA256withRSA")) { + md = MessageDigest.getInstance("SHA-256"); + digestOID = AlgorithmId.SHA256_oid; + } else if (algorithm.equals("SHA384withRSA")) { + md = MessageDigest.getInstance("SHA-384"); + digestOID = AlgorithmId.SHA384_oid; + } else if (algorithm.equals("SHA512withRSA")) { + md = MessageDigest.getInstance("SHA-512"); + digestOID = AlgorithmId.SHA512_oid; + } else { + throw new ProviderException("Unknown signature: " + algorithm); + } + break; + default: + throw new ProviderException("Unknown mechanism: " + mechanism); + } + this.buffer = buffer; + this.digestOID = digestOID; + this.md = md; + if (algorithm.endsWith("inP1363Format")) { + this.p1363Format = true; + } + } + + private void ensureInitialized() { + token.ensureValid(); + if (initialized == false) { + initialize(); + } + } + + private void cancelOperation() { + token.ensureValid(); + if (initialized == false) { + return; + } + initialized = false; + if ((session == null) || (token.explicitCancel == false)) { + return; + } + if (session.hasObjects() == false) { + session = token.killSession(session); + return; + } + // "cancel" operation by finishing it + // XXX make sure all this always works correctly + if (mode == M_SIGN) { + try { + if (type == T_UPDATE) { + token.p11.C_SignFinal(session.id(), 0); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + digest = buffer; + } + token.p11.C_Sign(session.id(), digest); + } + } catch (PKCS11Exception e) { + throw new ProviderException("cancel failed", e); + } + } else { // M_VERIFY + try { + byte[] signature; + if (keyAlgorithm.equals("DSA")) { + signature = new byte[40]; + } else { + signature = new byte[(p11Key.length() + 7) >> 3]; + } + if (type == T_UPDATE) { + token.p11.C_VerifyFinal(session.id(), signature); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + digest = buffer; + } + token.p11.C_Verify(session.id(), digest, signature); + } + } catch (PKCS11Exception e) { + // will fail since the signature is incorrect + // XXX check error code + } + } + } + + // assumes current state is initialized == false + private void initialize() { + try { + if (session == null) { + session = token.getOpSession(); + } + if (mode == M_SIGN) { + token.p11.C_SignInit(session.id(), + new CK_MECHANISM(mechanism), p11Key.keyID); + } else { + token.p11.C_VerifyInit(session.id(), + new CK_MECHANISM(mechanism), p11Key.keyID); + } + initialized = true; + } catch (PKCS11Exception e) { + throw new ProviderException("Initialization failed", e); + } + if (bytesProcessed != 0) { + bytesProcessed = 0; + if (md != null) { + md.reset(); + } + } + } + + private void checkKeySize(String keyAlgo, Key key) + throws InvalidKeyException { + CK_MECHANISM_INFO mechInfo = null; + try { + mechInfo = token.getMechanismInfo(mechanism); + } catch (PKCS11Exception e) { + // should not happen, ignore for now. + } + if (mechInfo == null) { + // skip the check if no native info available + return; + } + int minKeySize = (int) mechInfo.ulMinKeySize; + int maxKeySize = (int) mechInfo.ulMaxKeySize; + // need to override the MAX keysize for SHA1withDSA + if (md != null && mechanism == CKM_DSA && maxKeySize > 1024) { + maxKeySize = 1024; + } + int keySize = 0; + if (key instanceof P11Key) { + keySize = ((P11Key) key).length(); + } else { + if (keyAlgo.equals("RSA")) { + keySize = ((RSAKey) key).getModulus().bitLength(); + } else if (keyAlgo.equals("DSA")) { + keySize = ((DSAKey) key).getParams().getP().bitLength(); + } else if (keyAlgo.equals("EC")) { + keySize = ((ECKey) key).getParams().getCurve().getField().getFieldSize(); + } else { + throw new ProviderException("Error: unsupported algo " + keyAlgo); + } + } + if ((minKeySize != -1) && (keySize < minKeySize)) { + throw new InvalidKeyException(keyAlgo + + " key must be at least " + minKeySize + " bits"); + } + if ((maxKeySize != -1) && (keySize > maxKeySize)) { + throw new InvalidKeyException(keyAlgo + + " key must be at most " + maxKeySize + " bits"); + } + if (keyAlgo.equals("RSA")) { + checkRSAKeyLength(keySize); + } + } + + private void checkRSAKeyLength(int len) throws InvalidKeyException { + RSAPadding padding; + try { + padding = RSAPadding.getInstance + (RSAPadding.PAD_BLOCKTYPE_1, (len + 7) >> 3); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidKeyException(iape.getMessage()); + } + int maxDataSize = padding.getMaxDataSize(); + int encodedLength; + if (algorithm.equals("MD5withRSA") || + algorithm.equals("MD2withRSA")) { + encodedLength = 34; + } else if (algorithm.equals("SHA1withRSA")) { + encodedLength = 35; + } else if (algorithm.equals("SHA224withRSA")) { + encodedLength = 47; + } else if (algorithm.equals("SHA256withRSA")) { + encodedLength = 51; + } else if (algorithm.equals("SHA384withRSA")) { + encodedLength = 67; + } else if (algorithm.equals("SHA512withRSA")) { + encodedLength = 83; + } else { + throw new ProviderException("Unknown signature algo: " + algorithm); + } + if (encodedLength > maxDataSize) { + throw new InvalidKeyException + ("Key is too short for this signature algorithm"); + } + } + + // see JCA spec + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + if (publicKey == null) { + throw new InvalidKeyException("Key must not be null"); + } + // Need to check key length whenever a new key is set + if (publicKey != p11Key) { + checkKeySize(keyAlgorithm, publicKey); + } + cancelOperation(); + mode = M_VERIFY; + p11Key = P11KeyFactory.convertKey(token, publicKey, keyAlgorithm); + initialize(); + } + + // see JCA spec + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + if (privateKey == null) { + throw new InvalidKeyException("Key must not be null"); + } + // Need to check RSA key length whenever a new key is set + if (privateKey != p11Key) { + checkKeySize(keyAlgorithm, privateKey); + } + cancelOperation(); + mode = M_SIGN; + p11Key = P11KeyFactory.convertKey(token, privateKey, keyAlgorithm); + initialize(); + } + + // see JCA spec + protected void engineUpdate(byte b) throws SignatureException { + ensureInitialized(); + switch (type) { + case T_UPDATE: + buffer[0] = b; + engineUpdate(buffer, 0, 1); + break; + case T_DIGEST: + md.update(b); + bytesProcessed++; + break; + case T_RAW: + if (bytesProcessed >= buffer.length) { + bytesProcessed = buffer.length + 1; + return; + } + buffer[bytesProcessed++] = b; + break; + default: + throw new ProviderException("Internal error"); + } + } + + // see JCA spec + protected void engineUpdate(byte[] b, int ofs, int len) + throws SignatureException { + ensureInitialized(); + if (len == 0) { + return; + } + switch (type) { + case T_UPDATE: + try { + if (mode == M_SIGN) { + token.p11.C_SignUpdate(session.id(), 0, b, ofs, len); + } else { + token.p11.C_VerifyUpdate(session.id(), 0, b, ofs, len); + } + bytesProcessed += len; + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } + break; + case T_DIGEST: + md.update(b, ofs, len); + bytesProcessed += len; + break; + case T_RAW: + if (bytesProcessed + len > buffer.length) { + bytesProcessed = buffer.length + 1; + return; + } + System.arraycopy(b, ofs, buffer, bytesProcessed, len); + bytesProcessed += len; + break; + default: + throw new ProviderException("Internal error"); + } + } + + // see JCA spec + protected void engineUpdate(ByteBuffer byteBuffer) { + ensureInitialized(); + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + switch (type) { + case T_UPDATE: + if (byteBuffer instanceof DirectBuffer == false) { + // cannot do better than default impl + super.engineUpdate(byteBuffer); + return; + } + long addr = ((DirectBuffer)byteBuffer).address(); + int ofs = byteBuffer.position(); + try { + if (mode == M_SIGN) { + token.p11.C_SignUpdate + (session.id(), addr + ofs, null, 0, len); + } else { + token.p11.C_VerifyUpdate + (session.id(), addr + ofs, null, 0, len); + } + bytesProcessed += len; + byteBuffer.position(ofs + len); + } catch (PKCS11Exception e) { + throw new ProviderException("Update failed", e); + } + break; + case T_DIGEST: + md.update(byteBuffer); + bytesProcessed += len; + break; + case T_RAW: + if (bytesProcessed + len > buffer.length) { + bytesProcessed = buffer.length + 1; + return; + } + byteBuffer.get(buffer, bytesProcessed, len); + bytesProcessed += len; + break; + default: + throw new ProviderException("Internal error"); + } + } + + // see JCA spec + protected byte[] engineSign() throws SignatureException { + ensureInitialized(); + try { + byte[] signature; + if (type == T_UPDATE) { + int len = keyAlgorithm.equals("DSA") ? 40 : 0; + signature = token.p11.C_SignFinal(session.id(), len); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + if (mechanism == CKM_DSA) { + if (bytesProcessed != buffer.length) { + throw new SignatureException + ("Data for RawDSA must be exactly 20 bytes long"); + } + digest = buffer; + } else { // CKM_ECDSA + if (bytesProcessed > buffer.length) { + throw new SignatureException("Data for NONEwithECDSA" + + " must be at most " + RAW_ECDSA_MAX + " bytes long"); + } + digest = new byte[bytesProcessed]; + System.arraycopy(buffer, 0, digest, 0, bytesProcessed); + } + } + if (keyAlgorithm.equals("RSA") == false) { + // DSA and ECDSA + signature = token.p11.C_Sign(session.id(), digest); + } else { // RSA + byte[] data = encodeSignature(digest); + if (mechanism == CKM_RSA_X_509) { + data = pkcs1Pad(data); + } + signature = token.p11.C_Sign(session.id(), data); + } + } + if (keyAlgorithm.equals("RSA")) { + return signature; + } else { + if (p1363Format) { + return signature; + } else { + return dsaToASN1(signature); + } + } + } catch (PKCS11Exception pe) { + throw new ProviderException(pe); + } catch (SignatureException | ProviderException e) { + cancelOperation(); + throw e; + } finally { + initialized = false; + session = token.releaseSession(session); + } + } + + // see JCA spec + protected boolean engineVerify(byte[] signature) throws SignatureException { + ensureInitialized(); + try { + if (!p1363Format) { + if (keyAlgorithm.equals("DSA")) { + signature = asn1ToDSA(signature); + } else if (keyAlgorithm.equals("EC")) { + signature = asn1ToECDSA(signature); + } + } + if (type == T_UPDATE) { + token.p11.C_VerifyFinal(session.id(), signature); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + if (mechanism == CKM_DSA) { + if (bytesProcessed != buffer.length) { + throw new SignatureException + ("Data for RawDSA must be exactly 20 bytes long"); + } + digest = buffer; + } else { + if (bytesProcessed > buffer.length) { + throw new SignatureException("Data for NONEwithECDSA" + + " must be at most " + RAW_ECDSA_MAX + " bytes long"); + } + digest = new byte[bytesProcessed]; + System.arraycopy(buffer, 0, digest, 0, bytesProcessed); + } + } + if (keyAlgorithm.equals("RSA") == false) { + // DSA and ECDSA + token.p11.C_Verify(session.id(), digest, signature); + } else { // RSA + byte[] data = encodeSignature(digest); + if (mechanism == CKM_RSA_X_509) { + data = pkcs1Pad(data); + } + token.p11.C_Verify(session.id(), data, signature); + } + } + return true; + } catch (PKCS11Exception pe) { + long errorCode = pe.getErrorCode(); + if (errorCode == CKR_SIGNATURE_INVALID) { + return false; + } + if (errorCode == CKR_SIGNATURE_LEN_RANGE) { + // return false rather than throwing an exception + return false; + } + // ECF bug? + if (errorCode == CKR_DATA_LEN_RANGE) { + return false; + } + throw new ProviderException(pe); + } catch (SignatureException | ProviderException e) { + cancelOperation(); + throw e; + } finally { + initialized = false; + session = token.releaseSession(session); + } + } + + private byte[] pkcs1Pad(byte[] data) { + try { + int len = (p11Key.length() + 7) >> 3; + RSAPadding padding = RSAPadding.getInstance + (RSAPadding.PAD_BLOCKTYPE_1, len); + byte[] padded = padding.pad(data); + return padded; + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + + private byte[] encodeSignature(byte[] digest) throws SignatureException { + try { + return RSASignature.encodeSignature(digestOID, digest); + } catch (IOException e) { + throw new SignatureException("Invalid encoding", e); + } + } + +// private static byte[] decodeSignature(byte[] signature) throws IOException { +// return RSASignature.decodeSignature(digestOID, signature); +// } + + // For DSA and ECDSA signatures, PKCS#11 represents them as a simple + // byte array that contains the concatenation of r and s. + // For DSA, r and s are always exactly 20 bytes long. + // For ECDSA, r and s are of variable length, but we know that each + // occupies half of the array. + private static byte[] dsaToASN1(byte[] signature) { + int n = signature.length >> 1; + BigInteger r = new BigInteger(1, P11Util.subarray(signature, 0, n)); + BigInteger s = new BigInteger(1, P11Util.subarray(signature, n, n)); + try { + DerOutputStream outseq = new DerOutputStream(100); + outseq.putInteger(r); + outseq.putInteger(s); + DerValue result = new DerValue(DerValue.tag_Sequence, + outseq.toByteArray()); + return result.toByteArray(); + } catch (java.io.IOException e) { + throw new RuntimeException("Internal error", e); + } + } + + private static byte[] asn1ToDSA(byte[] signature) throws SignatureException { + try { + DerInputStream in = new DerInputStream(signature); + DerValue[] values = in.getSequence(2); + BigInteger r = values[0].getPositiveBigInteger(); + BigInteger s = values[1].getPositiveBigInteger(); + + // Check for trailing signature data + if (in.available() != 0) { + throw new IOException("Incorrect signature length"); + } + byte[] br = toByteArray(r, 20); + byte[] bs = toByteArray(s, 20); + if ((br == null) || (bs == null)) { + throw new SignatureException("Out of range value for R or S"); + } + return P11Util.concat(br, bs); + } catch (SignatureException e) { + throw e; + } catch (Exception e) { + throw new SignatureException("invalid encoding for signature", e); + } + } + + private byte[] asn1ToECDSA(byte[] signature) throws SignatureException { + try { + DerInputStream in = new DerInputStream(signature); + DerValue[] values = in.getSequence(2); + BigInteger r = values[0].getPositiveBigInteger(); + BigInteger s = values[1].getPositiveBigInteger(); + + // Check for trailing signature data + if (in.available() != 0) { + throw new IOException("Incorrect signature length"); + } + // trim leading zeroes + byte[] br = KeyUtil.trimZeroes(r.toByteArray()); + byte[] bs = KeyUtil.trimZeroes(s.toByteArray()); + int k = Math.max(br.length, bs.length); + // r and s each occupy half the array + byte[] res = new byte[k << 1]; + System.arraycopy(br, 0, res, k - br.length, br.length); + System.arraycopy(bs, 0, res, res.length - bs.length, bs.length); + return res; + } catch (Exception e) { + throw new SignatureException("invalid encoding for signature", e); + } + } + + private static byte[] toByteArray(BigInteger bi, int len) { + byte[] b = bi.toByteArray(); + int n = b.length; + if (n == len) { + return b; + } + if ((n == len + 1) && (b[0] == 0)) { + byte[] t = new byte[len]; + System.arraycopy(b, 1, t, 0, len); + return t; + } + if (n > len) { + return null; + } + // must be smaller + byte[] t = new byte[len]; + System.arraycopy(b, 0, t, (len - n), n); + return t; + } + + // see JCA spec + @SuppressWarnings("deprecation") + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + // see JCA spec + @SuppressWarnings("deprecation") + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } +}