52 * @since 1.2 |
54 * @since 1.2 |
53 */ |
55 */ |
54 |
56 |
55 public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi { |
57 public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi { |
56 |
58 |
57 // the modulus length |
59 // the default parameters |
58 private int modLen = 1024; // default |
60 private static final DSAGenParameterSpec DEFAULTS = |
|
61 new DSAGenParameterSpec(1024, 160, 160); |
|
62 |
|
63 // the length of prime P, subPrime Q, and seed in bits |
|
64 private int valueL = -1; |
|
65 private int valueN = -1; |
|
66 private int seedLen = -1; |
59 |
67 |
60 // the source of randomness |
68 // the source of randomness |
61 private SecureRandom random; |
69 private SecureRandom random; |
62 |
70 |
63 // useful constants |
71 // useful constants |
64 private static final BigInteger ZERO = BigInteger.valueOf(0); |
72 private static final BigInteger ZERO = BigInteger.valueOf(0); |
65 private static final BigInteger ONE = BigInteger.valueOf(1); |
73 private static final BigInteger ONE = BigInteger.valueOf(1); |
66 private static final BigInteger TWO = BigInteger.valueOf(2); |
74 private static final BigInteger TWO = BigInteger.valueOf(2); |
67 |
75 |
68 // Make a SHA-1 hash function |
|
69 private SHA sha; |
|
70 |
|
71 public DSAParameterGenerator() { |
76 public DSAParameterGenerator() { |
72 this.sha = new SHA(); |
|
73 } |
77 } |
74 |
78 |
75 /** |
79 /** |
76 * Initializes this parameter generator for a certain strength |
80 * Initializes this parameter generator for a certain strength |
77 * and source of randomness. |
81 * and source of randomness. |
78 * |
82 * |
79 * @param strength the strength (size of prime) in bits |
83 * @param strength the strength (size of prime) in bits |
80 * @param random the source of randomness |
84 * @param random the source of randomness |
81 */ |
85 */ |
82 protected void engineInit(int strength, SecureRandom random) { |
86 protected void engineInit(int strength, SecureRandom random) { |
83 /* |
87 if ((strength >= 512) && (strength <= 1024) && (strength % 64 == 0)) { |
84 * Bruce Schneier, "Applied Cryptography", 2nd Edition, |
88 this.valueN = 160; |
85 * Description of DSA: |
89 } else if (strength == 2048) { |
86 * [...] The algorithm uses the following parameter: |
90 this.valueN = 224; |
87 * p=a prime number L bits long, when L ranges from 512 to 1024 and is |
91 // } else if (strength == 3072) { |
88 * a multiple of 64. [...] |
92 // this.valueN = 256; |
89 */ |
93 } else { |
90 if ((strength < 512) || (strength > 1024) || (strength % 64 != 0)) { |
|
91 throw new InvalidParameterException |
94 throw new InvalidParameterException |
92 ("Prime size must range from 512 to 1024 " |
95 ("Prime size should be 512 - 1024, or 2048"); |
93 + "and be a multiple of 64"); |
96 } |
94 } |
97 this.valueL = strength; |
95 this.modLen = strength; |
98 this.seedLen = valueN; |
96 this.random = random; |
99 this.random = random; |
97 } |
100 } |
98 |
101 |
99 /** |
102 /** |
100 * Initializes this parameter generator with a set of |
103 * Initializes this parameter generator with a set of |
101 * algorithm-specific parameter generation values. |
104 * algorithm-specific parameter generation values. |
102 * |
105 * |
103 * @param params the set of algorithm-specific parameter generation values |
106 * @param genParamSpec the set of algorithm-specific parameter generation values |
104 * @param random the source of randomness |
107 * @param random the source of randomness |
105 * |
108 * |
106 * @exception InvalidAlgorithmParameterException if the given parameter |
109 * @exception InvalidAlgorithmParameterException if the given parameter |
107 * generation values are inappropriate for this parameter generator |
110 * generation values are inappropriate for this parameter generator |
108 */ |
111 */ |
109 protected void engineInit(AlgorithmParameterSpec genParamSpec, |
112 protected void engineInit(AlgorithmParameterSpec genParamSpec, |
110 SecureRandom random) |
113 SecureRandom random) |
111 throws InvalidAlgorithmParameterException { |
114 throws InvalidAlgorithmParameterException { |
|
115 if (!(genParamSpec instanceof DSAGenParameterSpec)) { |
112 throw new InvalidAlgorithmParameterException("Invalid parameter"); |
116 throw new InvalidAlgorithmParameterException("Invalid parameter"); |
|
117 } |
|
118 DSAGenParameterSpec dsaGenParams = (DSAGenParameterSpec) genParamSpec; |
|
119 if (dsaGenParams.getPrimePLength() > 2048) { |
|
120 throw new InvalidParameterException |
|
121 ("Prime size should be 512 - 1024, or 2048"); |
|
122 } |
|
123 // directly initialize using the already validated values |
|
124 this.valueL = dsaGenParams.getPrimePLength(); |
|
125 this.valueN = dsaGenParams.getSubprimeQLength(); |
|
126 this.seedLen = dsaGenParams.getSeedLength(); |
|
127 this.random = random; |
113 } |
128 } |
114 |
129 |
115 /** |
130 /** |
116 * Generates the parameters. |
131 * Generates the parameters. |
117 * |
132 * |
121 AlgorithmParameters algParams = null; |
136 AlgorithmParameters algParams = null; |
122 try { |
137 try { |
123 if (this.random == null) { |
138 if (this.random == null) { |
124 this.random = new SecureRandom(); |
139 this.random = new SecureRandom(); |
125 } |
140 } |
126 |
141 if (valueL == -1) { |
127 BigInteger[] pAndQ = generatePandQ(this.random, this.modLen); |
142 try { |
|
143 engineInit(DEFAULTS, this.random); |
|
144 } catch (InvalidAlgorithmParameterException iape) { |
|
145 // should never happen |
|
146 } |
|
147 } |
|
148 BigInteger[] pAndQ = generatePandQ(this.random, valueL, |
|
149 valueN, seedLen); |
128 BigInteger paramP = pAndQ[0]; |
150 BigInteger paramP = pAndQ[0]; |
129 BigInteger paramQ = pAndQ[1]; |
151 BigInteger paramQ = pAndQ[1]; |
130 BigInteger paramG = generateG(paramP, paramQ); |
152 BigInteger paramG = generateG(paramP, paramQ); |
131 |
153 |
132 DSAParameterSpec dsaParamSpec = new DSAParameterSpec(paramP, |
154 DSAParameterSpec dsaParamSpec = |
133 paramQ, |
155 new DSAParameterSpec(paramP, paramQ, paramG); |
134 paramG); |
|
135 algParams = AlgorithmParameters.getInstance("DSA", "SUN"); |
156 algParams = AlgorithmParameters.getInstance("DSA", "SUN"); |
136 algParams.init(dsaParamSpec); |
157 algParams.init(dsaParamSpec); |
137 } catch (InvalidParameterSpecException e) { |
158 } catch (InvalidParameterSpecException e) { |
138 // this should never happen |
159 // this should never happen |
139 throw new RuntimeException(e.getMessage()); |
160 throw new RuntimeException(e.getMessage()); |
154 * This method will generate new seeds until a suitable |
175 * This method will generate new seeds until a suitable |
155 * seed has been found. |
176 * seed has been found. |
156 * |
177 * |
157 * @param random the source of randomness to generate the |
178 * @param random the source of randomness to generate the |
158 * seed |
179 * seed |
159 * @param L the size of <code>p</code>, in bits. |
180 * @param valueL the size of <code>p</code>, in bits. |
|
181 * @param valueN the size of <code>q</code>, in bits. |
|
182 * @param seedLen the length of <code>seed</code>, in bits. |
160 * |
183 * |
161 * @return an array of BigInteger, with <code>p</code> at index 0 and |
184 * @return an array of BigInteger, with <code>p</code> at index 0 and |
162 * <code>q</code> at index 1. |
|
163 */ |
|
164 BigInteger[] generatePandQ(SecureRandom random, int L) { |
|
165 BigInteger[] result = null; |
|
166 byte[] seed = new byte[20]; |
|
167 |
|
168 while(result == null) { |
|
169 for (int i = 0; i < 20; i++) { |
|
170 seed[i] = (byte)random.nextInt(); |
|
171 } |
|
172 result = generatePandQ(seed, L); |
|
173 } |
|
174 return result; |
|
175 } |
|
176 |
|
177 /* |
|
178 * Generates the prime and subprime parameters for DSA. |
|
179 * |
|
180 * <p>The seed parameter corresponds to the <code>SEED</code> parameter |
|
181 * referenced in the FIPS specification of the DSA algorithm, |
|
182 * and L is the size of <code>p</code>, in bits. |
|
183 * |
|
184 * @param seed the seed to generate the parameters |
|
185 * @param L the size of <code>p</code>, in bits. |
|
186 * |
|
187 * @return an array of BigInteger, with <code>p</code> at index 0, |
|
188 * <code>q</code> at index 1, the seed at index 2, and the counter value |
185 * <code>q</code> at index 1, the seed at index 2, and the counter value |
189 * at index 3, or null if the seed does not yield suitable numbers. |
186 * at index 3. |
190 */ |
187 */ |
191 BigInteger[] generatePandQ(byte[] seed, int L) { |
188 private static BigInteger[] generatePandQ(SecureRandom random, int valueL, |
192 |
189 int valueN, int seedLen) { |
193 /* Useful variables */ |
190 String hashAlg = null; |
194 int g = seed.length * 8; |
191 if (valueN == 160) { |
195 int n = (L - 1) / 160; |
192 hashAlg = "SHA"; |
196 int b = (L - 1) % 160; |
193 } else if (valueN == 224) { |
197 |
194 hashAlg = "SHA-224"; |
198 BigInteger SEED = new BigInteger(1, seed); |
195 } else if (valueN == 256) { |
199 BigInteger TWOG = TWO.pow(2 * g); |
196 hashAlg = "SHA-256"; |
200 |
197 } |
201 /* Step 2 (Step 1 is getting seed). */ |
198 MessageDigest hashObj = null; |
202 byte[] U1 = SHA(seed); |
199 try { |
203 byte[] U2 = SHA(toByteArray((SEED.add(ONE)).mod(TWOG))); |
200 hashObj = MessageDigest.getInstance(hashAlg); |
204 |
201 } catch (NoSuchAlgorithmException nsae) { |
205 xor(U1, U2); |
202 // should never happen |
206 byte[] U = U1; |
203 nsae.printStackTrace(); |
207 |
204 } |
208 /* Step 3: For q by setting the msb and lsb to 1 */ |
205 |
209 U[0] |= 0x80; |
206 /* Step 3, 4: Useful variables */ |
210 U[19] |= 1; |
207 int outLen = hashObj.getDigestLength()*8; |
211 BigInteger q = new BigInteger(1, U); |
208 int n = (valueL - 1) / outLen; |
212 |
209 int b = (valueL - 1) % outLen; |
213 /* Step 5 */ |
210 byte[] seedBytes = new byte[seedLen/8]; |
214 if (!q.isProbablePrime(80)) { |
211 BigInteger twoSl = TWO.pow(seedLen); |
215 return null; |
212 int primeCertainty = 80; // for 1024-bit prime P |
216 |
213 if (valueL == 2048) { |
217 } else { |
214 primeCertainty = 112; |
218 BigInteger V[] = new BigInteger[n + 1]; |
215 //} else if (valueL == 3072) { |
219 BigInteger offset = TWO; |
216 // primeCertainty = 128; |
220 |
217 } |
221 /* Step 6 */ |
218 |
222 for (int counter = 0; counter < 4096; counter++) { |
219 BigInteger resultP, resultQ, seed = null; |
223 |
220 int counter; |
224 /* Step 7 */ |
221 while (true) { |
225 for (int k = 0; k <= n; k++) { |
222 do { |
226 BigInteger K = BigInteger.valueOf(k); |
223 /* Step 5 */ |
227 BigInteger tmp = (SEED.add(offset).add(K)).mod(TWOG); |
224 random.nextBytes(seedBytes); |
228 V[k] = new BigInteger(1, SHA(toByteArray(tmp))); |
225 seed = new BigInteger(1, seedBytes); |
229 } |
226 |
230 |
227 /* Step 6 */ |
231 /* Step 8 */ |
228 BigInteger U = new BigInteger(1, hashObj.digest(seedBytes)). |
232 BigInteger W = V[0]; |
229 mod(TWO.pow(valueN - 1)); |
233 for (int i = 1; i < n; i++) { |
230 |
234 W = W.add(V[i].multiply(TWO.pow(i * 160))); |
231 /* Step 7 */ |
235 } |
232 resultQ = TWO.pow(valueN - 1).add(U).add(ONE). subtract(U.mod(TWO)); |
236 W = W.add((V[n].mod(TWO.pow(b))).multiply(TWO.pow(n * 160))); |
233 } while (!resultQ.isProbablePrime(primeCertainty)); |
237 |
234 |
238 BigInteger TWOLm1 = TWO.pow(L - 1); |
235 /* Step 10 */ |
239 BigInteger X = W.add(TWOLm1); |
236 BigInteger offset = ONE; |
240 |
237 /* Step 11 */ |
241 /* Step 9 */ |
238 for (counter = 0; counter < 4*valueL; counter++) { |
242 BigInteger c = X.mod(q.multiply(TWO)); |
239 BigInteger V[] = new BigInteger[n + 1]; |
243 BigInteger p = X.subtract(c.subtract(ONE)); |
240 /* Step 11.1 */ |
244 |
241 for (int j = 0; j <= n; j++) { |
245 /* Step 10 - 13 */ |
242 BigInteger J = BigInteger.valueOf(j); |
246 if (p.compareTo(TWOLm1) > -1 && p.isProbablePrime(80)) { |
243 BigInteger tmp = (seed.add(offset).add(J)).mod(twoSl); |
247 BigInteger[] result = {p, q, SEED, |
244 byte[] vjBytes = hashObj.digest(toByteArray(tmp)); |
248 BigInteger.valueOf(counter)}; |
245 V[j] = new BigInteger(1, vjBytes); |
249 return result; |
246 } |
250 } |
247 /* Step 11.2 */ |
251 offset = offset.add(BigInteger.valueOf(n)).add(ONE); |
248 BigInteger W = V[0]; |
|
249 for (int i = 1; i < n; i++) { |
|
250 W = W.add(V[i].multiply(TWO.pow(i * outLen))); |
|
251 } |
|
252 W = W.add((V[n].mod(TWO.pow(b))).multiply(TWO.pow(n * outLen))); |
|
253 /* Step 11.3 */ |
|
254 BigInteger twoLm1 = TWO.pow(valueL - 1); |
|
255 BigInteger X = W.add(twoLm1); |
|
256 /* Step 11.4, 11.5 */ |
|
257 BigInteger c = X.mod(resultQ.multiply(TWO)); |
|
258 resultP = X.subtract(c.subtract(ONE)); |
|
259 /* Step 11.6, 11.7 */ |
|
260 if (resultP.compareTo(twoLm1) > -1 |
|
261 && resultP.isProbablePrime(primeCertainty)) { |
|
262 /* Step 11.8 */ |
|
263 BigInteger[] result = {resultP, resultQ, seed, |
|
264 BigInteger.valueOf(counter)}; |
|
265 return result; |
|
266 } |
|
267 /* Step 11.9 */ |
|
268 offset = offset.add(BigInteger.valueOf(n)).add(ONE); |
252 } |
269 } |
253 return null; |
270 } |
254 } |
271 |
255 } |
272 } |
256 |
273 |
257 /* |
274 /* |
258 * Generates the <code>g</code> parameter for DSA. |
275 * Generates the <code>g</code> parameter for DSA. |
259 * |
276 * |
260 * @param p the prime, <code>p</code>. |
277 * @param p the prime, <code>p</code>. |
261 * @param q the subprime, <code>q</code>. |
278 * @param q the subprime, <code>q</code>. |
262 * |
279 * |
263 * @param the <code>g</code> |
280 * @param the <code>g</code> |
264 */ |
281 */ |
265 BigInteger generateG(BigInteger p, BigInteger q) { |
282 private static BigInteger generateG(BigInteger p, BigInteger q) { |
266 BigInteger h = ONE; |
283 BigInteger h = ONE; |
|
284 /* Step 1 */ |
267 BigInteger pMinusOneOverQ = (p.subtract(ONE)).divide(q); |
285 BigInteger pMinusOneOverQ = (p.subtract(ONE)).divide(q); |
268 BigInteger g = ONE; |
286 BigInteger resultG = ONE; |
269 while (g.compareTo(TWO) < 0) { |
287 while (resultG.compareTo(TWO) < 0) { |
270 g = h.modPow(pMinusOneOverQ, p); |
288 /* Step 3 */ |
|
289 resultG = h.modPow(pMinusOneOverQ, p); |
271 h = h.add(ONE); |
290 h = h.add(ONE); |
272 } |
291 } |
273 return g; |
292 return resultG; |
274 } |
|
275 |
|
276 /* |
|
277 * Returns the SHA-1 digest of some data |
|
278 */ |
|
279 private byte[] SHA(byte[] array) { |
|
280 sha.engineReset(); |
|
281 sha.engineUpdate(array, 0, array.length); |
|
282 return sha.engineDigest(); |
|
283 } |
293 } |
284 |
294 |
285 /* |
295 /* |
286 * Converts the result of a BigInteger.toByteArray call to an exact |
296 * Converts the result of a BigInteger.toByteArray call to an exact |
287 * signed magnitude representation for any positive number. |
297 * signed magnitude representation for any positive number. |
288 */ |
298 */ |
289 private byte[] toByteArray(BigInteger bigInt) { |
299 private static byte[] toByteArray(BigInteger bigInt) { |
290 byte[] result = bigInt.toByteArray(); |
300 byte[] result = bigInt.toByteArray(); |
291 if (result[0] == 0) { |
301 if (result[0] == 0) { |
292 byte[] tmp = new byte[result.length - 1]; |
302 byte[] tmp = new byte[result.length - 1]; |
293 System.arraycopy(result, 1, tmp, 0, tmp.length); |
303 System.arraycopy(result, 1, tmp, 0, tmp.length); |
294 result = tmp; |
304 result = tmp; |
295 } |
305 } |
296 return result; |
306 return result; |
297 } |
307 } |
298 |
|
299 /* |
|
300 * XORs U2 into U1 |
|
301 */ |
|
302 private void xor(byte[] U1, byte[] U2) { |
|
303 for (int i = 0; i < U1.length; i++) { |
|
304 U1[i] ^= U2[i]; |
|
305 } |
|
306 } |
|
307 } |
308 } |