// --- BEGIN COPYRIGHT BLOCK --- // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program 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 for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // (C) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- package netscape.security.provider; import java.math.BigInteger; import java.security.AlgorithmParameterGenerator; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.InvalidParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.ProviderException; import java.security.SecureRandom; import java.security.interfaces.DSAParams; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.DSAParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.util.Hashtable; import netscape.security.x509.AlgIdDSA; /** * This class generates DSA key parameters and public/private key pairs * according to the DSS standard NIST FIPS 186. It uses the updated version of * SHA, SHA-1 as described in FIPS 180-1. * * @author Benjamin Renaud * * @version 1.23, 97/12/10 */ public class DSAKeyPairGenerator extends KeyPairGenerator implements java.security.interfaces.DSAKeyPairGenerator { private static Hashtable precomputedParams; static { /* * We support precomputed parameter for 512, 768 and 1024 bit moduli. In * this file we provide both the seed and counter value of the * generation process for each of these seeds, for validation purposes. * We also include the test vectors from the DSA specification, FIPS * 186, and the FIPS 186 Change No 1, which updates the test vector * using SHA-1 instead of SHA (for both the G function and the message * hash. */ precomputedParams = new Hashtable(); /* * L = 512 SEED = b869c82b35d70e1b1ff91b28e37a62ecdc34409b counter = 123 */ BigInteger p512 = new BigInteger( "fca682ce8e12caba26efccf7110e526db078b05edecb" + "cd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e1" + "2ed0899bcd132acd50d99151bdc43ee737592e17", 16); BigInteger q512 = new BigInteger( "962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16); BigInteger g512 = new BigInteger( "678471b27a9cf44ee91a49c5147db1a9aaf244f05a43" + "4d6486931d2d14271b9e35030b71fd73da179069b32e" + "2935630e1c2062354d0da20a6c416e50be794ca4", 16); /* * L = 768 SEED = 77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399 counter = 263 */ BigInteger p768 = new BigInteger( "e9e642599d355f37c97ffd3567120b8e25c9cd43e" + "927b3a9670fbec5d890141922d2c3b3ad24800937" + "99869d1e846aab49fab0ad26d2ce6a22219d470bc" + "e7d777d4a21fbe9c270b57f607002f3cef8393694" + "cf45ee3688c11a8c56ab127a3daf", 16); BigInteger q768 = new BigInteger( "9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", 16); BigInteger g768 = new BigInteger( "30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5fac" + "baecbe95f190aa7a31d23c4dbbcbe06174544401a" + "5b2c020965d8c2bd2171d3668445771f74ba084d2" + "029d83c1c158547f3a9f1a2715be23d51ae4d3e5a" + "1f6a7064f316933a346d3f529252", 16); /* * L = 1024 SEED = 8d5155894229d5e689ee01e6018a237e2cae64cd counter = 92 */ BigInteger p1024 = new BigInteger( "fd7f53811d75122952df4a9c2eece4e7f611b7523c" + "ef4400c31e3f80b6512669455d402251fb593d8d58" + "fabfc5f5ba30f6cb9b556cd7813b801d346ff26660" + "b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" + "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554" + "135a169132f675f3ae2b61d72aeff22203199dd148" + "01c7", 16); BigInteger q1024 = new BigInteger( "9760508f15230bccb292b982a2eb840bf0581cf5", 16); BigInteger g1024 = new BigInteger( "f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa" + "3aea82f9574c0b3d0782675159578ebad4594fe671" + "07108180b449167123e84c281613b7cf09328cc8a6" + "e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" + "0bfa213562f1fb627a01243bcca4f1bea8519089a8" + "83dfe15ae59f06928b665e807b552564014c3bfecf" + "492a", 16); try { AlgIdDSA alg512 = new AlgIdDSA(p512, q512, g512); AlgIdDSA alg768 = new AlgIdDSA(p768, q768, g768); AlgIdDSA alg1024 = new AlgIdDSA(p1024, q1024, g1024); precomputedParams.put(Integer.valueOf(512), alg512); precomputedParams.put(Integer.valueOf(768), alg768); precomputedParams.put(Integer.valueOf(1024), alg1024); } catch (Exception e) { throw new InternalError("initializing precomputed " + "algorithm parameters for Sun DSA"); } } /* The modulus length */ private int modlen = 1024; /* Generate new parameters, even if we have precomputed ones. */ boolean generateNewParameters = false; /* preset algorithm parameters. */ private BigInteger presetP, presetQ, presetG; /* The source of random bits to use */ SecureRandom random; public DSAKeyPairGenerator() { super("DSA"); } public void initialize(int strength, SecureRandom random) { if ((strength < 512) || (strength > 1024) || (strength % 64 != 0)) { throw new InvalidParameterException( "Modulus size must range from 512 to 1024 " + "and be a multiple of 64"); } /* Set the random */ this.random = random; if (this.random == null) { this.random = new SecureRandom(); } this.modlen = strength; DSAParams params = null; /* Find the precomputed parameters, if any */ if (!generateNewParameters) { Integer mod = Integer.valueOf(this.modlen); params = (DSAParams) precomputedParams.get(mod); } if (params != null) { setParams(params); } } /** * Initializes the DSA key pair generator. If genParams is * false, a set of pre-computed parameters is used. In this case, * modelen must be 512, 768, or 1024. */ public void initialize(int modlen, boolean genParams, SecureRandom random) throws InvalidParameterException { if (genParams == false && modlen != 512 && modlen != 768 && modlen != 1024) { throw new InvalidParameterException( "No precomputed parameters for requested modulus size " + "available"); } this.generateNewParameters = genParams; initialize(modlen, random); } /** * Initializes the DSA object using a DSA parameter object. * * @param params a fully initialized DSA parameter object. */ public void initialize(DSAParams params, SecureRandom random) throws InvalidParameterException { initialize(params.getP().bitLength(), random); setParams(params); } /** * Initializes the DSA object using a parameter object. * * @param params the parameter set to be used to generate the keys. * @param random the source of randomness for this generator. * * @exception InvalidAlgorithmParameterException if the given parameters are * inappropriate for this key pair generator */ public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { if (!(params instanceof DSAParameterSpec)) { throw new InvalidAlgorithmParameterException( "Inappropriate parameter"); } initialize(((DSAParameterSpec) params).getP().bitLength(), random); setParams((DSAParameterSpec) params); } /** * Generates a pair of keys usable by any JavaSecurity compliant DSA * implementation. * * @param rnd the source of random bits from which the random key generation * parameters are drawn. In particular, this includes the XSEED * parameter. * * @exception InvalidParameterException if the modulus is not between 512 * and 1024. */ public KeyPair generateKeyPair() { // set random if initialize() method has been skipped if (this.random == null) { this.random = new SecureRandom(); } if (presetP == null || presetQ == null || presetG == null || generateNewParameters) { AlgorithmParameterGenerator dsaParamGen; try { dsaParamGen = AlgorithmParameterGenerator.getInstance("DSA", "SUN"); } catch (NoSuchAlgorithmException e) { // this should never happen, because we provide it throw new RuntimeException(e.getMessage()); } catch (NoSuchProviderException e) { // this should never happen, because we provide it throw new RuntimeException(e.getMessage()); } dsaParamGen.init(modlen, random); DSAParameterSpec dsaParamSpec; try { dsaParamSpec = (DSAParameterSpec) dsaParamGen .generateParameters().getParameterSpec( DSAParameterSpec.class); } catch (InvalidParameterSpecException e) { // this should never happen throw new RuntimeException(e.getMessage()); } presetP = dsaParamSpec.getP(); presetQ = dsaParamSpec.getQ(); presetG = dsaParamSpec.getG(); } return generateKeyPair(presetP, presetQ, presetG, random); } public KeyPair generateKeyPair(BigInteger p, BigInteger q, BigInteger g, SecureRandom random) { BigInteger x = generateX(random, q); BigInteger y = generateY(x, p, g); try { DSAPublicKey pub = new DSAPublicKey(y, p, q, g); DSAPrivateKey priv = new DSAPrivateKey(x, p, q, g); KeyPair pair = new KeyPair(pub, priv); return pair; } catch (InvalidKeyException e) { throw new ProviderException(e.getMessage()); } } /* Test vectors from the DSA specs. */ private static int[] testXSeed = { 0xbd029bbe, 0x7f51960b, 0xcf9edb2b, 0x61f06f0f, 0xeb5a38b6 }; private int[] x_t = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; /** * Generate the private key component of the key pair using the provided * source of random bits. This method uses the random but source passed to * generate a seed and then calls the seed-based generateX method. */ private BigInteger generateX(SecureRandom random, BigInteger q) { BigInteger x = null; while (true) { int[] seed = new int[5]; for (int i = 0; i < 5; i++) { seed[i] = random.nextInt(); } x = generateX(seed, q); if (x.signum() > 0 && (x.compareTo(q) < 0)) { break; } } return x; } /** * Given a seed, generate the private key component of the key pair. In the * terminology used in the DSA specification (FIPS-186) seed is the XSEED * quantity. * * @param seed the seed to use to generate the private key. */ BigInteger generateX(int[] seed, BigInteger q) { /* * Test vector int[] tseed = { 0xbd029bbe, 0x7f51960b, 0xcf9edb2b, * 0x61f06f0f, 0xeb5a38b6 }; seed = tseed; */ // check out t in the spec. int[] t = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; // 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 x = new BigInteger(1, tmpBytes).mod(q); return x; } /** * Generate the public key component y of the key pair. * * @param x the private key component. * * @param p the base parameter. */ BigInteger generateY(BigInteger x, BigInteger p, BigInteger g) { BigInteger y = g.modPow(x, p); return y; } /** * Set the parameters. */ private void setParams(DSAParams params) { presetP = params.getP(); presetQ = params.getQ(); presetG = params.getG(); } /** * Set the parameters. */ private void setParams(DSAParameterSpec params) { presetP = params.getP(); presetQ = params.getQ(); presetG = params.getG(); } }