|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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.*; |
|
|
|
import sun.security.util.Debug; |
|
import sun.security.util.DerValue; |
|
import sun.security.util.DerInputStream; |
|
import sun.security.util.DerOutputStream; |
|
import sun.security.jca.JCAUtil; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
abstract class DSA extends SignatureSpi { |
|
|
|
|
|
private static final boolean debug = false; |
|
|
|
|
|
private static final int BLINDING_BITS = 7; |
|
|
|
|
|
private static final BigInteger BLINDING_CONSTANT = |
|
BigInteger.valueOf(1 << BLINDING_BITS); |
|
|
|
|
|
private DSAParams params; |
|
|
|
|
|
private BigInteger presetP, presetQ, presetG; |
|
|
|
|
|
private BigInteger presetY; |
|
|
|
|
|
private BigInteger presetX; |
|
|
|
|
|
private SecureRandom signingRandom; |
|
|
|
|
|
private final MessageDigest md; |
|
|
|
|
|
private final boolean p1363Format; |
|
|
|
|
|
|
|
|
|
*/ |
|
DSA(MessageDigest md) { |
|
this(md, false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
DSA(MessageDigest md, boolean p1363Format) { |
|
super(); |
|
this.md = md; |
|
this.p1363Format = p1363Format; |
|
} |
|
|
|
private static void checkKey(DSAParams params, int digestLen, String mdAlgo) |
|
throws InvalidKeyException { |
|
// FIPS186-3 states in sec4.2 that a hash function which provides |
|
// a lower security strength than the (L, N) pair ordinarily should |
|
|
|
int valueN = params.getQ().bitLength(); |
|
if (valueN > digestLen) { |
|
throw new InvalidKeyException("The security strength of " + |
|
mdAlgo + " digest algorithm is not sufficient for this key size"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
DSAParams params = priv.getParams(); |
|
if (params == null) { |
|
throw new InvalidKeyException("DSA private key lacks parameters"); |
|
} |
|
|
|
// check key size against hash output size for signing |
|
|
|
if (md.getAlgorithm() != "NullDigest20") { |
|
checkKey(params, md.getDigestLength()*8, md.getAlgorithm()); |
|
} |
|
|
|
this.params = params; |
|
this.presetX = priv.getX(); |
|
this.presetY = null; |
|
this.presetP = params.getP(); |
|
this.presetQ = params.getQ(); |
|
this.presetG = params.getG(); |
|
this.md.reset(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
DSAParams params = pub.getParams(); |
|
if (params == null) { |
|
throw new InvalidKeyException("DSA public key lacks parameters"); |
|
} |
|
this.params = params; |
|
this.presetY = pub.getY(); |
|
this.presetX = null; |
|
this.presetP = params.getP(); |
|
this.presetQ = params.getQ(); |
|
this.presetG = params.getG(); |
|
this.md.reset(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
protected void engineUpdate(byte b) { |
|
md.update(b); |
|
} |
|
|
|
|
|
|
|
*/ |
|
protected void engineUpdate(byte[] data, int off, int len) { |
|
md.update(data, off, len); |
|
} |
|
|
|
protected void engineUpdate(ByteBuffer b) { |
|
md.update(b); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected byte[] engineSign() throws SignatureException { |
|
BigInteger k = generateK(presetQ); |
|
BigInteger r = generateR(presetP, presetQ, presetG, k); |
|
BigInteger s = generateS(presetX, presetQ, r, k); |
|
|
|
if (p1363Format) { |
|
|
|
byte[] rBytes = r.toByteArray(); |
|
byte[] sBytes = s.toByteArray(); |
|
|
|
int size = presetQ.bitLength() / 8; |
|
byte[] outseq = new byte[size * 2]; |
|
|
|
int rLength = rBytes.length; |
|
int sLength = sBytes.length; |
|
int i; |
|
for (i = rLength; i > 0 && rBytes[rLength - i] == 0; i--); |
|
|
|
int j; |
|
for (j = sLength; |
|
j > 0 && sBytes[sLength - j] == 0; j--); |
|
|
|
System.arraycopy(rBytes, rLength - i, outseq, size - i, i); |
|
System.arraycopy(sBytes, sLength - j, outseq, size * 2 - j, j); |
|
|
|
return outseq; |
|
} else { |
|
|
|
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"); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean engineVerify(byte[] signature) |
|
throws SignatureException { |
|
return engineVerify(signature, 0, signature.length); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected boolean engineVerify(byte[] signature, int offset, int length) |
|
throws SignatureException { |
|
|
|
BigInteger r = null; |
|
BigInteger s = null; |
|
|
|
if (p1363Format) { |
|
if ((length & 1) == 1) { |
|
|
|
throw new SignatureException("invalid signature format"); |
|
} |
|
int mid = length/2; |
|
r = new BigInteger(Arrays.copyOfRange(signature, 0, mid)); |
|
s = new BigInteger(Arrays.copyOfRange(signature, mid, length)); |
|
} else { |
|
|
|
try { |
|
|
|
DerInputStream in = |
|
new DerInputStream(signature, offset, length, false); |
|
DerValue[] values = in.getSequence(2); |
|
|
|
// check number of components in the read sequence |
|
|
|
if ((values.length != 2) || (in.available() != 0)) { |
|
throw new IOException("Invalid encoding for signature"); |
|
} |
|
r = values[0].getBigInteger(); |
|
s = values[1].getBigInteger(); |
|
} catch (IOException e) { |
|
throw new SignatureException("Invalid encoding for signature", e); |
|
} |
|
} |
|
|
|
// 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 |
|
|
|
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"); |
|
} |
|
} |
|
|
|
@Deprecated |
|
protected void engineSetParameter(String key, Object param) { |
|
throw new InvalidParameterException("No parameter accepted"); |
|
} |
|
|
|
@Override |
|
protected void engineSetParameter(AlgorithmParameterSpec params) |
|
throws InvalidAlgorithmParameterException { |
|
if (params != null) { |
|
throw new InvalidAlgorithmParameterException("No parameter accepted"); |
|
} |
|
} |
|
|
|
@Deprecated |
|
protected Object engineGetParameter(String key) { |
|
return null; |
|
} |
|
|
|
@Override |
|
protected AlgorithmParameters engineGetParameters() { |
|
return null; |
|
} |
|
|
|
|
|
private BigInteger generateR(BigInteger p, BigInteger q, BigInteger g, |
|
BigInteger k) { |
|
|
|
|
|
SecureRandom random = getSigningRandom(); |
|
|
|
BigInteger blindingValue = new BigInteger(BLINDING_BITS, random); |
|
|
|
blindingValue = blindingValue.add(BLINDING_CONSTANT); |
|
|
|
k = k.add(q.multiply(blindingValue)); |
|
|
|
BigInteger temp = g.modPow(k, p); |
|
return temp.mod(q); |
|
} |
|
|
|
private BigInteger generateS(BigInteger x, BigInteger q, |
|
BigInteger r, BigInteger k) throws SignatureException { |
|
|
|
byte[] s2; |
|
try { |
|
s2 = md.digest(); |
|
} catch (RuntimeException re) { |
|
|
|
throw new SignatureException(re.getMessage()); |
|
} |
|
|
|
int nBytes = q.bitLength()/8; |
|
if (nBytes < s2.length) { |
|
s2 = Arrays.copyOfRange(s2, 0, nBytes); |
|
} |
|
BigInteger z = new BigInteger(1, s2); |
|
BigInteger k1 = k.modInverse(q); |
|
|
|
return x.multiply(r).add(z).multiply(k1).mod(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; |
|
try { |
|
s2 = md.digest(); |
|
} catch (RuntimeException re) { |
|
|
|
throw new SignatureException(re.getMessage()); |
|
} |
|
|
|
int nBytes = q.bitLength()/8; |
|
if (nBytes < s2.length) { |
|
s2 = Arrays.copyOfRange(s2, 0, nBytes); |
|
} |
|
BigInteger z = new BigInteger(1, s2); |
|
|
|
BigInteger u1 = z.multiply(w).mod(q); |
|
BigInteger u2 = (r.multiply(w)).mod(q); |
|
|
|
BigInteger t1 = g.modPow(u1,p); |
|
BigInteger t2 = y.modPow(u2,p); |
|
BigInteger t3 = t1.multiply(t2); |
|
BigInteger t5 = t3.mod(p); |
|
return t5.mod(q); |
|
} |
|
|
|
protected BigInteger generateK(BigInteger q) { |
|
|
|
SecureRandom random = getSigningRandom(); |
|
byte[] kValue = new byte[(q.bitLength() + 7)/8 + 8]; |
|
|
|
random.nextBytes(kValue); |
|
return new BigInteger(1, kValue).mod( |
|
q.subtract(BigInteger.ONE)).add(BigInteger.ONE); |
|
} |
|
|
|
// Use the application-specified SecureRandom Object if provided. |
|
|
|
protected SecureRandom getSigningRandom() { |
|
if (signingRandom == null) { |
|
if (appRandom != null) { |
|
signingRandom = appRandom; |
|
} else { |
|
signingRandom = JCAUtil.getSecureRandom(); |
|
} |
|
} |
|
return signingRandom; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class SHA224withDSA extends DSA { |
|
public SHA224withDSA() throws NoSuchAlgorithmException { |
|
super(MessageDigest.getInstance("SHA-224")); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class SHA224withDSAinP1363Format extends DSA { |
|
public SHA224withDSAinP1363Format() throws NoSuchAlgorithmException { |
|
super(MessageDigest.getInstance("SHA-224"), true); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class SHA256withDSA extends DSA { |
|
public SHA256withDSA() throws NoSuchAlgorithmException { |
|
super(MessageDigest.getInstance("SHA-256")); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class SHA256withDSAinP1363Format extends DSA { |
|
public SHA256withDSAinP1363Format() throws NoSuchAlgorithmException { |
|
super(MessageDigest.getInstance("SHA-256"), true); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class SHA1withDSA extends DSA { |
|
public SHA1withDSA() throws NoSuchAlgorithmException { |
|
super(MessageDigest.getInstance("SHA-1")); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class SHA1withDSAinP1363Format extends DSA { |
|
public SHA1withDSAinP1363Format() throws NoSuchAlgorithmException { |
|
super(MessageDigest.getInstance("SHA-1"), true); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static class Raw extends DSA { |
|
// Internal special-purpose MessageDigest impl for RawDSA |
|
// Only override whatever methods used |
|
|
|
public static final class NullDigest20 extends MessageDigest { |
|
|
|
private final byte[] digestBuffer = new byte[20]; |
|
|
|
// offset into the buffer; use Integer.MAX_VALUE to indicate |
|
|
|
private int ofs = 0; |
|
|
|
protected NullDigest20() { |
|
super("NullDigest20"); |
|
} |
|
protected void engineUpdate(byte input) { |
|
if (ofs == digestBuffer.length) { |
|
ofs = Integer.MAX_VALUE; |
|
} else { |
|
digestBuffer[ofs++] = input; |
|
} |
|
} |
|
protected void engineUpdate(byte[] input, int offset, int len) { |
|
if (len > (digestBuffer.length - ofs)) { |
|
ofs = Integer.MAX_VALUE; |
|
} else { |
|
System.arraycopy(input, offset, digestBuffer, ofs, len); |
|
ofs += len; |
|
} |
|
} |
|
protected final void engineUpdate(ByteBuffer input) { |
|
int inputLen = input.remaining(); |
|
if (inputLen > (digestBuffer.length - ofs)) { |
|
ofs = Integer.MAX_VALUE; |
|
} else { |
|
input.get(digestBuffer, ofs, inputLen); |
|
ofs += inputLen; |
|
} |
|
} |
|
protected byte[] engineDigest() throws RuntimeException { |
|
if (ofs != digestBuffer.length) { |
|
throw new RuntimeException |
|
("Data for RawDSA must be exactly 20 bytes long"); |
|
} |
|
reset(); |
|
return digestBuffer; |
|
} |
|
protected int engineDigest(byte[] buf, int offset, int len) |
|
throws DigestException { |
|
if (ofs != digestBuffer.length) { |
|
throw new DigestException |
|
("Data for RawDSA must be exactly 20 bytes long"); |
|
} |
|
if (len < digestBuffer.length) { |
|
throw new DigestException |
|
("Output buffer too small; must be at least 20 bytes"); |
|
} |
|
System.arraycopy(digestBuffer, 0, buf, offset, digestBuffer.length); |
|
reset(); |
|
return digestBuffer.length; |
|
} |
|
|
|
protected void engineReset() { |
|
ofs = 0; |
|
} |
|
protected final int engineGetDigestLength() { |
|
return digestBuffer.length; |
|
} |
|
} |
|
|
|
private Raw(boolean p1363Format) throws NoSuchAlgorithmException { |
|
super(new NullDigest20(), p1363Format); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class RawDSA extends Raw { |
|
public RawDSA() throws NoSuchAlgorithmException { |
|
super(false); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static final class RawDSAinP1363Format extends Raw { |
|
public RawDSAinP1363Format() throws NoSuchAlgorithmException { |
|
super(true); |
|
} |
|
} |
|
} |