678 lines · 20.5 KB · Java
RawDownload
/* * Copyright (c) 1996, 2004, 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.provider; import java.io.*;import java.util.*;import java.math.BigInteger;import java.nio.ByteBuffer; import java.security.*;import java.security.SecureRandom;import java.security.interfaces.*;import java.security.spec.DSAParameterSpec;import java.security.spec.InvalidParameterSpecException; import sun.security.util.Debug;import sun.security.util.DerValue;import sun.security.util.DerInputStream;import sun.security.util.DerOutputStream;import sun.security.x509.AlgIdDSA;import sun.security.jca.JCAUtil; /** * The Digital Signature Standard (using the Digital Signature * Algorithm), as described in fips186 of the National Instute of * Standards and Technology (NIST), using fips180-1 (SHA-1). * * This file contains both the signature implementation for the * commonly used SHA1withDSA (DSS) as well as RawDSA, used by TLS * among others. RawDSA expects the 20 byte SHA-1 digest as input * via update rather than the original data like other signature * implementations. * * @author Benjamin Renaud * * @since   1.1 * * @see DSAPublicKey * @see DSAPrivateKey */abstract class DSA extends SignatureSpi {     /* Are we debugging? */    private static final boolean debug = false;     /* The parameter object */    private DSAParams params;     /* algorithm parameters */    private BigInteger presetP, presetQ, presetG;     /* The public key, if any */    private BigInteger presetY;     /* The private key, if any */    private BigInteger presetX;     /* The random seed used to generate k */    private int[] Kseed;     /* The random seed used to generate k (specified by application) */    private byte[] KseedAsByteArray;     /*     * The random seed used to generate k     * (prevent the same Kseed from being used twice in a row     */    private int[] previousKseed;     /* The RNG used to output a seed for generating k */    private SecureRandom signingRandom;     /**     * Construct a blank DSA object. It must be     * initialized before being usable for signing or verifying.     */    DSA() {        super();    }     /**     * Return the 20 byte hash value and reset the digest.     */    abstract byte[] getDigest() throws SignatureException;     /**     * Reset the digest.     */    abstract void resetDigest();     /**     * Standard SHA1withDSA implementation.     */    public static final class SHA1withDSA extends DSA {         /* The SHA hash for the data */        private final MessageDigest dataSHA;         public SHA1withDSA() throws NoSuchAlgorithmException {            dataSHA = MessageDigest.getInstance("SHA-1");        }         /**         * Update a byte to be signed or verified.         */        protected void engineUpdate(byte b) {            dataSHA.update(b);        }         /**         * Update an array of bytes to be signed or verified.         */        protected void engineUpdate(byte[] data, int off, int len) {            dataSHA.update(data, off, len);        }         protected void engineUpdate(ByteBuffer b) {            dataSHA.update(b);        }         byte[] getDigest() {            return dataSHA.digest();        }         void resetDigest() {            dataSHA.reset();        }    }     /**     * RawDSA implementation.     *     * RawDSA requires the data to be exactly 20 bytes long. If it is     * not, a SignatureException is thrown when sign()/verify() is called     * per JCA spec.     */    public static final class RawDSA extends DSA {         // length of the SHA-1 digest (20 bytes)        private final static int SHA1_LEN = 20;         // 20 byte digest buffer        private final byte[] digestBuffer;         // offset into the buffer        private int ofs;         public RawDSA() {            digestBuffer = new byte[SHA1_LEN];        }         protected void engineUpdate(byte b) {            if (ofs == SHA1_LEN) {                ofs = SHA1_LEN + 1;                return;            }            digestBuffer[ofs++] = b;        }         protected void engineUpdate(byte[] data, int off, int len) {            if (ofs + len > SHA1_LEN) {                ofs = SHA1_LEN + 1;                return;            }            System.arraycopy(data, off, digestBuffer, ofs, len);            ofs += len;        }         byte[] getDigest() throws SignatureException {            if (ofs != SHA1_LEN) {                throw new SignatureException                        ("Data for RawDSA must be exactly 20 bytes long");            }            ofs = 0;            return digestBuffer;        }         void resetDigest() {            ofs = 0;        }    }     /**     * Initialize the DSA object with a DSA private key.     *     * @param privateKey the DSA private key     *     * @exception InvalidKeyException if the key is not a valid DSA private     * key.     */    protected void engineInitSign(PrivateKey privateKey)            throws InvalidKeyException {        if (!(privateKey instanceof java.security.interfaces.DSAPrivateKey)) {            throw new InvalidKeyException("not a DSA private key: " +                                          privateKey);        }        java.security.interfaces.DSAPrivateKey priv =            (java.security.interfaces.DSAPrivateKey)privateKey;        this.presetX = priv.getX();        this.presetY = null;        initialize(priv.getParams());    }     /**     * Initialize the DSA object with a DSA public key.     *     * @param publicKey the DSA public key.     *     * @exception InvalidKeyException if the key is not a valid DSA public     * key.     */    protected void engineInitVerify(PublicKey publicKey)            throws InvalidKeyException {        if (!(publicKey instanceof java.security.interfaces.DSAPublicKey)) {            throw new InvalidKeyException("not a DSA public key: " +                                          publicKey);        }        java.security.interfaces.DSAPublicKey pub =            (java.security.interfaces.DSAPublicKey)publicKey;        this.presetY = pub.getY();        this.presetX = null;        initialize(pub.getParams());    }     private void initialize(DSAParams params) throws InvalidKeyException {        resetDigest();        setParams(params);    }     /**     * Sign all the data thus far updated. The signature is formatted     * according to the Canonical Encoding Rules, returned as a DER     * sequence of Integer, r and s.     *     * @return a signature block formatted according to the Canonical     * Encoding Rules.     *     * @exception SignatureException if the signature object was not     * properly initialized, or if another exception occurs.     *     * @see sun.security.DSA#engineUpdate     * @see sun.security.DSA#engineVerify     */    protected byte[] engineSign() throws SignatureException {        BigInteger k = generateK(presetQ);        BigInteger r = generateR(presetP, presetQ, presetG, k);        BigInteger s = generateS(presetX, presetQ, r, k);         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 (IOException e) {            throw new SignatureException("error encoding signature");        }    }     /**     * Verify all the data thus far updated.     *     * @param signature the alledged signature, encoded using the     * Canonical Encoding Rules, as a sequence of integers, r and s.     *     * @exception SignatureException if the signature object was not     * properly initialized, or if another exception occurs.     *     * @see sun.security.DSA#engineUpdate     * @see sun.security.DSA#engineSign     */    protected boolean engineVerify(byte[] signature)            throws SignatureException {        return engineVerify(signature, 0, signature.length);    }     /**     * Verify all the data thus far updated.     *     * @param signature the alledged signature, encoded using the     * Canonical Encoding Rules, as a sequence of integers, r and s.     *     * @param offset the offset to start from in the array of bytes.     *     * @param length the number of bytes to use, starting at offset.     *     * @exception SignatureException if the signature object was not     * properly initialized, or if another exception occurs.     *     * @see sun.security.DSA#engineUpdate     * @see sun.security.DSA#engineSign     */    protected boolean engineVerify(byte[] signature, int offset, int length)            throws SignatureException {         BigInteger r = null;        BigInteger s = null;        // first decode the signature.        try {            DerInputStream in = new DerInputStream(signature, offset, length);            DerValue[] values = in.getSequence(2);             r = values[0].getBigInteger();            s = values[1].getBigInteger();         } catch (IOException e) {            throw new SignatureException("invalid encoding for signature");        }         // some implementations do not correctly encode values in the ASN.1        // 2's complement format. force r and s to be positive in order to        // to validate those signatures        if (r.signum() < 0) {            r = new BigInteger(1, r.toByteArray());        }        if (s.signum() < 0) {            s = new BigInteger(1, s.toByteArray());        }         if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) {            BigInteger w = generateW(presetP, presetQ, presetG, s);            BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r);            return v.equals(r);        } else {            throw new SignatureException("invalid signature: out of range values");        }    }     private BigInteger generateR(BigInteger p, BigInteger q, BigInteger g,                         BigInteger k) {        BigInteger temp = g.modPow(k, p);        return temp.remainder(q);   }     private BigInteger generateS(BigInteger x, BigInteger q,            BigInteger r, BigInteger k) throws SignatureException {         byte[] s2 = getDigest();        BigInteger temp = new BigInteger(1, s2);        BigInteger k1 = k.modInverse(q);         BigInteger s = x.multiply(r);        s = temp.add(s);        s = k1.multiply(s);        return s.remainder(q);    }     private BigInteger generateW(BigInteger p, BigInteger q,                         BigInteger g, BigInteger s) {        return s.modInverse(q);    }     private BigInteger generateV(BigInteger y, BigInteger p,             BigInteger q, BigInteger g, BigInteger w, BigInteger r)             throws SignatureException {         byte[] s2 = getDigest();        BigInteger temp = new BigInteger(1, s2);         temp = temp.multiply(w);        BigInteger u1 = temp.remainder(q);         BigInteger u2 = (r.multiply(w)).remainder(q);         BigInteger t1 = g.modPow(u1,p);        BigInteger t2 = y.modPow(u2,p);        BigInteger t3 = t1.multiply(t2);        BigInteger t5 = t3.remainder(p);        return t5.remainder(q);    }     /*     * Please read bug report 4044247 for an alternative, faster,     * NON-FIPS approved method to generate K     */    private BigInteger generateK(BigInteger q) {         BigInteger k = null;         // The application specified a Kseed for us to use.        // Note that we do not allow usage of the same Kseed twice in a row        if (Kseed != null && !Arrays.equals(Kseed, previousKseed)) {            k = generateK(Kseed, q);            if (k.signum() > 0 && k.compareTo(q) < 0) {                previousKseed = new int [Kseed.length];                System.arraycopy(Kseed, 0, previousKseed, 0, Kseed.length);                return k;            }        }         // The application did not specify a Kseed for us to use.        // We'll generate a new Kseed by getting random bytes from        // a SecureRandom object.        SecureRandom random = getSigningRandom();         while (true) {            int[] seed = new int[5];             for (int i = 0; i < 5; i++)                seed[i] = random.nextInt();            k = generateK(seed, q);            if (k.signum() > 0 && k.compareTo(q) < 0) {                previousKseed = new int [seed.length];                System.arraycopy(seed, 0, previousKseed, 0, seed.length);                return k;            }        }    }     // Use the application-specified SecureRandom Object if provided.    // Otherwise, use our default SecureRandom Object.    private SecureRandom getSigningRandom() {        if (signingRandom == null) {            if (appRandom != null) {                signingRandom = appRandom;            } else {                signingRandom = JCAUtil.getSecureRandom();            }        }        return signingRandom;    }     /**     * Compute k for a DSA signature.     *     * @param seed the seed for generating k. This seed should be     * secure. This is what is refered to as the KSEED in the DSA     * specification.     *     * @param g the g parameter from the DSA key pair.     */    private BigInteger generateK(int[] seed, BigInteger q) {         // check out t in the spec.        int[] t = { 0xEFCDAB89, 0x98BADCFE, 0x10325476,                    0xC3D2E1F0, 0x67452301 };        //        int[] tmp = DSA.SHA_7(seed, t);        byte[] tmpBytes = new byte[tmp.length * 4];        for (int i = 0; i < tmp.length; i++) {            int k = tmp[i];            for (int j = 0; j < 4; j++) {                tmpBytes[(i * 4) + j] = (byte) (k >>> (24 - (j * 8)));            }        }        BigInteger k = new BigInteger(1, tmpBytes).mod(q);        return k;    }    // Constants for each round    private static final int round1_kt = 0x5a827999;    private static final int round2_kt = 0x6ed9eba1;    private static final int round3_kt = 0x8f1bbcdc;    private static final int round4_kt = 0xca62c1d6;    /**    * Computes set 1 thru 7 of SHA-1 on m1. */   static int[] SHA_7(int[] m1, int[] h) {        int[] W = new int[80];       System.arraycopy(m1,0,W,0,m1.length);       int temp = 0;         for (int t = 16; t <= 79; t++){            temp = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16];            W[t] = ((temp << 1) | (temp >>>(32 - 1)));        }        int a = h[0],b = h[1],c = h[2], d = h[3], e = h[4];       for (int i = 0; i < 20; i++) {            temp = ((a<<5) | (a>>>(32-5))) +                ((b&c)|((~b)&d))+ e + W[i] + round1_kt;            e = d;            d = c;

Lines 1–500 of 678.