1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.provider; 27 28 import java.math.BigInteger; 29 import java.security.AlgorithmParameterGeneratorSpi; 30 import java.security.AlgorithmParameters; 31 import java.security.InvalidAlgorithmParameterException; 32 import java.security.NoSuchAlgorithmException; 33 import java.security.NoSuchProviderException; 34 import java.security.InvalidParameterException; 35 import java.security.MessageDigest; 36 import java.security.SecureRandom; 37 import java.security.ProviderException; 38 import java.security.spec.AlgorithmParameterSpec; 39 import java.security.spec.InvalidParameterSpecException; 40 import java.security.spec.DSAParameterSpec; 41 import java.security.spec.DSAGenParameterSpec; 42 43 import static sun.security.util.SecurityProviderConstants.DEF_DSA_KEY_SIZE; 44 import static sun.security.util.SecurityProviderConstants.getDefDSASubprimeSize; 45 46 47 /** 48 * This class generates parameters for the DSA algorithm. 49 * 50 * @author Jan Luehe 51 * 52 * 53 * @see java.security.AlgorithmParameters 54 * @see java.security.spec.AlgorithmParameterSpec 55 * @see DSAParameters 56 * 57 * @since 1.2 58 */ 59 60 public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi { 61 62 // the length of prime P, subPrime Q, and seed in bits 63 private int valueL = -1; 64 private int valueN = -1; 65 private int seedLen = -1; 66 67 // the source of randomness 68 private SecureRandom random; 69 70 // useful constants 71 private static final BigInteger ZERO = BigInteger.valueOf(0); 72 private static final BigInteger ONE = BigInteger.valueOf(1); 73 private static final BigInteger TWO = BigInteger.valueOf(2); 74 75 public DSAParameterGenerator() { 76 } 77 78 /** 79 * Initializes this parameter generator for a certain strength 80 * and source of randomness. 81 * 82 * @param strength the strength (size of prime) in bits 83 * @param random the source of randomness 84 */ 85 @Override 86 protected void engineInit(int strength, SecureRandom random) { 87 if ((strength != 2048) && 88 ((strength < 512) || (strength > 1024) || (strength % 64 != 0))) { 89 throw new InvalidParameterException( 90 "Unexpected strength (size of prime): " + strength + 91 ". Prime size should be 512-1024, or 2048"); 92 } 93 this.valueL = strength; 94 this.valueN = getDefDSASubprimeSize(strength); 95 this.seedLen = valueN; 96 this.random = random; 97 } 98 99 /** 100 * Initializes this parameter generator with a set of 101 * algorithm-specific parameter generation values. 102 * 103 * @param genParamSpec the set of algorithm-specific parameter 104 * generation values 105 * @param random the source of randomness 106 * 107 * @exception InvalidAlgorithmParameterException if the given parameter 108 * generation values are inappropriate for this parameter generator 109 */ 110 @Override 111 protected void engineInit(AlgorithmParameterSpec genParamSpec, 112 SecureRandom random) throws InvalidAlgorithmParameterException { 113 if (!(genParamSpec instanceof DSAGenParameterSpec)) { 114 throw new InvalidAlgorithmParameterException("Invalid parameter"); 115 } 116 DSAGenParameterSpec dsaGenParams = (DSAGenParameterSpec)genParamSpec; 117 int primePLen = dsaGenParams.getPrimePLength(); 118 if (primePLen > 2048) { 119 throw new InvalidParameterException 120 ("No support for prime size " + primePLen); 121 } 122 // directly initialize using the already validated values 123 this.valueL = primePLen; 124 this.valueN = dsaGenParams.getSubprimeQLength(); 125 this.seedLen = dsaGenParams.getSeedLength(); 126 this.random = random; 127 } 128 129 /** 130 * Generates the parameters. 131 * 132 * @return the new AlgorithmParameters object 133 */ 134 protected AlgorithmParameters engineGenerateParameters() { 135 AlgorithmParameters algParams = null; 136 try { 137 if (this.random == null) { 138 this.random = new SecureRandom(); 139 } 140 if (valueL == -1) { 141 engineInit(DEF_DSA_KEY_SIZE, this.random); 142 } 143 BigInteger[] pAndQ = generatePandQ(this.random, valueL, 144 valueN, seedLen); 145 BigInteger paramP = pAndQ[0]; 146 BigInteger paramQ = pAndQ[1]; 147 BigInteger paramG = generateG(paramP, paramQ); 148 149 DSAParameterSpec dsaParamSpec = 150 new DSAParameterSpec(paramP, paramQ, paramG); 151 algParams = AlgorithmParameters.getInstance("DSA", "SUN"); 152 algParams.init(dsaParamSpec); 153 } catch (InvalidParameterSpecException e) { 154 // this should never happen 155 throw new RuntimeException(e.getMessage()); 156 } catch (NoSuchAlgorithmException e) { 157 // this should never happen, because we provide it 158 throw new RuntimeException(e.getMessage()); 159 } catch (NoSuchProviderException e) { 160 // this should never happen, because we provide it 161 throw new RuntimeException(e.getMessage()); 162 } 163 164 return algParams; 165 } 166 167 /* 168 * Generates the prime and subprime parameters for DSA, 169 * using the provided source of randomness. 170 * This method will generate new seeds until a suitable 171 * seed has been found. 172 * 173 * @param random the source of randomness to generate the 174 * seed 175 * @param valueL the size of <code>p</code>, in bits. 176 * @param valueN the size of <code>q</code>, in bits. 177 * @param seedLen the length of <code>seed</code>, in bits. 178 * 179 * @return an array of BigInteger, with <code>p</code> at index 0 and 180 * <code>q</code> at index 1, the seed at index 2, and the counter value 181 * at index 3. 182 */ 183 private static BigInteger[] generatePandQ(SecureRandom random, int valueL, 184 int valueN, int seedLen) { 185 String hashAlg = null; 186 if (valueN == 160) { 187 hashAlg = "SHA"; 188 } else if (valueN == 224) { 189 hashAlg = "SHA-224"; 190 } else if (valueN == 256) { 191 hashAlg = "SHA-256"; 192 } 193 MessageDigest hashObj = null; 194 try { 195 hashObj = MessageDigest.getInstance(hashAlg); 196 } catch (NoSuchAlgorithmException nsae) { 197 // should never happen 198 nsae.printStackTrace(); 199 } 200 201 /* Step 3, 4: Useful variables */ 202 int outLen = hashObj.getDigestLength()*8; 203 int n = (valueL - 1) / outLen; 204 int b = (valueL - 1) % outLen; 205 byte[] seedBytes = new byte[seedLen/8]; 206 BigInteger twoSl = TWO.pow(seedLen); 207 int primeCertainty = -1; 208 if (valueL <= 1024) { 209 primeCertainty = 80; 210 } else if (valueL == 2048) { 211 primeCertainty = 112; 212 } 213 214 if (primeCertainty < 0) { 215 throw new ProviderException("Invalid valueL: " + valueL); 216 } 217 BigInteger resultP, resultQ, seed = null; 218 int counter; 219 while (true) { 220 do { 221 /* Step 5 */ 222 random.nextBytes(seedBytes); 223 seed = new BigInteger(1, seedBytes); 224 225 /* Step 6 */ 226 BigInteger U = new BigInteger(1, hashObj.digest(seedBytes)). 227 mod(TWO.pow(valueN - 1)); 228 229 /* Step 7 */ 230 resultQ = TWO.pow(valueN - 1).add(U).add(ONE). subtract(U.mod(TWO)); 231 } while (!resultQ.isProbablePrime(primeCertainty)); 232 233 /* Step 10 */ 234 BigInteger offset = ONE; 235 /* Step 11 */ 236 for (counter = 0; counter < 4*valueL; counter++) { 237 BigInteger V[] = new BigInteger[n + 1]; 238 /* Step 11.1 */ 239 for (int j = 0; j <= n; j++) { 240 BigInteger J = BigInteger.valueOf(j); 241 BigInteger tmp = (seed.add(offset).add(J)).mod(twoSl); 242 byte[] vjBytes = hashObj.digest(toByteArray(tmp)); 243 V[j] = new BigInteger(1, vjBytes); 244 } 245 /* Step 11.2 */ 246 BigInteger W = V[0]; 247 for (int i = 1; i < n; i++) { 248 W = W.add(V[i].multiply(TWO.pow(i * outLen))); 249 } 250 W = W.add((V[n].mod(TWO.pow(b))).multiply(TWO.pow(n * outLen))); 251 /* Step 11.3 */ 252 BigInteger twoLm1 = TWO.pow(valueL - 1); 253 BigInteger X = W.add(twoLm1); 254 /* Step 11.4, 11.5 */ 255 BigInteger c = X.mod(resultQ.multiply(TWO)); 256 resultP = X.subtract(c.subtract(ONE)); 257 /* Step 11.6, 11.7 */ 258 if (resultP.compareTo(twoLm1) > -1 259 && resultP.isProbablePrime(primeCertainty)) { 260 /* Step 11.8 */ 261 BigInteger[] result = {resultP, resultQ, seed, 262 BigInteger.valueOf(counter)}; 263 return result; 264 } 265 /* Step 11.9 */ 266 offset = offset.add(BigInteger.valueOf(n)).add(ONE); 267 } 268 } 269 270 } 271 272 /* 273 * Generates the <code>g</code> parameter for DSA. 274 * 275 * @param p the prime, <code>p</code>. 276 * @param q the subprime, <code>q</code>. 277 * 278 * @param the <code>g</code> 279 */ 280 private static BigInteger generateG(BigInteger p, BigInteger q) { 281 BigInteger h = ONE; 282 /* Step 1 */ 283 BigInteger pMinusOneOverQ = (p.subtract(ONE)).divide(q); 284 BigInteger resultG = ONE; 285 while (resultG.compareTo(TWO) < 0) { 286 /* Step 3 */ 287 resultG = h.modPow(pMinusOneOverQ, p); 288 h = h.add(ONE); 289 } 290 return resultG; 291 } 292 293 /* 294 * Converts the result of a BigInteger.toByteArray call to an exact 295 * signed magnitude representation for any positive number. 296 */ 297 private static byte[] toByteArray(BigInteger bigInt) { 298 byte[] result = bigInt.toByteArray(); 299 if (result[0] == 0) { 300 byte[] tmp = new byte[result.length - 1]; 301 System.arraycopy(result, 1, tmp, 0, tmp.length); 302 result = tmp; 303 } 304 return result; 305 } 306 }