1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package javax.crypto;
19 
20 import java.security.InvalidAlgorithmParameterException;
21 import java.security.InvalidKeyException;
22 import java.security.Key;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.NoSuchProviderException;
25 import java.security.Provider;
26 import java.security.ProviderException;
27 import java.security.SecureRandom;
28 import java.security.Security;
29 import java.security.spec.AlgorithmParameterSpec;
30 import java.util.ArrayList;
31 import org.apache.harmony.security.fortress.Engine;
32 
33 /**
34  * This class provides the functionality for a key exchange protocol. This
35  * enables two or more parties to agree on a secret key for symmetric
36  * cryptography.
37  */
38 public class KeyAgreement {
39 
40     // The service name.
41     private static final String SERVICE = "KeyAgreement";
42 
43     // Used to access common engine functionality
44     private static final Engine ENGINE = new Engine(SERVICE);
45 
46     // Store SecureRandom
47     private static final SecureRandom RANDOM = new SecureRandom();
48 
49     // Store used provider
50     private Provider provider;
51 
52     // Provider that was requested during creation.
53     private final Provider specifiedProvider;
54 
55     // Store used spi implementation
56     private KeyAgreementSpi spiImpl;
57 
58     // Store used algorithm name
59     private final String algorithm;
60 
61     /**
62      * Lock held while the SPI is initializing.
63      */
64     private final Object initLock = new Object();
65 
66     /**
67      * Creates a new {@code KeyAgreement} instance.
68      *
69      * @param keyAgreeSpi
70      *            the <b>SPI</b> delegate.
71      * @param provider
72      *            the provider providing this KeyAgreement.
73      * @param algorithm
74      *            the name of the key agreement algorithm.
75      */
KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider, String algorithm)76     protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider,
77             String algorithm) {
78         this.spiImpl = keyAgreeSpi;
79         this.specifiedProvider = provider;
80         this.algorithm = algorithm;
81     }
82 
83     /**
84      * Returns the name of the key agreement algorithm.
85      *
86      * @return the name of the key agreement algorithm.
87      */
getAlgorithm()88     public final String getAlgorithm() {
89         return algorithm;
90     }
91 
92     /**
93      * Returns the provider for this {@code KeyAgreement} instance.
94      *
95      * @return the provider for this {@code KeyAgreement} instance.
96      */
getProvider()97     public final Provider getProvider() {
98         getSpi();
99         return provider;
100     }
101 
102     /**
103      * Creates a new {@code KeyAgreement} for the specified algorithm.
104      *
105      * @param algorithm
106      *            the name of the key agreement algorithm to create.
107      * @return a key agreement for the specified algorithm.
108      * @throws NoSuchAlgorithmException
109      *             if no installed provider can provide the requested algorithm.
110      * @throws NullPointerException
111      *             if the specified algorithm is {@code null}.
112      */
getInstance(String algorithm)113     public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException {
114         return getKeyAgreement(algorithm, null);
115     }
116 
117     /**
118      * Creates a new {@code KeyAgreement} for the specified algorithm from the
119      * specified provider.
120      *
121      * @param algorithm
122      *            the name of the key agreement algorithm to create.
123      * @param provider
124      *            the name of the provider that provides the requested
125      *            algorithm.
126      * @return a key agreement for the specified algorithm from the specified
127      *         provider.
128      * @throws NoSuchAlgorithmException
129      *             if the specified provider cannot provide the requested
130      *             algorithm.
131      * @throws NoSuchProviderException
132      *             if the specified provider does not exist.
133      * @throws IllegalArgumentException
134      *             if the specified provider name is {@code null} or empty.
135      */
getInstance(String algorithm, String provider)136     public static final KeyAgreement getInstance(String algorithm, String provider)
137             throws NoSuchAlgorithmException, NoSuchProviderException {
138         if (provider == null || provider.isEmpty()) {
139             throw new IllegalArgumentException("Provider is null or empty");
140         }
141         Provider impProvider = Security.getProvider(provider);
142         if (impProvider == null) {
143             throw new NoSuchProviderException(provider);
144         }
145         return getKeyAgreement(algorithm, impProvider);
146     }
147 
148     /**
149      * Create a new {@code KeyAgreement} for the specified algorithm from the
150      * specified provider. The {@code provider} supplied does not have to be
151      * registered.
152      *
153      * @param algorithm
154      *            the name of the key agreement algorithm to create.
155      * @param provider
156      *            the provider that provides the requested algorithm.
157      * @return a key agreement for the specified algorithm from the specified
158      *         provider.
159      * @throws NoSuchAlgorithmException
160      *             if the specified provider cannot provide the requested
161      *             algorithm.
162      * @throws IllegalArgumentException
163      *             if the specified provider is {@code null}.
164      * @throws NullPointerException
165      *             if the specified algorithm name is {@code null}.
166      */
getInstance(String algorithm, Provider provider)167     public static final KeyAgreement getInstance(String algorithm, Provider provider)
168             throws NoSuchAlgorithmException {
169         if (provider == null) {
170             throw new IllegalArgumentException("provider == null");
171         }
172         return getKeyAgreement(algorithm, provider);
173     }
174 
getKeyAgreement(String algorithm, Provider provider)175     private static KeyAgreement getKeyAgreement(String algorithm, Provider provider)
176             throws NoSuchAlgorithmException {
177         if (algorithm == null) {
178             throw new NullPointerException("algorithm == null");
179         }
180 
181         if (tryAlgorithm(null, provider, algorithm) == null) {
182             if (provider == null) {
183                 throw new NoSuchAlgorithmException("No provider found for " + algorithm);
184             } else {
185                 throw new NoSuchAlgorithmException("Provider " + provider.getName()
186                         + " does not provide " + algorithm);
187             }
188         }
189         return new KeyAgreement(null, provider, algorithm);
190     }
191 
tryAlgorithm(Key key, Provider provider, String algorithm)192     private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) {
193         if (provider != null) {
194             Provider.Service service = provider.getService(SERVICE, algorithm);
195             if (service == null) {
196                 return null;
197             }
198             return tryAlgorithmWithProvider(key, service);
199         }
200         ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
201         if (services == null) {
202             return null;
203         }
204         for (Provider.Service service : services) {
205             Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service);
206             if (sap != null) {
207                 return sap;
208             }
209         }
210         return null;
211     }
212 
tryAlgorithmWithProvider(Key key, Provider.Service service)213     private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) {
214         try {
215             if (key != null && !service.supportsParameter(key)) {
216                 return null;
217             }
218 
219             Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
220             if (sap.spi == null || sap.provider == null) {
221                 return null;
222             }
223             if (!(sap.spi instanceof KeyAgreementSpi)) {
224                 return null;
225             }
226             return sap;
227         } catch (NoSuchAlgorithmException ignored) {
228         }
229         return null;
230     }
231 
232     /**
233      * Makes sure a KeyAgreementSpi that matches this type is selected.
234      */
getSpi(Key key)235     private KeyAgreementSpi getSpi(Key key) {
236         synchronized (initLock) {
237             if (spiImpl != null && key == null) {
238                 return spiImpl;
239             }
240 
241             final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm);
242             if (sap == null) {
243                 throw new ProviderException("No provider for " + getAlgorithm());
244             }
245 
246             spiImpl = (KeyAgreementSpi) sap.spi;
247             provider = sap.provider;
248 
249             return spiImpl;
250         }
251     }
252 
253     /**
254      * Convenience call when the Key is not available.
255      */
getSpi()256     private KeyAgreementSpi getSpi() {
257         return getSpi(null);
258     }
259 
260     /**
261      * Initializes this {@code KeyAgreement} with the specified key.
262      *
263      * @param key the key to initialize this key agreement.
264      * @throws InvalidKeyException if the specified key cannot be used to
265      *             initialize this key agreement.
266      */
init(Key key)267     public final void init(Key key) throws InvalidKeyException {
268         getSpi(key).engineInit(key, RANDOM);//new SecureRandom());
269     }
270 
271     /**
272      * Initializes this {@code KeyAgreement} with the specified key and the
273      * specified randomness source.
274      *
275      * @param key
276      *            the key to initialize this key agreement.
277      * @param random
278      *            the source for any randomness needed.
279      * @throws InvalidKeyException
280      *             if the specified key cannot be used to initialize this key
281      *             agreement.
282      */
init(Key key, SecureRandom random)283     public final void init(Key key, SecureRandom random)
284             throws InvalidKeyException {
285         getSpi(key).engineInit(key, random);
286     }
287 
288     /**
289      * Initializes this {@code KeyAgreement} with the specified key and the
290      * algorithm parameters.
291      *
292      * @param key
293      *            the key to initialize this key agreement.
294      * @param params
295      *            the parameters for this key agreement algorithm.
296      * @throws InvalidKeyException
297      *             if the specified key cannot be used to initialize this key
298      *             agreement.
299      * @throws InvalidAlgorithmParameterException
300      *             if the specified parameters are invalid for this key
301      *             agreement algorithm.
302      */
init(Key key, AlgorithmParameterSpec params)303     public final void init(Key key, AlgorithmParameterSpec params)
304             throws InvalidKeyException, InvalidAlgorithmParameterException {
305         getSpi(key).engineInit(key, params, RANDOM);//new SecureRandom());
306     }
307 
308     /**
309      * Initializes this {@code KeyAgreement} with the specified key, algorithm
310      * parameters and randomness source.
311      *
312      * @param key
313      *            the key to initialize this key agreement.
314      * @param params
315      *            the parameters for this key agreement algorithm.
316      * @param random
317      *            the source for any randomness needed.
318      * @throws InvalidKeyException
319      *             if the specified key cannot be used to initialize this key
320      *             agreement.
321      * @throws InvalidAlgorithmParameterException
322      *             if the specified parameters are invalid for this key
323      *             agreement algorithm.
324      */
init(Key key, AlgorithmParameterSpec params, SecureRandom random)325     public final void init(Key key, AlgorithmParameterSpec params,
326             SecureRandom random) throws InvalidKeyException,
327             InvalidAlgorithmParameterException {
328         getSpi(key).engineInit(key, params, random);
329     }
330 
331     /**
332      * Does the next (or the last) phase of the key agreement, using the
333      * specified key.
334      *
335      * @param key
336      *            the key received from the other party for this phase.
337      * @param lastPhase
338      *            set to {@code true} if this is the last phase of this key
339      *            agreement.
340      * @return the intermediate key from this phase or {@code null} if there is
341      *         no intermediate key for this phase.
342      * @throws InvalidKeyException
343      *             if the specified key cannot be used in this key agreement or
344      *             this phase,
345      * @throws IllegalStateException
346      *             if this instance has not been initialized.
347      */
doPhase(Key key, boolean lastPhase)348     public final Key doPhase(Key key, boolean lastPhase)
349             throws InvalidKeyException, IllegalStateException {
350         return getSpi().engineDoPhase(key, lastPhase);
351     }
352 
353     /**
354      * Generates the shared secret.
355      *
356      * @return the generated shared secret.
357      * @throws IllegalStateException
358      *             if this key agreement is not complete.
359      */
generateSecret()360     public final byte[] generateSecret() throws IllegalStateException {
361         return getSpi().engineGenerateSecret();
362     }
363 
364     /**
365      * Generates the shared secret and stores it into the buffer {@code
366      * sharedSecred} at {@code offset}.
367      *
368      * @param sharedSecret
369      *            the buffer to store the shared secret.
370      * @param offset
371      *            the offset in the buffer.
372      * @return the number of bytes stored in the buffer.
373      * @throws IllegalStateException
374      *             if this key agreement is not complete.
375      * @throws ShortBufferException
376      *             if the specified buffer is too small for the shared secret.
377      */
generateSecret(byte[] sharedSecret, int offset)378     public final int generateSecret(byte[] sharedSecret, int offset)
379             throws IllegalStateException, ShortBufferException {
380         return getSpi().engineGenerateSecret(sharedSecret, offset);
381     }
382 
383     /**
384      * Generates the shared secret.
385      *
386      * @param algorithm
387      *            the algorithm to for the {@code SecretKey}
388      * @return the shared secret as a {@code SecretKey} of the specified
389      *         algorithm.
390      * @throws IllegalStateException
391      *             if this key agreement is not complete.
392      * @throws NoSuchAlgorithmException
393      *             if the specified algorithm for the secret key does not
394      *             exists.
395      * @throws InvalidKeyException
396      *             if a {@code SecretKey} with the specified algorithm cannot be
397      *             created using the generated shared secret.
398      */
generateSecret(String algorithm)399     public final SecretKey generateSecret(String algorithm)
400             throws IllegalStateException, NoSuchAlgorithmException,
401             InvalidKeyException {
402         return getSpi().engineGenerateSecret(algorithm);
403     }
404 
405 }
406