1 /*
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
26 package javax.crypto;
27
28 import java.util;
29
30 import java.security;
31 import java.security.Provider.Service;
32 import java.security.spec;
33
34 import sun.security.util.Debug;
35 import sun.security.jca;
36 import sun.security.jca.GetInstance.Instance;
37
38 /**
39 * This class provides the functionality of a key agreement (or key
40 * exchange) protocol.
41 * <p>
42 * The keys involved in establishing a shared secret are created by one of the
43 * key generators (<code>KeyPairGenerator</code> or
44 * <code>KeyGenerator</code>), a <code>KeyFactory</code>, or as a result from
45 * an intermediate phase of the key agreement protocol.
46 *
47 * <p> For each of the correspondents in the key exchange, <code>doPhase</code>
48 * needs to be called. For example, if this key exchange is with one other
49 * party, <code>doPhase</code> needs to be called once, with the
50 * <code>lastPhase</code> flag set to <code>true</code>.
51 * If this key exchange is
52 * with two other parties, <code>doPhase</code> needs to be called twice,
53 * the first time setting the <code>lastPhase</code> flag to
54 * <code>false</code>, and the second time setting it to <code>true</code>.
55 * There may be any number of parties involved in a key exchange.
56 *
57 * @author Jan Luehe
58 *
59 * @see KeyGenerator
60 * @see SecretKey
61 * @since 1.4
62 */
63
64 public class KeyAgreement {
65
66 private static final Debug debug =
67 Debug.getInstance("jca", "KeyAgreement");
68
69 // The provider
70 private Provider provider;
71
72 // The provider implementation (delegate)
73 private KeyAgreementSpi spi;
74
75 // The name of the key agreement algorithm.
76 private final String algorithm;
77
78 // next service to try in provider selection
79 // null once provider is selected
80 private Service firstService;
81
82 // remaining services to try in provider selection
83 // null once provider is selected
84 private Iterator serviceIterator;
85
86 private final Object lock;
87
88 /**
89 * Creates a KeyAgreement object.
90 *
91 * @param keyAgreeSpi the delegate
92 * @param provider the provider
93 * @param algorithm the algorithm
94 */
95 protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider,
96 String algorithm) {
97 this.spi = keyAgreeSpi;
98 this.provider = provider;
99 this.algorithm = algorithm;
100 lock = null;
101 }
102
103 private KeyAgreement(Service s, Iterator t, String algorithm) {
104 firstService = s;
105 serviceIterator = t;
106 this.algorithm = algorithm;
107 lock = new Object();
108 }
109
110 /**
111 * Returns the algorithm name of this <code>KeyAgreement</code> object.
112 *
113 * <p>This is the same name that was specified in one of the
114 * <code>getInstance</code> calls that created this
115 * <code>KeyAgreement</code> object.
116 *
117 * @return the algorithm name of this <code>KeyAgreement</code> object.
118 */
119 public final String getAlgorithm() {
120 return this.algorithm;
121 }
122
123 /**
124 * Returns a <code>KeyAgreement</code> object that implements the
125 * specified key agreement algorithm.
126 *
127 * <p> This method traverses the list of registered security Providers,
128 * starting with the most preferred Provider.
129 * A new KeyAgreement object encapsulating the
130 * KeyAgreementSpi implementation from the first
131 * Provider that supports the specified algorithm is returned.
132 *
133 * <p> Note that the list of registered providers may be retrieved via
134 * the {@link Security#getProviders() Security.getProviders()} method.
135 *
136 * @param algorithm the standard name of the requested key agreement
137 * algorithm.
138 * See Appendix A in the
139 * <a href=
140 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
141 * Java Cryptography Architecture Reference Guide</a>
142 * for information about standard algorithm names.
143 *
144 * @return the new <code>KeyAgreement</code> object.
145 *
146 * @exception NullPointerException if the specified algorithm
147 * is null.
148 *
149 * @exception NoSuchAlgorithmException if no Provider supports a
150 * KeyAgreementSpi implementation for the
151 * specified algorithm.
152 *
153 * @see java.security.Provider
154 */
155 public static final KeyAgreement getInstance(String algorithm)
156 throws NoSuchAlgorithmException {
157 List services = GetInstance.getServices("KeyAgreement", algorithm);
158 // make sure there is at least one service from a signed provider
159 Iterator t = services.iterator();
160 while (t.hasNext()) {
161 Service s = (Service)t.next();
162 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
163 continue;
164 }
165 return new KeyAgreement(s, t, algorithm);
166 }
167 throw new NoSuchAlgorithmException
168 ("Algorithm " + algorithm + " not available");
169 }
170
171 /**
172 * Returns a <code>KeyAgreement</code> object that implements the
173 * specified key agreement algorithm.
174 *
175 * <p> A new KeyAgreement object encapsulating the
176 * KeyAgreementSpi implementation from the specified provider
177 * is returned. The specified provider must be registered
178 * in the security provider list.
179 *
180 * <p> Note that the list of registered providers may be retrieved via
181 * the {@link Security#getProviders() Security.getProviders()} method.
182 *
183 * @param algorithm the standard name of the requested key agreement
184 * algorithm.
185 * See Appendix A in the
186 * <a href=
187 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
188 * Java Cryptography Architecture Reference Guide</a>
189 * for information about standard algorithm names.
190 *
191 * @param provider the name of the provider.
192 *
193 * @return the new <code>KeyAgreement</code> object.
194 *
195 * @exception NullPointerException if the specified algorithm
196 * is null.
197 *
198 * @exception NoSuchAlgorithmException if a KeyAgreementSpi
199 * implementation for the specified algorithm is not
200 * available from the specified provider.
201 *
202 * @exception NoSuchProviderException if the specified provider is not
203 * registered in the security provider list.
204 *
205 * @exception IllegalArgumentException if the <code>provider</code>
206 * is null or empty.
207 *
208 * @see java.security.Provider
209 */
210 public static final KeyAgreement getInstance(String algorithm,
211 String provider) throws NoSuchAlgorithmException,
212 NoSuchProviderException {
213 Instance instance = JceSecurity.getInstance
214 ("KeyAgreement", KeyAgreementSpi.class, algorithm, provider);
215 return new KeyAgreement((KeyAgreementSpi)instance.impl,
216 instance.provider, algorithm);
217 }
218
219 /**
220 * Returns a <code>KeyAgreement</code> object that implements the
221 * specified key agreement algorithm.
222 *
223 * <p> A new KeyAgreement object encapsulating the
224 * KeyAgreementSpi implementation from the specified Provider
225 * object is returned. Note that the specified Provider object
226 * does not have to be registered in the provider list.
227 *
228 * @param algorithm the standard name of the requested key agreement
229 * algorithm.
230 * See Appendix A in the
231 * <a href=
232 * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
233 * Java Cryptography Architecture Reference Guide</a>
234 * for information about standard algorithm names.
235 *
236 * @param provider the provider.
237 *
238 * @return the new <code>KeyAgreement</code> object.
239 *
240 * @exception NullPointerException if the specified algorithm
241 * is null.
242 *
243 * @exception NoSuchAlgorithmException if a KeyAgreementSpi
244 * implementation for the specified algorithm is not available
245 * from the specified Provider object.
246 *
247 * @exception IllegalArgumentException if the <code>provider</code>
248 * is null.
249 *
250 * @see java.security.Provider
251 */
252 public static final KeyAgreement getInstance(String algorithm,
253 Provider provider) throws NoSuchAlgorithmException {
254 Instance instance = JceSecurity.getInstance
255 ("KeyAgreement", KeyAgreementSpi.class, algorithm, provider);
256 return new KeyAgreement((KeyAgreementSpi)instance.impl,
257 instance.provider, algorithm);
258 }
259
260 // max number of debug warnings to print from chooseFirstProvider()
261 private static int warnCount = 10;
262
263 /**
264 * Choose the Spi from the first provider available. Used if
265 * delayed provider selection is not possible because init()
266 * is not the first method called.
267 */
268 void chooseFirstProvider() {
269 if (spi != null) {
270 return;
271 }
272 synchronized (lock) {
273 if (spi != null) {
274 return;
275 }
276 if (debug != null) {
277 int w = --warnCount;
278 if (w >= 0) {
279 debug.println("KeyAgreement.init() not first method "
280 + "called, disabling delayed provider selection");
281 if (w == 0) {
282 debug.println("Further warnings of this type will "
283 + "be suppressed");
284 }
285 new Exception("Call trace").printStackTrace();
286 }
287 }
288 Exception lastException = null;
289 while ((firstService != null) || serviceIterator.hasNext()) {
290 Service s;
291 if (firstService != null) {
292 s = firstService;
293 firstService = null;
294 } else {
295 s = (Service)serviceIterator.next();
296 }
297 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
298 continue;
299 }
300 try {
301 Object obj = s.newInstance(null);
302 if (obj instanceof KeyAgreementSpi == false) {
303 continue;
304 }
305 spi = (KeyAgreementSpi)obj;
306 provider = s.getProvider();
307 // not needed any more
308 firstService = null;
309 serviceIterator = null;
310 return;
311 } catch (Exception e) {
312 lastException = e;
313 }
314 }
315 ProviderException e = new ProviderException
316 ("Could not construct KeyAgreementSpi instance");
317 if (lastException != null) {
318 e.initCause(lastException);
319 }
320 throw e;
321 }
322 }
323
324 private final static int I_NO_PARAMS = 1;
325 private final static int I_PARAMS = 2;
326
327 private void implInit(KeyAgreementSpi spi, int type, Key key,
328 AlgorithmParameterSpec params, SecureRandom random)
329 throws InvalidKeyException, InvalidAlgorithmParameterException {
330 if (type == I_NO_PARAMS) {
331 spi.engineInit(key, random);
332 } else { // I_PARAMS
333 spi.engineInit(key, params, random);
334 }
335 }
336
337 private void chooseProvider(int initType, Key key,
338 AlgorithmParameterSpec params, SecureRandom random)
339 throws InvalidKeyException, InvalidAlgorithmParameterException {
340 synchronized (lock) {
341 if (spi != null) {
342 implInit(spi, initType, key, params, random);
343 return;
344 }
345 Exception lastException = null;
346 while ((firstService != null) || serviceIterator.hasNext()) {
347 Service s;
348 if (firstService != null) {
349 s = firstService;
350 firstService = null;
351 } else {
352 s = (Service)serviceIterator.next();
353 }
354 // if provider says it does not support this key, ignore it
355 if (s.supportsParameter(key) == false) {
356 continue;
357 }
358 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
359 continue;
360 }
361 try {
362 KeyAgreementSpi spi = (KeyAgreementSpi)s.newInstance(null);
363 implInit(spi, initType, key, params, random);
364 provider = s.getProvider();
365 this.spi = spi;
366 firstService = null;
367 serviceIterator = null;
368 return;
369 } catch (Exception e) {
370 // NoSuchAlgorithmException from newInstance()
371 // InvalidKeyException from init()
372 // RuntimeException (ProviderException) from init()
373 if (lastException == null) {
374 lastException = e;
375 }
376 }
377 }
378 // no working provider found, fail
379 if (lastException instanceof InvalidKeyException) {
380 throw (InvalidKeyException)lastException;
381 }
382 if (lastException instanceof InvalidAlgorithmParameterException) {
383 throw (InvalidAlgorithmParameterException)lastException;
384 }
385 if (lastException instanceof RuntimeException) {
386 throw (RuntimeException)lastException;
387 }
388 String kName = (key != null) ? key.getClass().getName() : "(null)";
389 throw new InvalidKeyException
390 ("No installed provider supports this key: "
391 + kName, lastException);
392 }
393 }
394
395 /**
396 * Returns the provider of this <code>KeyAgreement</code> object.
397 *
398 * @return the provider of this <code>KeyAgreement</code> object
399 */
400 public final Provider getProvider() {
401 chooseFirstProvider();
402 return this.provider;
403 }
404
405 /**
406 * Initializes this key agreement with the given key, which is required to
407 * contain all the algorithm parameters required for this key agreement.
408 *
409 * <p> If this key agreement requires any random bytes, it will get
410 * them using the
411 * {@link SecureRandom <code>SecureRandom</code>}
412 * implementation of the highest-priority
413 * installed provider as the source of randomness.
414 * (If none of the installed providers supply an implementation of
415 * SecureRandom, a system-provided source of randomness will be used.)
416 *
417 * @param key the party's private information. For example, in the case
418 * of the Diffie-Hellman key agreement, this would be the party's own
419 * Diffie-Hellman private key.
420 *
421 * @exception InvalidKeyException if the given key is
422 * inappropriate for this key agreement, e.g., is of the wrong type or
423 * has an incompatible algorithm type.
424 */
425 public final void init(Key key) throws InvalidKeyException {
426 init(key, JceSecurity.RANDOM);
427 }
428
429 /**
430 * Initializes this key agreement with the given key and source of
431 * randomness. The given key is required to contain all the algorithm
432 * parameters required for this key agreement.
433 *
434 * <p> If the key agreement algorithm requires random bytes, it gets them
435 * from the given source of randomness, <code>random</code>.
436 * However, if the underlying
437 * algorithm implementation does not require any random bytes,
438 * <code>random</code> is ignored.
439 *
440 * @param key the party's private information. For example, in the case
441 * of the Diffie-Hellman key agreement, this would be the party's own
442 * Diffie-Hellman private key.
443 * @param random the source of randomness
444 *
445 * @exception InvalidKeyException if the given key is
446 * inappropriate for this key agreement, e.g., is of the wrong type or
447 * has an incompatible algorithm type.
448 */
449 public final void init(Key key, SecureRandom random)
450 throws InvalidKeyException {
451 if (spi != null) {
452 spi.engineInit(key, random);
453 } else {
454 try {
455 chooseProvider(I_NO_PARAMS, key, null, random);
456 } catch (InvalidAlgorithmParameterException e) {
457 // should never occur
458 throw new InvalidKeyException(e);
459 }
460 }
461 }
462
463 /**
464 * Initializes this key agreement with the given key and set of
465 * algorithm parameters.
466 *
467 * <p> If this key agreement requires any random bytes, it will get
468 * them using the
469 * {@link SecureRandom <code>SecureRandom</code>}
470 * implementation of the highest-priority
471 * installed provider as the source of randomness.
472 * (If none of the installed providers supply an implementation of
473 * SecureRandom, a system-provided source of randomness will be used.)
474 *
475 * @param key the party's private information. For example, in the case
476 * of the Diffie-Hellman key agreement, this would be the party's own
477 * Diffie-Hellman private key.
478 * @param params the key agreement parameters
479 *
480 * @exception InvalidKeyException if the given key is
481 * inappropriate for this key agreement, e.g., is of the wrong type or
482 * has an incompatible algorithm type.
483 * @exception InvalidAlgorithmParameterException if the given parameters
484 * are inappropriate for this key agreement.
485 */
486 public final void init(Key key, AlgorithmParameterSpec params)
487 throws InvalidKeyException, InvalidAlgorithmParameterException
488 {
489 init(key, params, JceSecurity.RANDOM);
490 }
491
492 /**
493 * Initializes this key agreement with the given key, set of
494 * algorithm parameters, and source of randomness.
495 *
496 * @param key the party's private information. For example, in the case
497 * of the Diffie-Hellman key agreement, this would be the party's own
498 * Diffie-Hellman private key.
499 * @param params the key agreement parameters
500 * @param random the source of randomness
501 *
502 * @exception InvalidKeyException if the given key is
503 * inappropriate for this key agreement, e.g., is of the wrong type or
504 * has an incompatible algorithm type.
505 * @exception InvalidAlgorithmParameterException if the given parameters
506 * are inappropriate for this key agreement.
507 */
508 public final void init(Key key, AlgorithmParameterSpec params,
509 SecureRandom random)
510 throws InvalidKeyException, InvalidAlgorithmParameterException
511 {
512 if (spi != null) {
513 spi.engineInit(key, params, random);
514 } else {
515 chooseProvider(I_PARAMS, key, params, random);
516 }
517 }
518
519 /**
520 * Executes the next phase of this key agreement with the given
521 * key that was received from one of the other parties involved in this key
522 * agreement.
523 *
524 * @param key the key for this phase. For example, in the case of
525 * Diffie-Hellman between 2 parties, this would be the other party's
526 * Diffie-Hellman public key.
527 * @param lastPhase flag which indicates whether or not this is the last
528 * phase of this key agreement.
529 *
530 * @return the (intermediate) key resulting from this phase, or null
531 * if this phase does not yield a key
532 *
533 * @exception InvalidKeyException if the given key is inappropriate for
534 * this phase.
535 * @exception IllegalStateException if this key agreement has not been
536 * initialized.
537 */
538 public final Key doPhase(Key key, boolean lastPhase)
539 throws InvalidKeyException, IllegalStateException
540 {
541 chooseFirstProvider();
542 return spi.engineDoPhase(key, lastPhase);
543 }
544
545 /**
546 * Generates the shared secret and returns it in a new buffer.
547 *
548 * <p>This method resets this <code>KeyAgreement</code> object, so that it
549 * can be reused for further key agreements. Unless this key agreement is
550 * reinitialized with one of the <code>init</code> methods, the same
551 * private information and algorithm parameters will be used for
552 * subsequent key agreements.
553 *
554 * @return the new buffer with the shared secret
555 *
556 * @exception IllegalStateException if this key agreement has not been
557 * completed yet
558 */
559 public final byte[] generateSecret() throws IllegalStateException {
560 chooseFirstProvider();
561 return spi.engineGenerateSecret();
562 }
563
564 /**
565 * Generates the shared secret, and places it into the buffer
566 * <code>sharedSecret</code>, beginning at <code>offset</code> inclusive.
567 *
568 * <p>If the <code>sharedSecret</code> buffer is too small to hold the
569 * result, a <code>ShortBufferException</code> is thrown.
570 * In this case, this call should be repeated with a larger output buffer.
571 *
572 * <p>This method resets this <code>KeyAgreement</code> object, so that it
573 * can be reused for further key agreements. Unless this key agreement is
574 * reinitialized with one of the <code>init</code> methods, the same
575 * private information and algorithm parameters will be used for
576 * subsequent key agreements.
577 *
578 * @param sharedSecret the buffer for the shared secret
579 * @param offset the offset in <code>sharedSecret</code> where the
580 * shared secret will be stored
581 *
582 * @return the number of bytes placed into <code>sharedSecret</code>
583 *
584 * @exception IllegalStateException if this key agreement has not been
585 * completed yet
586 * @exception ShortBufferException if the given output buffer is too small
587 * to hold the secret
588 */
589 public final int generateSecret(byte[] sharedSecret, int offset)
590 throws IllegalStateException, ShortBufferException
591 {
592 chooseFirstProvider();
593 return spi.engineGenerateSecret(sharedSecret, offset);
594 }
595
596 /**
597 * Creates the shared secret and returns it as a <code>SecretKey</code>
598 * object of the specified algorithm.
599 *
600 * <p>This method resets this <code>KeyAgreement</code> object, so that it
601 * can be reused for further key agreements. Unless this key agreement is
602 * reinitialized with one of the <code>init</code> methods, the same
603 * private information and algorithm parameters will be used for
604 * subsequent key agreements.
605 *
606 * @param algorithm the requested secret-key algorithm
607 *
608 * @return the shared secret key
609 *
610 * @exception IllegalStateException if this key agreement has not been
611 * completed yet
612 * @exception NoSuchAlgorithmException if the specified secret-key
613 * algorithm is not available
614 * @exception InvalidKeyException if the shared secret-key material cannot
615 * be used to generate a secret key of the specified algorithm (e.g.,
616 * the key material is too short)
617 */
618 public final SecretKey generateSecret(String algorithm)
619 throws IllegalStateException, NoSuchAlgorithmException,
620 InvalidKeyException
621 {
622 chooseFirstProvider();
623 return spi.engineGenerateSecret(algorithm);
624 }
625 }