--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/provider/DSA.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,678 @@
+/*
+ * Copyright 1996-2004 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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;
+ c = ((b<<30) | (b>>>(32-30)));
+ b = a;
+ a = temp;
+ }
+
+ // Round 2
+ for (int i = 20; i < 40; i++) {
+ temp = ((a<<5) | (a>>>(32-5))) +
+ (b ^ c ^ d) + e + W[i] + round2_kt;
+ e = d;
+ d = c;
+ c = ((b<<30) | (b>>>(32-30)));
+ b = a;
+ a = temp;
+ }
+
+ // Round 3
+ for (int i = 40; i < 60; i++) {
+ temp = ((a<<5) | (a>>>(32-5))) +
+ ((b&c)|(b&d)|(c&d)) + e + W[i] + round3_kt;
+ e = d;
+ d = c;
+ c = ((b<<30) | (b>>>(32-30)));
+ b = a;
+ a = temp;
+ }
+
+ // Round 4
+ for (int i = 60; i < 80; i++) {
+ temp = ((a<<5) | (a>>>(32-5))) +
+ (b ^ c ^ d) + e + W[i] + round4_kt;
+ e = d;
+ d = c;
+ c = ((b<<30) | (b>>>(32-30)));
+ b = a;
+ a = temp;
+ }
+ int[] md = new int[5];
+ md[0] = h[0] + a;
+ md[1] = h[1] + b;
+ md[2] = h[2] + c;
+ md[3] = h[3] + d;
+ md[4] = h[4] + e;
+ return md;
+ }
+
+
+ /**
+ * This implementation recognizes the following parameter:<dl>
+ *
+ * <dt><tt>Kseed</tt>
+ *
+ * <dd>a byte array.
+ *
+ * </dl>
+ *
+ * @deprecated
+ */
+ @Deprecated
+ protected void engineSetParameter(String key, Object param) {
+ if (key.equals("KSEED")) {
+ if (param instanceof byte[]) {
+ Kseed = byteArray2IntArray((byte[])param);
+ KseedAsByteArray = (byte[])param;
+ } else {
+ debug("unrecognized param: " + key);
+ throw new InvalidParameterException("Kseed not a byte array");
+ }
+ } else {
+ throw new InvalidParameterException("invalid parameter");
+ }
+ }
+
+ /**
+ * Return the value of the requested parameter. Recognized
+ * parameters are:
+ *
+ * <dl>
+ *
+ * <dt><tt>Kseed</tt>
+ *
+ * <dd>a byte array.
+ *
+ * </dl>
+ *
+ * @return the value of the requested parameter.
+ *
+ * @see java.security.SignatureEngine
+ *
+ * @deprecated
+ */
+ @Deprecated
+ protected Object engineGetParameter(String key) {
+ if (key.equals("KSEED")) {
+ return KseedAsByteArray;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the algorithm object.
+ */
+ private void setParams(DSAParams params) throws InvalidKeyException {
+ if (params == null) {
+ throw new InvalidKeyException("DSA public key lacks parameters");
+ }
+ this.params = params;
+ this.presetP = params.getP();
+ this.presetQ = params.getQ();
+ this.presetG = params.getG();
+ }
+
+ /**
+ * Return a human readable rendition of the engine.
+ */
+ public String toString() {
+ String printable = "DSA Signature";
+ if (presetP != null && presetQ != null && presetG != null) {
+ printable += "\n\tp: " + Debug.toHexString(presetP);
+ printable += "\n\tq: " + Debug.toHexString(presetQ);
+ printable += "\n\tg: " + Debug.toHexString(presetG);
+ } else {
+ printable += "\n\t P, Q or G not initialized.";
+ }
+ if (presetY != null) {
+ printable += "\n\ty: " + Debug.toHexString(presetY);
+ }
+ if (presetY == null && presetX == null) {
+ printable += "\n\tUNINIIALIZED";
+ }
+ return printable;
+ }
+
+ /*
+ * Utility routine for converting a byte array into an int array
+ */
+ private int[] byteArray2IntArray(byte[] byteArray) {
+
+ int j = 0;
+ byte[] newBA;
+ int mod = byteArray.length % 4;
+
+ // guarantee that the incoming byteArray is a multiple of 4
+ // (pad with 0's)
+ switch (mod) {
+ case 3: newBA = new byte[byteArray.length + 1]; break;
+ case 2: newBA = new byte[byteArray.length + 2]; break;
+ case 1: newBA = new byte[byteArray.length + 3]; break;
+ default: newBA = new byte[byteArray.length + 0]; break;
+ }
+ System.arraycopy(byteArray, 0, newBA, 0, byteArray.length);
+
+ // copy each set of 4 bytes in the byte array into an integer
+ int[] newSeed = new int[newBA.length / 4];
+ for (int i = 0; i < newBA.length; i += 4) {
+ newSeed[j] = newBA[i + 3] & 0xFF;
+ newSeed[j] |= (newBA[i + 2] << 8) & 0xFF00;
+ newSeed[j] |= (newBA[i + 1] << 16) & 0xFF0000;
+ newSeed[j] |= (newBA[i + 0] << 24) & 0xFF000000;
+ j++;
+ }
+
+ return newSeed;
+ }
+
+ private static void debug(Exception e) {
+ if (debug) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void debug(String s) {
+ if (debug) {
+ System.err.println(s);
+ }
+ }
+}