| package org.bouncycastle.crypto.signers; |
| |
| import java.math.BigInteger; |
| import java.security.SecureRandom; |
| |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.DSA; |
| import org.bouncycastle.crypto.params.DSAKeyParameters; |
| import org.bouncycastle.crypto.params.DSAParameters; |
| import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; |
| import org.bouncycastle.crypto.params.DSAPublicKeyParameters; |
| import org.bouncycastle.crypto.params.ParametersWithRandom; |
| |
| /** |
| * The Digital Signature Algorithm - as described in "Handbook of Applied |
| * Cryptography", pages 452 - 453. |
| */ |
| public class DSASigner |
| implements DSA |
| { |
| private final DSAKCalculator kCalculator; |
| |
| private DSAKeyParameters key; |
| private SecureRandom random; |
| |
| /** |
| * Default configuration, random K values. |
| */ |
| public DSASigner() |
| { |
| this.kCalculator = new RandomDSAKCalculator(); |
| } |
| |
| /** |
| * Configuration with an alternate, possibly deterministic calculator of K. |
| * |
| * @param kCalculator a K value calculator. |
| */ |
| public DSASigner(DSAKCalculator kCalculator) |
| { |
| this.kCalculator = kCalculator; |
| } |
| |
| public void init( |
| boolean forSigning, |
| CipherParameters param) |
| { |
| SecureRandom providedRandom = null; |
| |
| if (forSigning) |
| { |
| if (param instanceof ParametersWithRandom) |
| { |
| ParametersWithRandom rParam = (ParametersWithRandom)param; |
| |
| this.key = (DSAPrivateKeyParameters)rParam.getParameters(); |
| providedRandom = rParam.getRandom(); |
| } |
| else |
| { |
| this.key = (DSAPrivateKeyParameters)param; |
| } |
| } |
| else |
| { |
| this.key = (DSAPublicKeyParameters)param; |
| } |
| |
| this.random = initSecureRandom(forSigning && !kCalculator.isDeterministic(), providedRandom); |
| } |
| |
| /** |
| * generate a signature for the given message using the key we were |
| * initialised with. For conventional DSA the message should be a SHA-1 |
| * hash of the message of interest. |
| * |
| * @param message the message that will be verified later. |
| */ |
| public BigInteger[] generateSignature( |
| byte[] message) |
| { |
| DSAParameters params = key.getParameters(); |
| BigInteger q = params.getQ(); |
| BigInteger m = calculateE(q, message); |
| BigInteger x = ((DSAPrivateKeyParameters)key).getX(); |
| |
| if (kCalculator.isDeterministic()) |
| { |
| kCalculator.init(q, x, message); |
| } |
| else |
| { |
| kCalculator.init(q, random); |
| } |
| |
| BigInteger k = kCalculator.nextK(); |
| |
| // the randomizer is to conceal timing information related to k and x. |
| BigInteger r = params.getG().modPow(k.add(getRandomizer(q, random)), params.getP()).mod(q); |
| |
| k = k.modInverse(q).multiply(m.add(x.multiply(r))); |
| |
| BigInteger s = k.mod(q); |
| |
| return new BigInteger[]{ r, s }; |
| } |
| |
| /** |
| * return true if the value r and s represent a DSA signature for |
| * the passed in message for standard DSA the message should be a |
| * SHA-1 hash of the real message to be verified. |
| */ |
| public boolean verifySignature( |
| byte[] message, |
| BigInteger r, |
| BigInteger s) |
| { |
| DSAParameters params = key.getParameters(); |
| BigInteger q = params.getQ(); |
| BigInteger m = calculateE(q, message); |
| BigInteger zero = BigInteger.valueOf(0); |
| |
| if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0) |
| { |
| return false; |
| } |
| |
| if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0) |
| { |
| return false; |
| } |
| |
| BigInteger w = s.modInverse(q); |
| |
| BigInteger u1 = m.multiply(w).mod(q); |
| BigInteger u2 = r.multiply(w).mod(q); |
| |
| BigInteger p = params.getP(); |
| u1 = params.getG().modPow(u1, p); |
| u2 = ((DSAPublicKeyParameters)key).getY().modPow(u2, p); |
| |
| BigInteger v = u1.multiply(u2).mod(p).mod(q); |
| |
| return v.equals(r); |
| } |
| |
| private BigInteger calculateE(BigInteger n, byte[] message) |
| { |
| if (n.bitLength() >= message.length * 8) |
| { |
| return new BigInteger(1, message); |
| } |
| else |
| { |
| byte[] trunc = new byte[n.bitLength() / 8]; |
| |
| System.arraycopy(message, 0, trunc, 0, trunc.length); |
| |
| return new BigInteger(1, trunc); |
| } |
| } |
| |
| protected SecureRandom initSecureRandom(boolean needed, SecureRandom provided) |
| { |
| return !needed ? null : (provided != null) ? provided : new SecureRandom(); |
| } |
| |
| private BigInteger getRandomizer(BigInteger q, SecureRandom provided) |
| { |
| // Calculate a random multiple of q to add to k. Note that g^q = 1 (mod p), so adding multiple of q to k does not change r. |
| int randomBits = 7; |
| |
| return new BigInteger(randomBits, provided != null ? provided : new SecureRandom()).add(BigInteger.valueOf(128)).multiply(q); |
| } |
| } |