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 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.jca.*;
35 import sun.security.jca.GetInstance.Instance;
36 
37 /**
38  * This class represents a factory for secret keys.
39  *
40  * <P> Key factories are used to convert <I>keys</I> (opaque
41  * cryptographic keys of type {@code Key}) into <I>key specifications</I>
42  * (transparent representations of the underlying key material), and vice
43  * versa.
44  * Secret key factories operate only on secret (symmetric) keys.
45  *
46  * <P> Key factories are bi-directional, i.e., they allow to build an opaque
47  * key object from a given key specification (key material), or to retrieve
48  * the underlying key material of a key object in a suitable format.
49  *
50  * <P> Application developers should refer to their provider's documentation
51  * to find out which key specifications are supported by the
52  * {@link #generateSecret(java.security.spec.KeySpec) generateSecret} and
53  * {@link #getKeySpec(javax.crypto.SecretKey, java.lang.Class) getKeySpec}
54  * methods.
55  * For example, the DES secret-key factory supplied by the "SunJCE" provider
56  * supports {@code DESKeySpec} as a transparent representation of DES
57  * keys, and that provider's secret-key factory for Triple DES keys supports
58  * {@code DESedeKeySpec} as a transparent representation of Triple DES
59  * keys.
60  *
61  * <p> Every implementation of the Java platform is required to support the
62  * following standard {@code SecretKeyFactory} algorithms:
63  * <ul>
64  * <li>{@code DES}</li>
65  * <li>{@code DESede}</li>
66  * </ul>
67  * These algorithms are described in the <a href=
68  * "{@docRoot}/../specs/security/standard-names.html#secretkeyfactory-algorithms">
69  * SecretKeyFactory section</a> of the
70  * Java Security Standard Algorithm Names Specification.
71  * Consult the release documentation for your implementation to see if any
72  * other algorithms are supported.
73  *
74  * @author Jan Luehe
75  *
76  * @see SecretKey
77  * @see javax.crypto.spec.DESKeySpec
78  * @see javax.crypto.spec.DESedeKeySpec
79  * @see javax.crypto.spec.PBEKeySpec
80  * @since 1.4
81  */
82 
83 public class SecretKeyFactory {
84 
85     // The provider
86     private Provider provider;
87 
88     // The algorithm associated with this factory
89     private final String algorithm;
90 
91     // The provider implementation (delegate)
92     private volatile SecretKeyFactorySpi spi;
93 
94     // lock for mutex during provider selection
95     private final Object lock = new Object();
96 
97     // remaining services to try in provider selection
98     // null once provider is selected
99     private Iterator<Service> serviceIterator;
100 
101     /**
102      * Creates a SecretKeyFactory object.
103      *
104      * @param keyFacSpi the delegate
105      * @param provider the provider
106      * @param algorithm the secret-key algorithm
107      */
SecretKeyFactory(SecretKeyFactorySpi keyFacSpi, Provider provider, String algorithm)108     protected SecretKeyFactory(SecretKeyFactorySpi keyFacSpi,
109                                Provider provider, String algorithm) {
110         this.spi = keyFacSpi;
111         this.provider = provider;
112         this.algorithm = algorithm;
113     }
114 
SecretKeyFactory(String algorithm)115     private SecretKeyFactory(String algorithm) throws NoSuchAlgorithmException {
116         this.algorithm = algorithm;
117         List<Service> list =
118                 GetInstance.getServices("SecretKeyFactory", algorithm);
119         serviceIterator = list.iterator();
120         // fetch and instantiate initial spi
121         if (nextSpi(null) == null) {
122             throw new NoSuchAlgorithmException
123                 (algorithm + " SecretKeyFactory not available");
124         }
125     }
126 
127     /**
128      * Returns a {@code SecretKeyFactory} object that converts
129      * secret keys of the specified algorithm.
130      *
131      * <p> This method traverses the list of registered security Providers,
132      * starting with the most preferred Provider.
133      * A new SecretKeyFactory object encapsulating the
134      * SecretKeyFactorySpi implementation from the first
135      * Provider that supports the specified algorithm is returned.
136      *
137      * <p> Note that the list of registered providers may be retrieved via
138      * the {@link Security#getProviders() Security.getProviders()} method.
139      *
140      * @implNote
141      * The JDK Reference Implementation additionally uses the
142      * {@code jdk.security.provider.preferred}
143      * {@link Security#getProperty(String) Security} property to determine
144      * the preferred provider order for the specified algorithm. This
145      * may be different than the order of providers returned by
146      * {@link Security#getProviders() Security.getProviders()}.
147      *
148      * @param algorithm the standard name of the requested secret-key
149      * algorithm.
150      * See the SecretKeyFactory section in the <a href=
151      * "{@docRoot}/../specs/security/standard-names.html#secretkeyfactory-algorithms">
152      * Java Security Standard Algorithm Names Specification</a>
153      * for information about standard algorithm names.
154      *
155      * @return the new {@code SecretKeyFactory} object
156      *
157      * @throws NoSuchAlgorithmException if no {@code Provider} supports a
158      *         {@code SecretKeyFactorySpi} implementation for the
159      *         specified algorithm
160      *
161      * @throws NullPointerException if {@code algorithm} is {@code null}
162      *
163      * @see java.security.Provider
164      */
getInstance(String algorithm)165     public static final SecretKeyFactory getInstance(String algorithm)
166             throws NoSuchAlgorithmException {
167         Objects.requireNonNull(algorithm, "null algorithm name");
168         return new SecretKeyFactory(algorithm);
169     }
170 
171     /**
172      * Returns a {@code SecretKeyFactory} object that converts
173      * secret keys of the specified algorithm.
174      *
175      * <p> A new SecretKeyFactory object encapsulating the
176      * SecretKeyFactorySpi 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 secret-key
184      * algorithm.
185      * See the SecretKeyFactory section in the <a href=
186      * "{@docRoot}/../specs/security/standard-names.html#secretkeyfactory-algorithms">
187      * Java Security Standard Algorithm Names Specification</a>
188      * for information about standard algorithm names.
189      *
190      * @param provider the name of the provider.
191      *
192      * @return the new {@code SecretKeyFactory} object
193      *
194      * @throws IllegalArgumentException if the {@code provider}
195      *         is {@code null} or empty
196      *
197      * @throws NoSuchAlgorithmException if a {@code SecretKeyFactorySpi}
198      *         implementation for the specified algorithm is not
199      *         available from the specified provider
200      *
201      * @throws NoSuchProviderException if the specified provider is not
202      *         registered in the security provider list
203      *
204      * @throws NullPointerException if {@code algorithm} is {@code null}
205      *
206      * @see java.security.Provider
207      */
getInstance(String algorithm, String provider)208     public static final SecretKeyFactory getInstance(String algorithm,
209             String provider) throws NoSuchAlgorithmException,
210             NoSuchProviderException {
211         Objects.requireNonNull(algorithm, "null algorithm name");
212         Instance instance = JceSecurity.getInstance("SecretKeyFactory",
213                 SecretKeyFactorySpi.class, algorithm, provider);
214         return new SecretKeyFactory((SecretKeyFactorySpi)instance.impl,
215                 instance.provider, algorithm);
216     }
217 
218     /**
219      * Returns a {@code SecretKeyFactory} object that converts
220      * secret keys of the specified algorithm.
221      *
222      * <p> A new SecretKeyFactory object encapsulating the
223      * SecretKeyFactorySpi implementation from the specified Provider
224      * object is returned.  Note that the specified Provider object
225      * does not have to be registered in the provider list.
226      *
227      * @param algorithm the standard name of the requested secret-key
228      * algorithm.
229      * See the SecretKeyFactory section in the <a href=
230      * "{@docRoot}/../specs/security/standard-names.html#secretkeyfactory-algorithms">
231      * Java Security Standard Algorithm Names Specification</a>
232      * for information about standard algorithm names.
233      *
234      * @param provider the provider.
235      *
236      * @return the new {@code SecretKeyFactory} object
237      *
238      * @throws IllegalArgumentException if the {@code provider}
239      *         is {@code null}
240      *
241      * @throws NoSuchAlgorithmException if a {@code SecretKeyFactorySpi}
242      *         implementation for the specified algorithm is not available
243      *         from the specified {@code Provider} object
244      *
245      * @throws NullPointerException if {@code algorithm} is {@code null}
246      *
247      * @see java.security.Provider
248      */
getInstance(String algorithm, Provider provider)249     public static final SecretKeyFactory getInstance(String algorithm,
250             Provider provider) throws NoSuchAlgorithmException {
251         Objects.requireNonNull(algorithm, "null algorithm name");
252         Instance instance = JceSecurity.getInstance("SecretKeyFactory",
253                 SecretKeyFactorySpi.class, algorithm, provider);
254         return new SecretKeyFactory((SecretKeyFactorySpi)instance.impl,
255                 instance.provider, algorithm);
256     }
257 
258     /**
259      * Returns the provider of this {@code SecretKeyFactory} object.
260      *
261      * @return the provider of this {@code SecretKeyFactory} object
262      */
getProvider()263     public final Provider getProvider() {
264         synchronized (lock) {
265             // disable further failover after this call
266             serviceIterator = null;
267             return provider;
268         }
269     }
270 
271     /**
272      * Returns the algorithm name of this {@code SecretKeyFactory} object.
273      *
274      * <p>This is the same name that was specified in one of the
275      * {@code getInstance} calls that created this
276      * {@code SecretKeyFactory} object.
277      *
278      * @return the algorithm name of this {@code SecretKeyFactory}
279      * object.
280      */
getAlgorithm()281     public final String getAlgorithm() {
282         return this.algorithm;
283     }
284 
285     /**
286      * Update the active spi of this class and return the next
287      * implementation for failover. If no more implemenations are
288      * available, this method returns null. However, the active spi of
289      * this class is never set to null.
290      */
nextSpi(SecretKeyFactorySpi oldSpi)291     private SecretKeyFactorySpi nextSpi(SecretKeyFactorySpi oldSpi) {
292         synchronized (lock) {
293             // somebody else did a failover concurrently
294             // try that spi now
295             if ((oldSpi != null) && (oldSpi != spi)) {
296                 return spi;
297             }
298             if (serviceIterator == null) {
299                 return null;
300             }
301             while (serviceIterator.hasNext()) {
302                 Service s = serviceIterator.next();
303                 if (JceSecurity.canUseProvider(s.getProvider()) == false) {
304                     continue;
305                 }
306                 try {
307                     Object obj = s.newInstance(null);
308                     if (obj instanceof SecretKeyFactorySpi == false) {
309                         continue;
310                     }
311                     SecretKeyFactorySpi spi = (SecretKeyFactorySpi)obj;
312                     provider = s.getProvider();
313                     this.spi = spi;
314                     return spi;
315                 } catch (NoSuchAlgorithmException e) {
316                     // ignore
317                 }
318             }
319             serviceIterator = null;
320             return null;
321         }
322     }
323 
324     /**
325      * Generates a {@code SecretKey} object from the provided key
326      * specification (key material).
327      *
328      * @param keySpec the specification (key material) of the secret key
329      *
330      * @return the secret key
331      *
332      * @exception InvalidKeySpecException if the given key specification
333      * is inappropriate for this secret-key factory to produce a secret key.
334      */
generateSecret(KeySpec keySpec)335     public final SecretKey generateSecret(KeySpec keySpec)
336             throws InvalidKeySpecException {
337         if (serviceIterator == null) {
338             return spi.engineGenerateSecret(keySpec);
339         }
340         Exception failure = null;
341         SecretKeyFactorySpi mySpi = spi;
342         do {
343             try {
344                 return mySpi.engineGenerateSecret(keySpec);
345             } catch (Exception e) {
346                 if (failure == null) {
347                     failure = e;
348                 }
349                 mySpi = nextSpi(mySpi);
350             }
351         } while (mySpi != null);
352         if (failure instanceof InvalidKeySpecException) {
353             throw (InvalidKeySpecException)failure;
354         }
355         throw new InvalidKeySpecException
356                 ("Could not generate secret key", failure);
357     }
358 
359     /**
360      * Returns a specification (key material) of the given key object
361      * in the requested format.
362      *
363      * @param key the key
364      * @param keySpec the requested format in which the key material shall be
365      * returned
366      *
367      * @return the underlying key specification (key material) in the
368      * requested format
369      *
370      * @exception InvalidKeySpecException if the requested key specification is
371      * inappropriate for the given key (e.g., the algorithms associated with
372      * {@code key} and {@code keySpec} do not match, or
373      * {@code key} references a key on a cryptographic hardware device
374      * whereas {@code keySpec} is the specification of a software-based
375      * key), or the given key cannot be dealt with
376      * (e.g., the given key has an algorithm or format not supported by this
377      * secret-key factory).
378      */
getKeySpec(SecretKey key, Class<?> keySpec)379     public final KeySpec getKeySpec(SecretKey key, Class<?> keySpec)
380             throws InvalidKeySpecException {
381         if (serviceIterator == null) {
382             return spi.engineGetKeySpec(key, keySpec);
383         }
384         Exception failure = null;
385         SecretKeyFactorySpi mySpi = spi;
386         do {
387             try {
388                 return mySpi.engineGetKeySpec(key, keySpec);
389             } catch (Exception e) {
390                 if (failure == null) {
391                     failure = e;
392                 }
393                 mySpi = nextSpi(mySpi);
394             }
395         } while (mySpi != null);
396         if (failure instanceof InvalidKeySpecException) {
397             throw (InvalidKeySpecException)failure;
398         }
399         throw new InvalidKeySpecException
400                 ("Could not get key spec", failure);
401     }
402 
403     /**
404      * Translates a key object, whose provider may be unknown or potentially
405      * untrusted, into a corresponding key object of this secret-key factory.
406      *
407      * @param key the key whose provider is unknown or untrusted
408      *
409      * @return the translated key
410      *
411      * @exception InvalidKeyException if the given key cannot be processed
412      * by this secret-key factory.
413      */
translateKey(SecretKey key)414     public final SecretKey translateKey(SecretKey key)
415             throws InvalidKeyException {
416         if (serviceIterator == null) {
417             return spi.engineTranslateKey(key);
418         }
419         Exception failure = null;
420         SecretKeyFactorySpi mySpi = spi;
421         do {
422             try {
423                 return mySpi.engineTranslateKey(key);
424             } catch (Exception e) {
425                 if (failure == null) {
426                     failure = e;
427                 }
428                 mySpi = nextSpi(mySpi);
429             }
430         } while (mySpi != null);
431         if (failure instanceof InvalidKeyException) {
432             throw (InvalidKeyException)failure;
433         }
434         throw new InvalidKeyException
435                 ("Could not translate key", failure);
436     }
437 }
438