/* * Copyright (c) 2014, 2015, 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 com.oracle.security.ucrypto; import java.util.Set; import java.util.Arrays; import java.util.concurrent.ConcurrentSkipListSet; import java.lang.ref.*; import java.math.BigInteger; import java.nio.ByteBuffer; import java.security.SignatureSpi; import java.security.NoSuchAlgorithmException; import java.security.InvalidParameterException; import java.security.InvalidKeyException; import java.security.SignatureException; import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.*; import java.security.interfaces.*; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.InvalidKeySpecException; import sun.nio.ch.DirectBuffer; import java.nio.ByteBuffer; /** * Signature implementation class. This class currently supports the * following algorithms: * * . RSA: * . MD5withRSA * . SHA1withRSA * . SHA256withRSA * . SHA384withRSA * . SHA512withRSA * * @since 1.9 */ class NativeRSASignature extends SignatureSpi { private static final int PKCS1PADDING_LEN = 11; // fields set in constructor private final UcryptoMech mech; private final int encodedLen; // field for ensuring native memory is freed private SignatureContextRef pCtxt = null; // // fields (re)set in every init() // private boolean initialized = false; private boolean sign = true; private int sigLength; private NativeKey key; private NativeRSAKeyFactory keyFactory; // may need a more generic type later // public implementation classes public static final class MD5 extends NativeRSASignature { public MD5() throws NoSuchAlgorithmException { super(UcryptoMech.CRYPTO_MD5_RSA_PKCS, 34); } } public static final class SHA1 extends NativeRSASignature { public SHA1() throws NoSuchAlgorithmException { super(UcryptoMech.CRYPTO_SHA1_RSA_PKCS, 35); } } public static final class SHA256 extends NativeRSASignature { public SHA256() throws NoSuchAlgorithmException { super(UcryptoMech.CRYPTO_SHA256_RSA_PKCS, 51); } } public static final class SHA384 extends NativeRSASignature { public SHA384() throws NoSuchAlgorithmException { super(UcryptoMech.CRYPTO_SHA384_RSA_PKCS, 67); } } public static final class SHA512 extends NativeRSASignature { public SHA512() throws NoSuchAlgorithmException { super(UcryptoMech.CRYPTO_SHA512_RSA_PKCS, 83); } } // internal class for native resource cleanup private static class SignatureContextRef extends PhantomReference implements Comparable { private static ReferenceQueue refQueue = new ReferenceQueue(); // Needed to keep these references from being GC'ed until when their // referents are GC'ed so we can do post-mortem processing private static Set refList = new ConcurrentSkipListSet(); // Collections.synchronizedSortedSet(new TreeSet()); private final long id; private final boolean sign; private static void drainRefQueueBounded() { while (true) { SignatureContextRef next = (SignatureContextRef) refQueue.poll(); if (next == null) break; next.dispose(true); } } SignatureContextRef(NativeRSASignature ns, long id, boolean sign) { super(ns, refQueue); this.id = id; this.sign = sign; refList.add(this); UcryptoProvider.debug("Resource: track Signature Ctxt " + this.id); drainRefQueueBounded(); } public int compareTo(SignatureContextRef other) { if (this.id == other.id) { return 0; } else { return (this.id < other.id) ? -1 : 1; } } void dispose(boolean doCancel) { refList.remove(this); try { if (doCancel) { UcryptoProvider.debug("Resource: free Signature Ctxt " + this.id); NativeRSASignature.nativeFinal(id, sign, null, 0, 0); } else { UcryptoProvider.debug("Resource: stop tracking Signature Ctxt " + this.id); } } finally { this.clear(); } } } NativeRSASignature(UcryptoMech mech, int encodedLen) throws NoSuchAlgorithmException { this.mech = mech; this.encodedLen = encodedLen; this.keyFactory = new NativeRSAKeyFactory(); } // deprecated but abstract @SuppressWarnings("deprecation") protected Object engineGetParameter(String param) throws InvalidParameterException { throw new UnsupportedOperationException("getParameter() not supported"); } @Override protected synchronized void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { if (privateKey == null) { throw new InvalidKeyException("Key must not be null"); } NativeKey newKey = key; int newSigLength = sigLength; // Need to check RSA key length whenever a new private key is set if (privateKey != key) { if (privateKey instanceof RSAPrivateCrtKey) { RSAPrivateCrtKey rsaPrivKey = (RSAPrivateCrtKey) privateKey; BigInteger mod = rsaPrivKey.getModulus(); newSigLength = checkRSAKeyLength(mod); try { newKey = (NativeKey) keyFactory.engineGeneratePrivate (new RSAPrivateCrtKeySpec(mod, rsaPrivKey.getPublicExponent(), rsaPrivKey.getPrivateExponent(), rsaPrivKey.getPrimeP(), rsaPrivKey.getPrimeQ(), rsaPrivKey.getPrimeExponentP(), rsaPrivKey.getPrimeExponentQ(), rsaPrivKey.getCrtCoefficient())); } catch (InvalidKeySpecException ikse) { throw new InvalidKeyException(ikse); } } else { throw new InvalidKeyException("RSAPrivateCrtKey required"); } } init(true, newKey, newSigLength); } @Override protected synchronized void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { if (publicKey == null) { throw new InvalidKeyException("Key must not be null"); } NativeKey newKey = key; int newSigLength = sigLength; // Need to check RSA key length whenever a new public key is set if (publicKey != key) { if (publicKey instanceof RSAPublicKey) { BigInteger mod = ((RSAPublicKey) publicKey).getModulus(); newSigLength = checkRSAKeyLength(mod); try { newKey = (NativeKey) keyFactory.engineGeneratePublic (new RSAPublicKeySpec(mod, ((RSAPublicKey) publicKey).getPublicExponent())); } catch (InvalidKeySpecException ikse) { throw new InvalidKeyException(ikse); } } else { throw new InvalidKeyException("RSAPublicKey required"); } } init(false, newKey, newSigLength); } // deprecated but abstract @SuppressWarnings("deprecation") protected void engineSetParameter(String param, Object value) throws InvalidParameterException { throw new UnsupportedOperationException("setParameter() not supported"); } @Override protected synchronized byte[] engineSign() throws SignatureException { byte[] sig = new byte[sigLength]; int rv = doFinal(sig, 0, sigLength); if (rv < 0) { throw new SignatureException(new UcryptoException(-rv)); } return sig; } @Override protected synchronized int engineSign(byte[] outbuf, int offset, int len) throws SignatureException { if (outbuf == null || (offset < 0) || (outbuf.length < (offset + sigLength)) || (len < sigLength)) { throw new SignatureException("Invalid output buffer"); } int rv = doFinal(outbuf, offset, sigLength); if (rv < 0) { throw new SignatureException(new UcryptoException(-rv)); } return sigLength; } @Override protected synchronized void engineUpdate(byte b) throws SignatureException { byte[] in = { b }; int rv = update(in, 0, 1); if (rv < 0) { throw new SignatureException(new UcryptoException(-rv)); } } @Override protected synchronized void engineUpdate(byte[] in, int inOfs, int inLen) throws SignatureException { if (in == null || inOfs < 0 || inLen == 0) return; int rv = update(in, inOfs, inLen); if (rv < 0) { throw new SignatureException(new UcryptoException(-rv)); } } @Override protected synchronized void engineUpdate(ByteBuffer in) { if (in == null || in.remaining() == 0) return; if (in instanceof DirectBuffer == false) { // cannot do better than default impl super.engineUpdate(in); return; } long inAddr = ((DirectBuffer)in).address(); int inOfs = in.position(); int inLen = in.remaining(); int rv = update((inAddr + inOfs), inLen); if (rv < 0) { throw new UcryptoException(-rv); } in.position(inOfs + inLen); } @Override protected synchronized boolean engineVerify(byte[] sigBytes) throws SignatureException { return engineVerify(sigBytes, 0, sigBytes.length); } @Override protected synchronized boolean engineVerify(byte[] sigBytes, int sigOfs, int sigLen) throws SignatureException { if (sigBytes == null || (sigOfs < 0) || (sigBytes.length < (sigOfs + this.sigLength)) || (sigLen < this.sigLength)) { throw new SignatureException("Invalid signature buffer"); } int rv = doFinal(sigBytes, sigOfs, sigLen); if (rv == 0) { return true; } else { UcryptoProvider.debug("Signature: " + mech + " verification error " + new UcryptoException(-rv).getMessage()); return false; } } void reset(boolean doCancel) { initialized = false; if (pCtxt != null) { pCtxt.dispose(doCancel); pCtxt = null; } } /** * calls ucrypto_sign_init(...) or ucrypto_verify_init(...) * @return pointer to the context */ private native static long nativeInit(int mech, boolean sign, long keyValue, int keyLength); /** * calls ucrypto_sign_update(...) or ucrypto_verify_update(...) * @returns an error status code (0 means SUCCESS) */ private native static int nativeUpdate(long pContext, boolean sign, byte[] in, int inOfs, int inLen); /** * calls ucrypto_sign_update(...) or ucrypto_verify_update(...) * @returns an error status code (0 means SUCCESS) */ private native static int nativeUpdate(long pContext, boolean sign, long pIn, int inLen); /** * calls ucrypto_sign_final(...) or ucrypto_verify_final(...) * @returns the length of signature bytes or verification status. * If negative, it indicates an error status code */ private native static int nativeFinal(long pContext, boolean sign, byte[] sig, int sigOfs, int sigLen); // actual init() implementation - caller should clone key if needed private void init(boolean sign, NativeKey key, int sigLength) { reset(true); this.sign = sign; this.sigLength = sigLength; this.key = key; long pCtxtVal = nativeInit(mech.value(), sign, key.value(), key.length()); initialized = (pCtxtVal != 0L); if (initialized) { pCtxt = new SignatureContextRef(this, pCtxtVal, sign); } else { throw new UcryptoException("Cannot initialize Signature"); } } private void ensureInitialized() { if (!initialized) { init(sign, key, sigLength); if (!initialized) { throw new UcryptoException("Cannot initialize Signature"); } } } // returns 0 (success) or negative (ucrypto error occurred) private int update(byte[] in, int inOfs, int inLen) { if (inOfs < 0 || inOfs + inLen > in.length) { throw new ArrayIndexOutOfBoundsException(); } ensureInitialized(); int k = nativeUpdate(pCtxt.id, sign, in, inOfs, inLen); if (k < 0) { reset(false); } return k; } // returns 0 (success) or negative (ucrypto error occurred) private int update(long pIn, int inLen) { ensureInitialized(); int k = nativeUpdate(pCtxt.id, sign, pIn, inLen); if (k < 0) { reset(false); } return k; } // returns 0 (success) or negative (ucrypto error occurred) private int doFinal(byte[] sigBytes, int sigOfs, int sigLen) { try { ensureInitialized(); int k = nativeFinal(pCtxt.id, sign, sigBytes, sigOfs, sigLen); return k; } finally { reset(false); } } // check and return RSA key size in number of bytes private int checkRSAKeyLength(BigInteger mod) throws InvalidKeyException { int keySize = (mod.bitLength() + 7) >> 3; int maxDataSize = keySize - PKCS1PADDING_LEN; if (maxDataSize < encodedLen) { throw new InvalidKeyException ("Key is too short for this signature algorithm"); } return keySize; } }