| /* |
| * Copyright (c) 2005, 2018, 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.mscapi; |
| |
| import java.nio.ByteBuffer; |
| import java.security.PublicKey; |
| import java.security.PrivateKey; |
| import java.security.InvalidKeyException; |
| import java.security.InvalidParameterException; |
| import java.security.KeyStoreException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.ProviderException; |
| import java.security.MessageDigest; |
| import java.security.SignatureException; |
| import java.math.BigInteger; |
| |
| import sun.security.rsa.RSAKeyFactory; |
| |
| /** |
| * RSA signature implementation. Supports RSA signing using PKCS#1 v1.5 padding. |
| * |
| * Objects should be instantiated by calling Signature.getInstance() using the |
| * following algorithm names: |
| * |
| * . "NONEwithRSA" |
| * . "SHA1withRSA" |
| * . "SHA256withRSA" |
| * . "SHA384withRSA" |
| * . "SHA512withRSA" |
| * . "MD5withRSA" |
| * . "MD2withRSA" |
| * |
| * NOTE: RSA keys must be at least 512 bits long. |
| * |
| * NOTE: NONEwithRSA must be supplied with a pre-computed message digest. |
| * Only the following digest algorithms are supported: MD5, SHA-1, |
| * SHA-256, SHA-384, SHA-512 and a special-purpose digest |
| * algorithm which is a concatenation of SHA-1 and MD5 digests. |
| * |
| * @since 1.6 |
| * @author Stanley Man-Kit Ho |
| */ |
| abstract class RSASignature extends java.security.SignatureSpi |
| { |
| // message digest implementation we use |
| private final MessageDigest messageDigest; |
| |
| // message digest name |
| private String messageDigestAlgorithm; |
| |
| // flag indicating whether the digest has been reset |
| private boolean needsReset; |
| |
| // the signing key |
| private Key privateKey = null; |
| |
| // the verification key |
| private Key publicKey = null; |
| |
| /** |
| * Constructs a new RSASignature. Used by Raw subclass. |
| */ |
| RSASignature() { |
| messageDigest = null; |
| messageDigestAlgorithm = null; |
| } |
| |
| /** |
| * Constructs a new RSASignature. Used by subclasses. |
| */ |
| RSASignature(String digestName) { |
| |
| try { |
| messageDigest = MessageDigest.getInstance(digestName); |
| // Get the digest's canonical name |
| messageDigestAlgorithm = messageDigest.getAlgorithm(); |
| |
| } catch (NoSuchAlgorithmException e) { |
| throw new ProviderException(e); |
| } |
| |
| needsReset = false; |
| } |
| |
| // Nested class for NONEwithRSA signatures |
| public static final class Raw extends RSASignature { |
| |
| // the longest supported digest is 512 bits (SHA-512) |
| private static final int RAW_RSA_MAX = 64; |
| |
| private final byte[] precomputedDigest; |
| private int offset = 0; |
| |
| public Raw() { |
| precomputedDigest = new byte[RAW_RSA_MAX]; |
| } |
| |
| // Stores the precomputed message digest value. |
| @Override |
| protected void engineUpdate(byte b) throws SignatureException { |
| if (offset >= precomputedDigest.length) { |
| offset = RAW_RSA_MAX + 1; |
| return; |
| } |
| precomputedDigest[offset++] = b; |
| } |
| |
| // Stores the precomputed message digest value. |
| @Override |
| protected void engineUpdate(byte[] b, int off, int len) |
| throws SignatureException { |
| if (len > (precomputedDigest.length - offset)) { |
| offset = RAW_RSA_MAX + 1; |
| return; |
| } |
| System.arraycopy(b, off, precomputedDigest, offset, len); |
| offset += len; |
| } |
| |
| // Stores the precomputed message digest value. |
| @Override |
| protected void engineUpdate(ByteBuffer byteBuffer) { |
| int len = byteBuffer.remaining(); |
| if (len <= 0) { |
| return; |
| } |
| if (len > (precomputedDigest.length - offset)) { |
| offset = RAW_RSA_MAX + 1; |
| return; |
| } |
| byteBuffer.get(precomputedDigest, offset, len); |
| offset += len; |
| } |
| |
| @Override |
| protected void resetDigest(){ |
| offset = 0; |
| } |
| |
| // Returns the precomputed message digest value. |
| @Override |
| protected byte[] getDigestValue() throws SignatureException { |
| if (offset > RAW_RSA_MAX) { |
| throw new SignatureException("Message digest is too long"); |
| } |
| |
| // Determine the digest algorithm from the digest length |
| if (offset == 20) { |
| setDigestName("SHA1"); |
| } else if (offset == 36) { |
| setDigestName("SHA1+MD5"); |
| } else if (offset == 32) { |
| setDigestName("SHA-256"); |
| } else if (offset == 48) { |
| setDigestName("SHA-384"); |
| } else if (offset == 64) { |
| setDigestName("SHA-512"); |
| } else if (offset == 16) { |
| setDigestName("MD5"); |
| } else { |
| throw new SignatureException( |
| "Message digest length is not supported"); |
| } |
| |
| byte[] result = new byte[offset]; |
| System.arraycopy(precomputedDigest, 0, result, 0, offset); |
| offset = 0; |
| |
| return result; |
| } |
| } |
| |
| public static final class SHA1 extends RSASignature { |
| public SHA1() { |
| super("SHA1"); |
| } |
| } |
| |
| public static final class SHA256 extends RSASignature { |
| public SHA256() { |
| super("SHA-256"); |
| } |
| } |
| |
| public static final class SHA384 extends RSASignature { |
| public SHA384() { |
| super("SHA-384"); |
| } |
| } |
| |
| public static final class SHA512 extends RSASignature { |
| public SHA512() { |
| super("SHA-512"); |
| } |
| } |
| |
| public static final class MD5 extends RSASignature { |
| public MD5() { |
| super("MD5"); |
| } |
| } |
| |
| public static final class MD2 extends RSASignature { |
| public MD2() { |
| super("MD2"); |
| } |
| } |
| |
| // initialize for signing. See JCA doc |
| protected void engineInitVerify(PublicKey key) |
| throws InvalidKeyException |
| { |
| // This signature accepts only RSAPublicKey |
| if ((key instanceof java.security.interfaces.RSAPublicKey) == false) { |
| throw new InvalidKeyException("Key type not supported"); |
| } |
| |
| java.security.interfaces.RSAPublicKey rsaKey = |
| (java.security.interfaces.RSAPublicKey) key; |
| |
| if ((key instanceof sun.security.mscapi.RSAPublicKey) == false) { |
| |
| // convert key to MSCAPI format |
| |
| BigInteger modulus = rsaKey.getModulus(); |
| BigInteger exponent = rsaKey.getPublicExponent(); |
| |
| // Check against the local and global values to make sure |
| // the sizes are ok. Round up to the nearest byte. |
| RSAKeyFactory.checkKeyLengths(((modulus.bitLength() + 7) & ~7), |
| exponent, -1, RSAKeyPairGenerator.KEY_SIZE_MAX); |
| |
| byte[] modulusBytes = modulus.toByteArray(); |
| byte[] exponentBytes = exponent.toByteArray(); |
| |
| // Adjust key length due to sign bit |
| int keyBitLength = (modulusBytes[0] == 0) |
| ? (modulusBytes.length - 1) * 8 |
| : modulusBytes.length * 8; |
| |
| byte[] keyBlob = generatePublicKeyBlob( |
| keyBitLength, modulusBytes, exponentBytes); |
| |
| try { |
| publicKey = importPublicKey(keyBlob, keyBitLength); |
| |
| } catch (KeyStoreException e) { |
| throw new InvalidKeyException(e); |
| } |
| |
| } else { |
| publicKey = (sun.security.mscapi.RSAPublicKey) key; |
| } |
| |
| this.privateKey = null; |
| resetDigest(); |
| } |
| |
| // initialize for signing. See JCA doc |
| protected void engineInitSign(PrivateKey key) throws InvalidKeyException |
| { |
| // This signature accepts only RSAPrivateKey |
| if ((key instanceof sun.security.mscapi.RSAPrivateKey) == false) { |
| throw new InvalidKeyException("Key type not supported"); |
| } |
| privateKey = (sun.security.mscapi.RSAPrivateKey) key; |
| |
| // Check against the local and global values to make sure |
| // the sizes are ok. Round up to nearest byte. |
| RSAKeyFactory.checkKeyLengths(((privateKey.length() + 7) & ~7), |
| null, RSAKeyPairGenerator.KEY_SIZE_MIN, |
| RSAKeyPairGenerator.KEY_SIZE_MAX); |
| |
| this.publicKey = null; |
| resetDigest(); |
| } |
| |
| /** |
| * Resets the message digest if needed. |
| */ |
| protected void resetDigest() { |
| if (needsReset) { |
| messageDigest.reset(); |
| needsReset = false; |
| } |
| } |
| |
| protected byte[] getDigestValue() throws SignatureException { |
| needsReset = false; |
| return messageDigest.digest(); |
| } |
| |
| protected void setDigestName(String name) { |
| messageDigestAlgorithm = name; |
| } |
| |
| /** |
| * Updates the data to be signed or verified |
| * using the specified byte. |
| * |
| * @param b the byte to use for the update. |
| * |
| * @exception SignatureException if the engine is not initialized |
| * properly. |
| */ |
| protected void engineUpdate(byte b) throws SignatureException |
| { |
| messageDigest.update(b); |
| needsReset = true; |
| } |
| |
| /** |
| * Updates the data to be signed or verified, using the |
| * specified array of bytes, starting at the specified offset. |
| * |
| * @param b the array of bytes |
| * @param off the offset to start from in the array of bytes |
| * @param len the number of bytes to use, starting at offset |
| * |
| * @exception SignatureException if the engine is not initialized |
| * properly |
| */ |
| protected void engineUpdate(byte[] b, int off, int len) |
| throws SignatureException |
| { |
| messageDigest.update(b, off, len); |
| needsReset = true; |
| } |
| |
| /** |
| * Updates the data to be signed or verified, using the |
| * specified ByteBuffer. |
| * |
| * @param input the ByteBuffer |
| */ |
| protected void engineUpdate(ByteBuffer input) |
| { |
| messageDigest.update(input); |
| needsReset = true; |
| } |
| |
| /** |
| * Returns the signature bytes of all the data |
| * updated so far. |
| * The format of the signature depends on the underlying |
| * signature scheme. |
| * |
| * @return the signature bytes of the signing operation's result. |
| * |
| * @exception SignatureException if the engine is not |
| * initialized properly or if this signature algorithm is unable to |
| * process the input data provided. |
| */ |
| protected byte[] engineSign() throws SignatureException { |
| |
| byte[] hash = getDigestValue(); |
| |
| // Omit the hash OID when generating a Raw signature |
| boolean noHashOID = this instanceof Raw; |
| |
| // Sign hash using MS Crypto APIs |
| |
| byte[] result = signHash(noHashOID, hash, hash.length, |
| messageDigestAlgorithm, privateKey.getHCryptProvider(), |
| privateKey.getHCryptKey()); |
| |
| // Convert signature array from little endian to big endian |
| return convertEndianArray(result); |
| } |
| |
| /** |
| * Convert array from big endian to little endian, or vice versa. |
| */ |
| private byte[] convertEndianArray(byte[] byteArray) |
| { |
| if (byteArray == null || byteArray.length == 0) |
| return byteArray; |
| |
| byte [] retval = new byte[byteArray.length]; |
| |
| // make it big endian |
| for (int i=0;i < byteArray.length;i++) |
| retval[i] = byteArray[byteArray.length - i - 1]; |
| |
| return retval; |
| } |
| |
| /** |
| * Sign hash using Microsoft Crypto API with HCRYPTKEY. |
| * The returned data is in little-endian. |
| */ |
| private native static byte[] signHash(boolean noHashOID, byte[] hash, |
| int hashSize, String hashAlgorithm, long hCryptProv, long hCryptKey) |
| throws SignatureException; |
| |
| /** |
| * Verify a signed hash using Microsoft Crypto API with HCRYPTKEY. |
| */ |
| private native static boolean verifySignedHash(byte[] hash, int hashSize, |
| String hashAlgorithm, byte[] signature, int signatureSize, |
| long hCryptProv, long hCryptKey) throws SignatureException; |
| |
| /** |
| * Verifies the passed-in signature. |
| * |
| * @param sigBytes the signature bytes to be verified. |
| * |
| * @return true if the signature was verified, false if not. |
| * |
| * @exception SignatureException if the engine is not |
| * initialized properly, the passed-in signature is improperly |
| * encoded or of the wrong type, if this signature algorithm is unable to |
| * process the input data provided, etc. |
| */ |
| protected boolean engineVerify(byte[] sigBytes) |
| throws SignatureException |
| { |
| byte[] hash = getDigestValue(); |
| |
| return verifySignedHash(hash, hash.length, |
| messageDigestAlgorithm, convertEndianArray(sigBytes), |
| sigBytes.length, publicKey.getHCryptProvider(), |
| publicKey.getHCryptKey()); |
| } |
| |
| /** |
| * Sets the specified algorithm parameter to the specified |
| * value. This method supplies a general-purpose mechanism through |
| * which it is possible to set the various parameters of this object. |
| * A parameter may be any settable parameter for the algorithm, such as |
| * a parameter size, or a source of random bits for signature generation |
| * (if appropriate), or an indication of whether or not to perform |
| * a specific but optional computation. A uniform algorithm-specific |
| * naming scheme for each parameter is desirable but left unspecified |
| * at this time. |
| * |
| * @param param the string identifier of the parameter. |
| * |
| * @param value the parameter value. |
| * |
| * @exception InvalidParameterException if <code>param</code> is an |
| * invalid parameter for this signature algorithm engine, |
| * the parameter is already set |
| * and cannot be set again, a security exception occurs, and so on. |
| * |
| * @deprecated Replaced by {@link |
| * #engineSetParameter(java.security.spec.AlgorithmParameterSpec) |
| * engineSetParameter}. |
| */ |
| @Deprecated |
| protected void engineSetParameter(String param, Object value) |
| throws InvalidParameterException |
| { |
| throw new InvalidParameterException("Parameter not supported"); |
| } |
| |
| |
| /** |
| * Gets the value of the specified algorithm parameter. |
| * This method supplies a general-purpose mechanism through which it |
| * is possible to get the various parameters of this object. A parameter |
| * may be any settable parameter for the algorithm, such as a parameter |
| * size, or a source of random bits for signature generation (if |
| * appropriate), or an indication of whether or not to perform a |
| * specific but optional computation. A uniform algorithm-specific |
| * naming scheme for each parameter is desirable but left unspecified |
| * at this time. |
| * |
| * @param param the string name of the parameter. |
| * |
| * @return the object that represents the parameter value, or null if |
| * there is none. |
| * |
| * @exception InvalidParameterException if <code>param</code> is an |
| * invalid parameter for this engine, or another exception occurs while |
| * trying to get this parameter. |
| * |
| * @deprecated |
| */ |
| @Deprecated |
| protected Object engineGetParameter(String param) |
| throws InvalidParameterException |
| { |
| throw new InvalidParameterException("Parameter not supported"); |
| } |
| |
| /** |
| * Generates a public-key BLOB from a key's components. |
| */ |
| // used by RSACipher |
| static native byte[] generatePublicKeyBlob( |
| int keyBitLength, byte[] modulus, byte[] publicExponent) |
| throws InvalidKeyException; |
| |
| /** |
| * Imports a public-key BLOB. |
| */ |
| // used by RSACipher |
| static native RSAPublicKey importPublicKey(byte[] keyBlob, int keySize) |
| throws KeyStoreException; |
| } |