/* * Copyright (c) 2003, 2013, 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.rsa; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.security.*; import java.security.interfaces.*; import sun.security.util.*; import sun.security.x509.AlgorithmId; /** * PKCS#1 RSA signatures with the various message digest algorithms. * This file contains an abstract base class with all the logic plus * a nested static class for each of the message digest algorithms * (see end of the file). We support MD2, MD5, SHA-1, SHA-224, SHA-256, * SHA-384, and SHA-512. * * @since 1.5 * @author Andreas Sterbenz */ public abstract class RSASignature extends SignatureSpi { // we sign an ASN.1 SEQUENCE of AlgorithmId and digest // it has the form 30:xx:30:xx:[digestOID]:05:00:04:xx:[digest] // this means the encoded length is (8 + digestOID.length + digest.length) private static final int baseLength = 8; // object identifier for the message digest algorithm used private final ObjectIdentifier digestOID; // length of the encoded signature blob private final int encodedLength; // message digest implementation we use private final MessageDigest md; // flag indicating whether the digest is reset private boolean digestReset; // private key, if initialized for signing private RSAPrivateKey privateKey; // public key, if initialized for verifying private RSAPublicKey publicKey; // padding to use, set when the initSign/initVerify is called private RSAPadding padding; /** * Construct a new RSASignature. Used by subclasses. */ RSASignature(String algorithm, ObjectIdentifier digestOID, int oidLength) { this.digestOID = digestOID; try { md = MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); } digestReset = true; encodedLength = baseLength + oidLength + md.getDigestLength(); } // initialize for verification. See JCA doc protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { RSAPublicKey rsaKey = (RSAPublicKey)RSAKeyFactory.toRSAKey(publicKey); this.privateKey = null; this.publicKey = rsaKey; initCommon(rsaKey, null); } // initialize for signing. See JCA doc protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { engineInitSign(privateKey, null); } // initialize for signing. See JCA doc protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { RSAPrivateKey rsaKey = (RSAPrivateKey)RSAKeyFactory.toRSAKey(privateKey); this.privateKey = rsaKey; this.publicKey = null; initCommon(rsaKey, random); } /** * Init code common to sign and verify. */ private void initCommon(RSAKey rsaKey, SecureRandom random) throws InvalidKeyException { resetDigest(); int keySize = RSACore.getByteLength(rsaKey); try { padding = RSAPadding.getInstance (RSAPadding.PAD_BLOCKTYPE_1, keySize, random); } catch (InvalidAlgorithmParameterException iape) { throw new InvalidKeyException(iape.getMessage()); } int maxDataSize = padding.getMaxDataSize(); if (encodedLength > maxDataSize) { throw new InvalidKeyException ("Key is too short for this signature algorithm"); } } /** * Reset the message digest if it is not already reset. */ private void resetDigest() { if (digestReset == false) { md.reset(); digestReset = true; } } /** * Return the message digest value. */ private byte[] getDigestValue() { digestReset = true; return md.digest(); } // update the signature with the plaintext data. See JCA doc protected void engineUpdate(byte b) throws SignatureException { md.update(b); digestReset = false; } // update the signature with the plaintext data. See JCA doc protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { md.update(b, off, len); digestReset = false; } // update the signature with the plaintext data. See JCA doc protected void engineUpdate(ByteBuffer b) { md.update(b); digestReset = false; } // sign the data and return the signature. See JCA doc protected byte[] engineSign() throws SignatureException { byte[] digest = getDigestValue(); try { byte[] encoded = encodeSignature(digestOID, digest); byte[] padded = padding.pad(encoded); byte[] encrypted = RSACore.rsa(padded, privateKey); return encrypted; } catch (GeneralSecurityException e) { throw new SignatureException("Could not sign data", e); } catch (IOException e) { throw new SignatureException("Could not encode data", e); } } // verify the data and return the result. See JCA doc protected boolean engineVerify(byte[] sigBytes) throws SignatureException { if (sigBytes.length != RSACore.getByteLength(publicKey)) { throw new SignatureException("Signature length not correct: got " + sigBytes.length + " but was expecting " + RSACore.getByteLength(publicKey)); } byte[] digest = getDigestValue(); try { byte[] decrypted = RSACore.rsa(sigBytes, publicKey); byte[] unpadded = padding.unpad(decrypted); byte[] decodedDigest = decodeSignature(digestOID, unpadded); return Arrays.equals(digest, decodedDigest); } catch (javax.crypto.BadPaddingException e) { // occurs if the app has used the wrong RSA public key // or if sigBytes is invalid // return false rather than propagating the exception for // compatibility/ease of use return false; } catch (IOException e) { throw new SignatureException("Signature encoding error", e); } } /** * Encode the digest, return the to-be-signed data. * Also used by the PKCS#11 provider. */ public static byte[] encodeSignature(ObjectIdentifier oid, byte[] digest) throws IOException { DerOutputStream out = new DerOutputStream(); new AlgorithmId(oid).encode(out); out.putOctetString(digest); DerValue result = new DerValue(DerValue.tag_Sequence, out.toByteArray()); return result.toByteArray(); } /** * Decode the signature data. Verify that the object identifier matches * and return the message digest. */ public static byte[] decodeSignature(ObjectIdentifier oid, byte[] signature) throws IOException { DerInputStream in = new DerInputStream(signature); DerValue[] values = in.getSequence(2); if ((values.length != 2) || (in.available() != 0)) { throw new IOException("SEQUENCE length error"); } AlgorithmId algId = AlgorithmId.parse(values[0]); if (algId.getOID().equals((Object)oid) == false) { throw new IOException("ObjectIdentifier mismatch: " + algId.getOID()); } if (algId.getEncodedParams() != null) { throw new IOException("Unexpected AlgorithmId parameters"); } byte[] digest = values[1].getOctetString(); return digest; } // set parameter, not supported. See JCA doc @Deprecated protected void engineSetParameter(String param, Object value) throws InvalidParameterException { throw new UnsupportedOperationException("setParameter() not supported"); } // get parameter, not supported. See JCA doc @Deprecated protected Object engineGetParameter(String param) throws InvalidParameterException { throw new UnsupportedOperationException("getParameter() not supported"); } // Nested class for MD2withRSA signatures public static final class MD2withRSA extends RSASignature { public MD2withRSA() { super("MD2", AlgorithmId.MD2_oid, 10); } } // Nested class for MD5withRSA signatures public static final class MD5withRSA extends RSASignature { public MD5withRSA() { super("MD5", AlgorithmId.MD5_oid, 10); } } // Nested class for SHA1withRSA signatures public static final class SHA1withRSA extends RSASignature { public SHA1withRSA() { super("SHA-1", AlgorithmId.SHA_oid, 7); } } // Nested class for SHA224withRSA signatures public static final class SHA224withRSA extends RSASignature { public SHA224withRSA() { super("SHA-224", AlgorithmId.SHA224_oid, 11); } } // Nested class for SHA256withRSA signatures public static final class SHA256withRSA extends RSASignature { public SHA256withRSA() { super("SHA-256", AlgorithmId.SHA256_oid, 11); } } // Nested class for SHA384withRSA signatures public static final class SHA384withRSA extends RSASignature { public SHA384withRSA() { super("SHA-384", AlgorithmId.SHA384_oid, 11); } } // Nested class for SHA512withRSA signatures public static final class SHA512withRSA extends RSASignature { public SHA512withRSA() { super("SHA-512", AlgorithmId.SHA512_oid, 11); } } }