diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java index ebdb7d6289..bacbb6c244 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi.java @@ -6,18 +6,26 @@ import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.DSAParameterSpec; +import java.util.Hashtable; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.generators.DSAKeyPairGenerator; import org.bouncycastle.crypto.generators.DSAParametersGenerator; import org.bouncycastle.crypto.params.DSAKeyGenerationParameters; +import org.bouncycastle.crypto.params.DSAParameterGenerationParameters; import org.bouncycastle.crypto.params.DSAParameters; import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; import org.bouncycastle.crypto.params.DSAPublicKeyParameters; +import org.bouncycastle.util.Integers; +import org.bouncycastle.util.Properties; public class KeyPairGeneratorSpi extends java.security.KeyPairGenerator { + private static Hashtable params = new Hashtable(); + private static Object lock = new Object(); + DSAKeyGenerationParameters param; DSAKeyPairGenerator engine = new DSAKeyPairGenerator(); int strength = 1024; @@ -41,6 +49,7 @@ public void initialize( this.strength = strength; this.random = random; + this.initialised = false; } public void initialize( @@ -64,10 +73,65 @@ public KeyPair generateKeyPair() { if (!initialised) { - DSAParametersGenerator pGen = new DSAParametersGenerator(); + Integer paramStrength = Integers.valueOf(strength); + + if (params.containsKey(paramStrength)) + { + param = (DSAKeyGenerationParameters)params.get(paramStrength); + } + else + { + synchronized (lock) + { + // we do the check again in case we were blocked by a generator for + // our key size. + if (params.containsKey(paramStrength)) + { + param = (DSAKeyGenerationParameters)params.get(paramStrength); + } + else + { + DSAParametersGenerator pGen; + DSAParameterGenerationParameters dsaParams; + + // Typical combination of keysize and size of q. + // keysize = 1024, q's size = 160 + // keysize = 2048, q's size = 224 + // keysize = 2048, q's size = 256 + // keysize = 3072, q's size = 256 + // For simplicity if keysize is greater than 1024 then we choose q's size to be 256. + // For legacy keysize that is less than 1024-bit, we just use the 186-2 style parameters + if (strength == 1024) + { + pGen = new DSAParametersGenerator(); + if (Properties.isOverrideSet("org.bouncycastle.dsa.FIPS186-2for1024bits")) + { + pGen.init(strength, certainty, random); + } + else + { + dsaParams = new DSAParameterGenerationParameters(1024, 160, certainty, random); + pGen.init(dsaParams); + } + } + else if (strength > 1024) + { + dsaParams = new DSAParameterGenerationParameters(strength, 256, certainty, random); + pGen = new DSAParametersGenerator(new SHA256Digest()); + pGen.init(dsaParams); + } + else + { + pGen = new DSAParametersGenerator(); + pGen.init(strength, certainty, random); + } + param = new DSAKeyGenerationParameters(random, pGen.generateParameters()); + + params.put(paramStrength, param); + } + } + } - pGen.init(strength, certainty, random); - param = new DSAKeyGenerationParameters(random, pGen.generateParameters()); engine.init(param); initialised = true; } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java index a388c0e1b2..6d7b858173 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/DSATest.java @@ -20,6 +20,7 @@ import java.security.Security; import java.security.Signature; import java.security.SignatureException; +import java.security.interfaces.DSAParams; import java.security.interfaces.DSAPrivateKey; import java.security.interfaces.DSAPublicKey; import java.security.spec.DSAParameterSpec; @@ -1294,6 +1295,51 @@ private void testDSA2Parameters() } } + private void testKeyGeneration(int keysize) + throws Exception + { + KeyPairGenerator generator = KeyPairGenerator.getInstance("DSA", "BC"); + generator.initialize(keysize); + KeyPair keyPair = generator.generateKeyPair(); + DSAPrivateKey priv = (DSAPrivateKey)keyPair.getPrivate(); + DSAParams params = priv.getParams(); + isTrue("keysize mismatch", keysize == params.getP().bitLength()); + // The NIST standard does not fully specify the size of q that + // must be used for a given key size. Hence there are differences. + // For example if keysize = 2048, then OpenSSL uses 256 bit q's by default, + // but the SUN provider uses 224 bits. Both are acceptable sizes. + // The tests below simply asserts that the size of q does not decrease the + // overall security of the DSA. + int qsize = params.getQ().bitLength(); + switch (keysize) + { + case 1024: + isTrue("Invalid qsize for 1024 bit key:" + qsize, qsize >= 160); + break; + case 2048: + isTrue("Invalid qsize for 2048 bit key:" + qsize, qsize >= 224); + break; + case 3072: + isTrue("Invalid qsize for 3072 bit key:" + qsize, qsize >= 256); + break; + default: + fail("Invalid key size:" + keysize); + } + // Check the length of the private key. + // For example GPG4Browsers or the KJUR library derived from it use + // q.bitCount() instead of q.bitLength() to determine the size of the private key + // and hence would generate keys that are much too small. + isTrue("privkey error", priv.getX().bitLength() >= qsize - 32); + } + + private void testKeyGenerationAll() + throws Exception + { + testKeyGeneration(1024); + testKeyGeneration(2048); + testKeyGeneration(3072); + } + public void performTest() throws Exception { @@ -1331,6 +1377,7 @@ public void performTest() testNullParameters(); testValidate(); testModified(); + testKeyGenerationAll(); } protected BigInteger[] derDecode(