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