blob: 30bec7261174a6ee5ea371d2114287f3cbd369e4 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.crypto.provider;
27
28import java.util.*;
29import java.lang.*;
30import java.math.BigInteger;
31import java.security.InvalidAlgorithmParameterException;
32import java.security.InvalidKeyException;
33import java.security.Key;
34import java.security.NoSuchAlgorithmException;
35import java.security.SecureRandom;
36import java.security.spec.AlgorithmParameterSpec;
37import java.security.spec.InvalidKeySpecException;
38import javax.crypto.KeyAgreementSpi;
39import javax.crypto.ShortBufferException;
40import javax.crypto.SecretKey;
41import javax.crypto.spec.*;
42
43/**
44 * This class implements the Diffie-Hellman key agreement protocol between
45 * any number of parties.
46 *
47 * @author Jan Luehe
48 *
49 */
50
51public final class DHKeyAgreement
52extends KeyAgreementSpi {
53
54 private boolean generateSecret = false;
55 private BigInteger init_p = null;
56 private BigInteger init_g = null;
57 private BigInteger x = BigInteger.ZERO; // the private value
58 private BigInteger y = BigInteger.ZERO;
59
60 /**
61 * Verify the SunJCE provider in the constructor.
62 *
63 * @exception SecurityException if fails to verify
64 * its own integrity
65 */
66 public DHKeyAgreement() {
67 if (!SunJCE.verifySelfIntegrity(this.getClass())) {
68 throw new SecurityException("The SunJCE provider may have been " +
69 "tampered.");
70 }
71 }
72
73 /**
74 * Initializes this key agreement with the given key and source of
75 * randomness. The given key is required to contain all the algorithm
76 * parameters required for this key agreement.
77 *
78 * <p> If the key agreement algorithm requires random bytes, it gets them
79 * from the given source of randomness, <code>random</code>.
80 * However, if the underlying
81 * algorithm implementation does not require any random bytes,
82 * <code>random</code> is ignored.
83 *
84 * @param key the party's private information. For example, in the case
85 * of the Diffie-Hellman key agreement, this would be the party's own
86 * Diffie-Hellman private key.
87 * @param random the source of randomness
88 *
89 * @exception InvalidKeyException if the given key is
90 * inappropriate for this key agreement, e.g., is of the wrong type or
91 * has an incompatible algorithm type.
92 */
93 protected void engineInit(Key key, SecureRandom random)
94 throws InvalidKeyException
95 {
96 try {
97 engineInit(key, null, random);
98 } catch (InvalidAlgorithmParameterException e) {
99 // never happens, because we did not pass any parameters
100 }
101 }
102
103 /**
104 * Initializes this key agreement with the given key, set of
105 * algorithm parameters, and source of randomness.
106 *
107 * @param key the party's private information. For example, in the case
108 * of the Diffie-Hellman key agreement, this would be the party's own
109 * Diffie-Hellman private key.
110 * @param params the key agreement parameters
111 * @param random the source of randomness
112 *
113 * @exception InvalidKeyException if the given key is
114 * inappropriate for this key agreement, e.g., is of the wrong type or
115 * has an incompatible algorithm type.
116 * @exception InvalidAlgorithmParameterException if the given parameters
117 * are inappropriate for this key agreement.
118 */
119 protected void engineInit(Key key, AlgorithmParameterSpec params,
120 SecureRandom random)
121 throws InvalidKeyException, InvalidAlgorithmParameterException
122 {
123 // ignore "random" parameter, because our implementation does not
124 // require any source of randomness
125 generateSecret = false;
126 init_p = null;
127 init_g = null;
128
129 if ((params != null) && !(params instanceof DHParameterSpec)) {
130 throw new InvalidAlgorithmParameterException
131 ("Diffie-Hellman parameters expected");
132 }
133 if (!(key instanceof javax.crypto.interfaces.DHPrivateKey)) {
134 throw new InvalidKeyException("Diffie-Hellman private key "
135 + "expected");
136 }
137 javax.crypto.interfaces.DHPrivateKey dhPrivKey;
138 dhPrivKey = (javax.crypto.interfaces.DHPrivateKey)key;
139
140 // check if private key parameters are compatible with
141 // initialized ones
142 if (params != null) {
143 init_p = ((DHParameterSpec)params).getP();
144 init_g = ((DHParameterSpec)params).getG();
145 }
146 BigInteger priv_p = dhPrivKey.getParams().getP();
147 BigInteger priv_g = dhPrivKey.getParams().getG();
148 if (init_p != null && priv_p != null && !(init_p.equals(priv_p))) {
149 throw new InvalidKeyException("Incompatible parameters");
150 }
151 if (init_g != null && priv_g != null && !(init_g.equals(priv_g))) {
152 throw new InvalidKeyException("Incompatible parameters");
153 }
154 if ((init_p == null && priv_p == null)
155 || (init_g == null && priv_g == null)) {
156 throw new InvalidKeyException("Missing parameters");
157 }
158 init_p = priv_p;
159 init_g = priv_g;
160
161 // store the x value
162 this.x = dhPrivKey.getX();
163 }
164
165 /**
166 * Executes the next phase of this key agreement with the given
167 * key that was received from one of the other parties involved in this key
168 * agreement.
169 *
170 * @param key the key for this phase. For example, in the case of
171 * Diffie-Hellman between 2 parties, this would be the other party's
172 * Diffie-Hellman public key.
173 * @param lastPhase flag which indicates whether or not this is the last
174 * phase of this key agreement.
175 *
176 * @return the (intermediate) key resulting from this phase, or null if
177 * this phase does not yield a key
178 *
179 * @exception InvalidKeyException if the given key is inappropriate for
180 * this phase.
181 * @exception IllegalStateException if this key agreement has not been
182 * initialized.
183 */
184 protected Key engineDoPhase(Key key, boolean lastPhase)
185 throws InvalidKeyException, IllegalStateException
186 {
187 if (!(key instanceof javax.crypto.interfaces.DHPublicKey)) {
188 throw new InvalidKeyException("Diffie-Hellman public key "
189 + "expected");
190 }
191 javax.crypto.interfaces.DHPublicKey dhPubKey;
192 dhPubKey = (javax.crypto.interfaces.DHPublicKey)key;
193
194 if (init_p == null || init_g == null) {
195 throw new IllegalStateException("Not initialized");
196 }
197
198 // check if public key parameters are compatible with
199 // initialized ones
200 BigInteger pub_p = dhPubKey.getParams().getP();
201 BigInteger pub_g = dhPubKey.getParams().getG();
202 if (pub_p != null && !(init_p.equals(pub_p))) {
203 throw new InvalidKeyException("Incompatible parameters");
204 }
205 if (pub_g != null && !(init_g.equals(pub_g))) {
206 throw new InvalidKeyException("Incompatible parameters");
207 }
208
209 // store the y value
210 this.y = dhPubKey.getY();
211
212 // we've received a public key (from one of the other parties),
213 // so we are ready to create the secret, which may be an
214 // intermediate secret, in which case we wrap it into a
215 // Diffie-Hellman public key object and return it.
216 generateSecret = true;
217 if (lastPhase == false) {
218 byte[] intermediate = engineGenerateSecret();
219 return new DHPublicKey(new BigInteger(1, intermediate),
220 init_p, init_g);
221 } else {
222 return null;
223 }
224 }
225
226 /**
227 * Generates the shared secret and returns it in a new buffer.
228 *
229 * <p>This method resets this <code>KeyAgreementSpi</code> object,
230 * so that it
231 * can be reused for further key agreements. Unless this key agreement is
232 * reinitialized with one of the <code>engineInit</code> methods, the same
233 * private information and algorithm parameters will be used for
234 * subsequent key agreements.
235 *
236 * @return the new buffer with the shared secret
237 *
238 * @exception IllegalStateException if this key agreement has not been
239 * completed yet
240 */
241 protected byte[] engineGenerateSecret()
242 throws IllegalStateException
243 {
244 if (generateSecret == false) {
245 throw new IllegalStateException
246 ("Key agreement has not been completed yet");
247 }
248
249 // Reset the key agreement here (in case anything goes wrong)
250 generateSecret = false;
251
252 // get the modulus
253 BigInteger modulus = init_p;
254
255 BigInteger tmpResult = y.modPow(x, modulus);
256 byte[] secret = tmpResult.toByteArray();
257
258 /*
259 * BigInteger.toByteArray will sometimes put a sign byte up front, but
260 * we NEVER want one.
261 */
262 if ((tmpResult.bitLength() % 8) == 0) {
263 byte retval[] = new byte[secret.length - 1];
264 System.arraycopy(secret, 1, retval, 0, retval.length);
265 return retval;
266 } else {
267 return secret;
268 }
269 }
270
271 /**
272 * Generates the shared secret, and places it into the buffer
273 * <code>sharedSecret</code>, beginning at <code>offset</code>.
274 *
275 * <p>If the <code>sharedSecret</code> buffer is too small to hold the
276 * result, a <code>ShortBufferException</code> is thrown.
277 * In this case, this call should be repeated with a larger output buffer.
278 *
279 * <p>This method resets this <code>KeyAgreementSpi</code> object,
280 * so that it
281 * can be reused for further key agreements. Unless this key agreement is
282 * reinitialized with one of the <code>engineInit</code> methods, the same
283 * private information and algorithm parameters will be used for
284 * subsequent key agreements.
285 *
286 * @param sharedSecret the buffer for the shared secret
287 * @param offset the offset in <code>sharedSecret</code> where the
288 * shared secret will be stored
289 *
290 * @return the number of bytes placed into <code>sharedSecret</code>
291 *
292 * @exception IllegalStateException if this key agreement has not been
293 * completed yet
294 * @exception ShortBufferException if the given output buffer is too small
295 * to hold the secret
296 */
297 protected int engineGenerateSecret(byte[] sharedSecret, int offset)
298 throws IllegalStateException, ShortBufferException
299 {
300 if (generateSecret == false) {
301 throw new IllegalStateException
302 ("Key agreement has not been completed yet");
303 }
304
305 if (sharedSecret == null) {
306 throw new ShortBufferException
307 ("No buffer provided for shared secret");
308 }
309
310 BigInteger modulus = init_p;
311 byte[] secret = this.y.modPow(this.x, modulus).toByteArray();
312
313 // BigInteger.toByteArray will sometimes put a sign byte up front,
314 // but we NEVER want one.
315 if ((secret.length << 3) != modulus.bitLength()) {
316 if ((sharedSecret.length - offset) < (secret.length - 1)) {
317 throw new ShortBufferException
318 ("Buffer too short for shared secret");
319 }
320 System.arraycopy(secret, 1, sharedSecret, offset,
321 secret.length - 1);
322
323 // Reset the key agreement here (not earlier!), so that people
324 // can recover from ShortBufferException above without losing
325 // internal state
326 generateSecret = false;
327
328 return secret.length - 1;
329
330 } else {
331 if ((sharedSecret.length - offset) < secret.length) {
332 throw new ShortBufferException
333 ("Buffer too short to hold shared secret");
334 }
335 System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
336
337 // Reset the key agreement here (not earlier!), so that people
338 // can recover from ShortBufferException above without losing
339 // internal state
340 generateSecret = false;
341
342 return secret.length;
343 }
344 }
345
346 /**
347 * Creates the shared secret and returns it as a secret key object
348 * of the requested algorithm type.
349 *
350 * <p>This method resets this <code>KeyAgreementSpi</code> object,
351 * so that it
352 * can be reused for further key agreements. Unless this key agreement is
353 * reinitialized with one of the <code>engineInit</code> methods, the same
354 * private information and algorithm parameters will be used for
355 * subsequent key agreements.
356 *
357 * @param algorithm the requested secret key algorithm
358 *
359 * @return the shared secret key
360 *
361 * @exception IllegalStateException if this key agreement has not been
362 * completed yet
363 * @exception NoSuchAlgorithmException if the requested secret key
364 * algorithm is not available
365 * @exception InvalidKeyException if the shared secret key material cannot
366 * be used to generate a secret key of the requested algorithm type (e.g.,
367 * the key material is too short)
368 */
369 protected SecretKey engineGenerateSecret(String algorithm)
370 throws IllegalStateException, NoSuchAlgorithmException,
371 InvalidKeyException
372 {
373 if (algorithm == null) {
374 throw new NoSuchAlgorithmException("null algorithm");
375 }
376 byte[] secret = engineGenerateSecret();
377 if (algorithm.equalsIgnoreCase("DES")) {
378 // DES
379 return new DESKey(secret);
380 } else if (algorithm.equalsIgnoreCase("DESede")
381 || algorithm.equalsIgnoreCase("TripleDES")) {
382 // Triple DES
383 return new DESedeKey(secret);
384 } else if (algorithm.equalsIgnoreCase("Blowfish")) {
385 // Blowfish
386 int keysize = secret.length;
387 if (keysize >= BlowfishConstants.BLOWFISH_MAX_KEYSIZE)
388 keysize = BlowfishConstants.BLOWFISH_MAX_KEYSIZE;
389 SecretKeySpec skey = new SecretKeySpec(secret, 0, keysize,
390 "Blowfish");
391 return skey;
392 } else if (algorithm.equalsIgnoreCase("AES")) {
393 // AES
394 int keysize = secret.length;
395 SecretKeySpec skey = null;
396 int idx = AESConstants.AES_KEYSIZES.length - 1;
397 while (skey == null && idx >= 0) {
398 // Generate the strongest key using the shared secret
399 // assuming the key sizes in AESConstants class are
400 // in ascending order
401 if (keysize >= AESConstants.AES_KEYSIZES[idx]) {
402 keysize = AESConstants.AES_KEYSIZES[idx];
403 skey = new SecretKeySpec(secret, 0, keysize, "AES");
404 }
405 idx--;
406 }
407 if (skey == null) {
408 throw new InvalidKeyException("Key material is too short");
409 }
410 return skey;
411 } else if (algorithm.equals("TlsPremasterSecret")) {
412 // return entire secret
413 return new SecretKeySpec(secret, "TlsPremasterSecret");
414 } else {
415 throw new NoSuchAlgorithmException("Unsupported secret key "
416 + "algorithm: "+ algorithm);
417 }
418 }
419}