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