alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (RSASignature.java)

This example Java source code file (RSASignature.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

biginteger, invalidkeyexception, invalidparameterexception, math, md2, md5, message, nio, override, raw_rsa_max, rsasignature, security, sha1, sha512, sha\-512, signatureexception, string

The RSASignature.java Java example source code

/*
 * Copyright (c) 2005, 2012, 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 (offset + len > precomputedDigest.length) {
                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 (offset + len > precomputedDigest.length) {
                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 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 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;
}

Other Java examples (source code examples)

Here is a short list of links related to this Java RSASignature.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.