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
18package javax.crypto;
19
20import java.nio.ByteBuffer;
21import java.security.AlgorithmParameters;
22import java.security.InvalidAlgorithmParameterException;
23import java.security.InvalidKeyException;
24import java.security.InvalidParameterException;
25import java.security.Key;
26import java.security.NoSuchAlgorithmException;
27import java.security.NoSuchProviderException;
28import java.security.Provider;
29import java.security.SecureRandom;
30import java.security.Security;
31import java.security.cert.Certificate;
32import java.security.cert.X509Certificate;
33import java.security.spec.AlgorithmParameterSpec;
34import java.util.Set;
35import java.util.StringTokenizer;
36
37import org.apache.harmony.crypto.internal.NullCipherSpi;
38import org.apache.harmony.crypto.internal.nls.Messages;
39import org.apache.harmony.security.fortress.Engine;
40
41/**
42 * This class provides access to implementations of cryptographic ciphers for
43 * encryption and decryption. Cipher classes can not be instantiated directly,
44 * one has to call the Cipher's {@code getInstance} method with the name of a
45 * requested transformation, optionally with a provider. A transformation
46 * specifies an operation (or a set of operations) as a string in the form:
47 * <ul>
48 * <li><i>"algorithm/mode/padding"</i></li> or
49 * <li><i>"algorithm"</i></li>
50 * </ul>
51 * <i>algorithm</i> is the name of a cryptographic algorithm, <i>mode</i> is the
52 * name of a feedback mode and <i>padding</i> is the name of a padding scheme.
53 * If <i>mode</i> and/or <i>padding</i> values are omitted, provider specific
54 * default values will be used.
55 * <p>
56 * A valid transformation would be:
57 * <ul>
58 * {@code Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");}
59 * </ul>
60 * When a block cipher is requested in in stream cipher mode, the number of bits
61 * to be processed at a time can be optionally specified by appending it to the
62 * mode name. e.g. <i>"AES/CFB8/NoPadding"</i>. If no number is specified, a
63 * provider specific default value is used.
64 */
65public class Cipher {
66
67    /**
68     * Constant for decryption operation mode.
69     */
70    public static final int DECRYPT_MODE = 2;
71
72    /**
73     * Constant for encryption operation mode.
74     */
75    public static final int ENCRYPT_MODE = 1;
76
77    /**
78     * Constant indicating that the key to be unwrapped is a private key.
79     */
80    public static final int PRIVATE_KEY = 2;
81
82    /**
83     * Constant indicating that the key to be unwrapped is a public key.
84     */
85    public static final int PUBLIC_KEY = 1;
86
87    /**
88     * Constant indicating that the key to be unwrapped is a secret key.
89     */
90    public static final int SECRET_KEY = 3;
91
92    /**
93     * Constant for key unwrapping operation mode.
94     */
95    public static final int UNWRAP_MODE = 4;
96
97    /**
98     * Constant for key wrapping operation mode.
99     */
100    public static final int WRAP_MODE = 3;
101
102    private int mode;
103
104    /**
105     * The service name.
106     */
107    private static final String SERVICE = "Cipher"; //$NON-NLS-1$
108
109    /**
110     * Used to access common engine functionality.
111     */
112    private static final Engine engine = new Engine(SERVICE);
113
114    /**
115     * The provider.
116     */
117    private Provider provider;
118
119    /**
120     * The SPI implementation.
121     */
122    private CipherSpi spiImpl;
123
124    /**
125     * The transformation.
126     */
127    private String transformation;
128
129    private static SecureRandom sec_rand;
130
131    /**
132     * Creates a new Cipher instance.
133     *
134     * @param cipherSpi
135     *            the implementation delegate of the cipher.
136     * @param provider
137     *            the provider of the implementation of this cipher.
138     * @param transformation
139     *            the name of the transformation that this cipher performs.
140     * @throws NullPointerException
141     *             if either cipherSpi is {@code null} or provider is {@code
142     *             null} and {@code cipherSpi} is a {@code NullCipherSpi}.
143     */
144    protected Cipher(CipherSpi cipherSpi, Provider provider,
145            String transformation) {
146        if (cipherSpi == null) {
147            throw new NullPointerException();
148        }
149        if (!(cipherSpi instanceof NullCipherSpi) && provider == null) {
150            throw new NullPointerException();
151        }
152        this.provider = provider;
153        this.transformation = transformation;
154        this.spiImpl = cipherSpi;
155    }
156
157    /**
158     * Creates a new Cipher for the specified transformation. The installed
159     * providers are searched in order for an implementation of the specified
160     * transformation. The first found provider providing the transformation is
161     * used to create the cipher. If no provider is found an exception is
162     * thrown.
163     *
164     * @param transformation
165     *            the name of the transformation to create a cipher for.
166     * @return a cipher for the requested transformation.
167     * @throws NoSuchAlgorithmException
168     *             if no installed provider can provide the
169     *             <i>transformation</i>, or it is {@code null}, empty or in an
170     *             invalid format.
171     * @throws NoSuchPaddingException
172     *             if no installed provider can provide the padding scheme in
173     *             the <i>transformation</i>.
174     */
175    public static final Cipher getInstance(String transformation)
176            throws NoSuchAlgorithmException, NoSuchPaddingException {
177        return getCipher(transformation, null);
178    }
179
180    /**
181     * Creates a new cipher for the specified transformation provided by the
182     * specified provider.
183     *
184     * @param transformation
185     *            the name of the transformation to create a cipher for.
186     * @param provider
187     *            the name of the provider to ask for the transformation.
188     * @return a cipher for the requested transformation.
189     * @throws NoSuchAlgorithmException
190     *             if the specified provider can not provide the
191     *             <i>transformation</i>, or it is {@code null}, empty or in an
192     *             invalid format.
193     * @throws NoSuchProviderException
194     *             if no provider with the specified name can be found.
195     * @throws NoSuchPaddingException
196     *             if the requested padding scheme in the <i>transformation</i>
197     *             is not available.
198     * @throws IllegalArgumentException
199     *             if the specified provider is {@code null}.
200     */
201    public static final Cipher getInstance(String transformation,
202            String provider) throws NoSuchAlgorithmException,
203            NoSuchProviderException, NoSuchPaddingException {
204
205        if (provider == null) {
206            throw new IllegalArgumentException(Messages.getString("crypto.04")); //$NON-NLS-1$
207        }
208
209        Provider p = Security.getProvider(provider);
210        if (p == null) {
211            throw new NoSuchProviderException(Messages.getString("crypto.16", provider)); //$NON-NLS-1$
212        }
213        return getInstance(transformation, p);
214    }
215
216    /**
217     * Creates a new cipher for the specified transformation.
218     *
219     * @param transformation
220     *            the name of the transformation to create a cipher for.
221     * @param provider
222     *            the provider to ask for the transformation.
223     * @return a cipher for the requested transformation.
224     * @throws NoSuchAlgorithmException
225     *             if the specified provider can not provide the
226     *             <i>transformation</i>, or it is {@code null}, empty or in an
227     *             invalid format.
228     * @throws NoSuchPaddingException
229     *             if the requested padding scheme in the <i>transformation</i>
230     *             is not available.
231     * @throws IllegalArgumentException
232     *             if the provider is {@code null}.
233     */
234    public static final Cipher getInstance(String transformation,
235            Provider provider) throws NoSuchAlgorithmException,
236            NoSuchPaddingException {
237        if (provider == null) {
238            throw new IllegalArgumentException(Messages.getString("crypto.04")); //$NON-NLS-1$
239        }
240        Cipher c = getCipher(transformation, provider);
241        return c;
242    }
243
244    /**
245     * Find appropriate Cipher according the specification rules
246     *
247     * @param transformation
248     * @param provider
249     * @return
250     * @throws NoSuchAlgorithmException
251     * @throws NoSuchPaddingException
252     */
253    private static synchronized Cipher getCipher(String transformation, Provider provider)
254            throws NoSuchAlgorithmException, NoSuchPaddingException {
255
256        if (transformation == null || "".equals(transformation)) { //$NON-NLS-1$
257            throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$
258                    transformation));
259        }
260
261        String[] transf = checkTransformation(transformation);
262
263        boolean needSetPadding = false;
264        boolean needSetMode = false;
265        if (transf[1] == null && transf[2] == null) { // "algorithm"
266            if (provider == null) {
267                engine.getInstance(transf[0], null);
268            } else {
269                engine.getInstance(transf[0], provider, null);
270            }
271        } else {
272            String[] searhOrder = {
273                    transf[0] + "/" + transf[1] + "/" + transf[2], // "algorithm/mode/padding" //$NON-NLS-1$ //$NON-NLS-2$
274                    transf[0] + "/" + transf[1], // "algorithm/mode" //$NON-NLS-1$
275                    transf[0] + "//" + transf[2], // "algorithm//padding" //$NON-NLS-1$
276                    transf[0] // "algorithm"
277            };
278            int i;
279            for (i = 0; i < searhOrder.length; i++) {
280                try {
281                    if (provider == null) {
282                        engine.getInstance(searhOrder[i], null);
283                    } else {
284                        engine.getInstance(searhOrder[i], provider, null);
285                    }
286                    break;
287                } catch (NoSuchAlgorithmException e) {
288                    if ( i == searhOrder.length-1) {
289                        throw new NoSuchAlgorithmException(transformation);
290                    }
291                }
292            }
293            switch (i) {
294            case 1: // "algorithm/mode"
295                needSetPadding = true;
296                break;
297            case 2: // "algorithm//padding"
298                needSetMode = true;
299                break;
300            case 3: // "algorithm"
301                needSetPadding = true;
302                needSetMode = true;
303            }
304        }
305        CipherSpi cspi;
306        try {
307            cspi = (CipherSpi) engine.spi;
308        } catch (ClassCastException e) {
309            throw new NoSuchAlgorithmException(e);
310        }
311        Cipher c = new Cipher(cspi, engine.provider, transformation);
312        if (needSetMode) {
313            c.spiImpl.engineSetMode(transf[1]);
314        }
315        if (needSetPadding) {
316            c.spiImpl.engineSetPadding(transf[2]);
317        }
318        return c;
319    }
320
321    private static String[] checkTransformation(String transformation)
322            throws NoSuchAlgorithmException {
323        String[] transf = { null, null, null };
324        StringTokenizer st;
325        int i = 0;
326        for (st = new StringTokenizer(transformation, "/"); st //$NON-NLS-1$
327                .hasMoreElements();) {
328            if (i > 2) {
329                throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$
330                        transformation));
331            }
332            transf[i] = st.nextToken();
333            if (transf[i] != null) {
334                transf[i] = transf[i].trim();
335                if ("".equals(transf[i])) { //$NON-NLS-1$
336                    transf[i] = null;
337                }
338                i++;
339            }
340        }
341        if (transf[0] == null) {
342            throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$
343                    transformation));
344        }
345        if (!(transf[1] == null && transf[2] == null)
346                && (transf[1] == null || transf[2] == null)) {
347            throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$
348                    transformation));
349        }
350        return transf;
351    }
352
353    /**
354     * Returns the provider of this cipher instance.
355     *
356     * @return the provider of this cipher instance.
357     */
358    public final Provider getProvider() {
359        return provider;
360    }
361
362    /**
363     * Returns the name of the algorithm of this cipher instance.
364     * <p>
365     * This is the name of the <i>transformation</i> argument used in the
366     * {@code getInstance} call creating this object.
367     *
368     * @return the name of the algorithm of this cipher instance.
369     */
370    public final String getAlgorithm() {
371        return transformation;
372    }
373
374    /**
375     * Returns this ciphers block size (in bytes).
376     *
377     * @return this ciphers block size.
378     */
379    public final int getBlockSize() {
380        return spiImpl.engineGetBlockSize();
381    }
382
383    /**
384     * Returns the length in bytes an output buffer needs to be when this cipher
385     * is updated with {@code inputLen} bytes.
386     *
387     * @param inputLen
388     *            the number of bytes of the input.
389     * @return the output buffer length for the input length.
390     * @throws IllegalStateException
391     *             if this cipher instance is in an invalid state.
392     */
393    public final int getOutputSize(int inputLen) {
394        if (mode == 0) {
395            throw new IllegalStateException(
396                    Messages.getString("crypto.18")); //$NON-NLS-1$
397        }
398        return spiImpl.engineGetOutputSize(inputLen);
399    }
400
401    /**
402     * Returns the <i>initialization vector</i> for this cipher instance.
403     *
404     * @return the <i>initialization vector</i> for this cipher instance.
405     */
406    public final byte[] getIV() {
407        return spiImpl.engineGetIV();
408    }
409
410    /**
411     * Returns the parameters that where used to create this cipher instance.
412     * <p>
413     * These may be a the same parameters that were used to create this cipher
414     * instance, or may be a combination of default and random parameters,
415     * depending on the underlying cipher implementation.
416     *
417     * @return the parameters that where used to create this cipher instance, or
418     *         {@code null} if this cipher instance does not have any
419     *         parameters.
420     */
421    public final AlgorithmParameters getParameters() {
422        return spiImpl.engineGetParameters();
423    }
424
425    /**
426     * Returns the exemption mechanism associated with this cipher.
427     *
428     * @return currently {@code null}
429     */
430    public final ExemptionMechanism getExemptionMechanism() {
431        //FIXME implement getExemptionMechanism
432
433        //        try {
434        //            return ExemptionMechanism.getInstance(transformation, provider);
435        //        } catch (NoSuchAlgorithmException e) {
436        return null;
437        //        }
438
439    }
440
441    /**
442     * Initializes this cipher instance with the specified key.
443     * <p>
444     * The cipher is initialized for the specified operational mode (one of:
445     * encryption, decryption, key wrapping or key unwrapping) depending on
446     * {@code opmode}.
447     * <p>
448     * If this cipher instance needs any algorithm parameters or random values
449     * that the specified key can not provide, the underlying implementation of
450     * this cipher is supposed to generate the required parameters (using its
451     * provider or random values).
452     * <p>
453     * When a cipher instance is initialized by a call to any of the {@code
454     * init} methods, the state of the instance is overridden, meaning that it
455     * is equivalent to creating a new instance and calling its {@code init}
456     * method.
457     *
458     * @param opmode
459     *            the operation this cipher instance should be initialized for
460     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
461     *            WRAP_MODE} or {@code UNWRAP_MODE}).
462     * @param key
463     *            the input key for the operation.
464     * @throws InvalidKeyException
465     *             if the specified key can not be used to initialize this
466     *             cipher instance.
467     */
468    public final void init(int opmode, Key key) throws InvalidKeyException {
469        if (sec_rand == null) {
470            // In theory it might be thread-unsafe but in the given case it's OK
471            // since it does not matter which SecureRandom instance is passed
472            // to the init()
473            sec_rand = new SecureRandom();
474        }
475        init(opmode, key, sec_rand);
476    }
477
478    /**
479     * Initializes this cipher instance with the specified key and a source of
480     * randomness.
481     * <p>
482     * The cipher is initialized for the specified operational mode (one of:
483     * encryption, decryption, key wrapping or key unwrapping) depending on
484     * {@code opmode}.
485     * <p>
486     * If this cipher instance needs any algorithm parameters or random values
487     * that the specified key can not provide, the underlying implementation of
488     * this cipher is supposed to generate the required parameters (using its
489     * provider or random values). Random values are generated using {@code
490     * random};
491     * <p>
492     * When a cipher instance is initialized by a call to any of the {@code
493     * init} methods, the state of the instance is overridden, means it is
494     * equivalent to creating a new instance and calling it {@code init} method.
495     *
496     * @param opmode
497     *            the operation this cipher instance should be initialized for
498     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
499     *            WRAP_MODE} or {@code UNWRAP_MODE}).
500     * @param key
501     *            the input key for the operation.
502     * @param random
503     *            the source of randomness to use.
504     * @throws InvalidKeyException
505     *             if the specified key can not be used to initialize this
506     *             cipher instance.
507     * @throws InvalidParameterException
508     *             if the specified opmode is invalid.
509     */
510    public final void init(int opmode, Key key, SecureRandom random)
511            throws InvalidKeyException {
512        if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE
513                && opmode != UNWRAP_MODE && opmode != WRAP_MODE) {
514            throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$
515        }
516        //        FIXME InvalidKeyException
517        //        if keysize exceeds the maximum allowable keysize
518        //        (jurisdiction policy files)
519        spiImpl.engineInit(opmode, key, random);
520        mode = opmode;
521    }
522
523    /**
524     * Initializes this cipher instance with the specified key and algorithm
525     * parameters.
526     * <p>
527     * The cipher is initialized for the specified operational mode (one of:
528     * encryption, decryption, key wrapping or key unwrapping).
529     * <p>
530     * If this cipher instance needs any algorithm parameters and {@code params}
531     * is {@code null}, the underlying implementation of this cipher is supposed
532     * to generate the required parameters (using its provider or random
533     * values).
534     * <p>
535     * When a cipher instance is initialized by a call to any of the {@code
536     * init} methods, the state of the instance is overridden, means it is
537     * equivalent to creating a new instance and calling it {@code init} method.
538     *
539     * @param opmode
540     *            the operation this cipher instance should be initialized for
541     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
542     *            WRAP_MODE} or {@code UNWRAP_MODE}).
543     * @param key
544     *            the input key for the operation.
545     * @param params
546     *            the algorithm parameters.
547     * @throws InvalidKeyException
548     *             if the specified key can not be used to initialize this
549     *             cipher instance.
550     * @throws InvalidAlgorithmParameterException
551     *             it the specified parameters are inappropriate for this
552     *             cipher.
553     */
554    public final void init(int opmode, Key key, AlgorithmParameterSpec params)
555            throws InvalidKeyException, InvalidAlgorithmParameterException {
556        if (sec_rand == null) {
557            sec_rand = new SecureRandom();
558        }
559        init(opmode, key, params, sec_rand);
560    }
561
562    /**
563     * Initializes this cipher instance with the specified key, algorithm
564     * parameters and a source of randomness.
565     * <p>
566     * The cipher is initialized for the specified operational mode (one of:
567     * encryption, decryption, key wrapping or key unwrapping) depending on
568     * {@code opmode}.
569     * <p>
570     * If this cipher instance needs any algorithm parameters and {@code params}
571     * is {@code null}, the underlying implementation of this cipher is supposed
572     * to generate the required parameters (using its provider or random
573     * values). Random values are generated using {@code random};
574     * <p>
575     * When a cipher instance is initialized by a call to any of the {@code
576     * init} methods, the state of the instance is overridden, meaning that it
577     * is equivalent to creating a new instance and calling it {@code init}
578     * method.
579     *
580     * @param opmode
581     *            the operation this cipher instance should be initialized for
582     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
583     *            WRAP_MODE} or {@code UNWRAP_MODE}).
584     * @param key
585     *            the input key for the operation.
586     * @param params
587     *            the algorithm parameters.
588     * @param random
589     *            the source of randomness to use.
590     * @throws InvalidKeyException
591     *             if the specified key can not be used to initialize this
592     *             cipher instance.
593     * @throws InvalidAlgorithmParameterException
594     *             it the specified parameters are inappropriate for this
595     *             cipher.
596     * @throws InvalidParameterException
597     *             if the specified {@code opmode} is invalid.
598     */
599    public final void init(int opmode, Key key, AlgorithmParameterSpec params,
600            SecureRandom random) throws InvalidKeyException,
601            InvalidAlgorithmParameterException {
602        if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE
603                && opmode != UNWRAP_MODE && opmode != WRAP_MODE) {
604            throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$
605        }
606        //        FIXME InvalidKeyException
607        //        if keysize exceeds the maximum allowable keysize
608        //        (jurisdiction policy files)
609        //        FIXME InvalidAlgorithmParameterException
610        //        cryptographic strength exceed the legal limits
611        //        (jurisdiction policy files)
612        spiImpl.engineInit(opmode, key, params, random);
613        mode = opmode;
614    }
615
616    /**
617     * Initializes this cipher instance with the specified key and algorithm
618     * parameters.
619     * <p>
620     * The cipher is initialized for the specified operation (one of:
621     * encryption, decryption, key wrapping or key unwrapping) depending on
622     * {@code opmode}.
623     * <p>
624     * If this cipher instance needs any algorithm parameters and {@code params}
625     * is {@code null}, the underlying implementation of this cipher is supposed
626     * to generate the required parameters (using its provider or random
627     * values).
628     * <p>
629     * When a cipher instance is initialized by a call to any of the {@code
630     * init} methods, the state of the instance is overridden, meaning that it
631     * is equivalent to creating a new instance and calling it {@code init}
632     * method.
633     *
634     * @param opmode
635     *            the operation this cipher instance should be initialized for
636     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
637     *            WRAP_MODE} or {@code UNWRAP_MODE}).
638     * @param key
639     *            the input key for the operation.
640     * @param params
641     *            the algorithm parameters.
642     * @throws InvalidKeyException
643     *             if the specified key can not be used to initialize this
644     *             cipher instance.
645     * @throws InvalidAlgorithmParameterException
646     *             it the specified parameters are inappropriate for this
647     *             cipher.
648     */
649    public final void init(int opmode, Key key, AlgorithmParameters params)
650            throws InvalidKeyException, InvalidAlgorithmParameterException {
651        if (sec_rand == null) {
652            sec_rand = new SecureRandom();
653        }
654        init(opmode, key, params, sec_rand);
655    }
656
657    /**
658     * Initializes this cipher instance with the specified key, algorithm
659     * parameters and a source of randomness.
660     * <p>
661     * The cipher will be initialized for the specified operation (one of:
662     * encryption, decryption, key wrapping or key unwrapping) depending on
663     * {@code opmode}.
664     * <p>
665     * If this cipher instance needs any algorithm parameters and {@code params}
666     * is {@code null}, the underlying implementation of this cipher is supposed
667     * to generate the required parameters (using its provider or random
668     * values). Random values are generated using {@code random}.
669     * <p>
670     * When a cipher instance is initialized by a call to any of the {@code
671     * init} methods, the state of the instance is overridden, means it is
672     * equivalent to creating a new instance and calling it {@code init} method.
673     *
674     * @param opmode
675     *            the operation this cipher instance should be initialized for
676     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
677     *            WRAP_MODE} or {@code UNWRAP_MODE}).
678     * @param key
679     *            the input key for the operation.
680     * @param params
681     *            the algorithm parameters.
682     * @param random
683     *            the source of randomness to use.
684     * @throws InvalidKeyException
685     *             if the specified key can not be used to initialize this
686     *             cipher instance.
687     * @throws InvalidAlgorithmParameterException
688     *             if the specified parameters are inappropriate for this
689     *             cipher.
690     * @throws InvalidParameterException
691     *             if the specified {@code opmode} is invalid.
692     */
693    public final void init(int opmode, Key key, AlgorithmParameters params,
694            SecureRandom random) throws InvalidKeyException,
695            InvalidAlgorithmParameterException {
696        if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE
697                && opmode != UNWRAP_MODE && opmode != WRAP_MODE) {
698            throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$
699        }
700        //        FIXME InvalidKeyException
701        //        if keysize exceeds the maximum allowable keysize
702        //        (jurisdiction policy files)
703        //        FIXME InvalidAlgorithmParameterException
704        //        cryptographic strength exceed the legal limits
705        //        (jurisdiction policy files)
706        spiImpl.engineInit(opmode, key, params, random);
707        mode = opmode;
708    }
709
710    /**
711     * Initializes this cipher instance with the public key from the specified
712     * certificate.
713     * <p>
714     * The cipher will be initialized for the specified operation (one of:
715     * encryption, decryption, key wrapping or key unwrapping) depending on
716     * {@code opmode}.
717     * <p>
718     * It the type of the certificate is X.509 and the certificate has a <i>key
719     * usage</i> extension field marked as critical, the specified {@code
720     * opmode} has the be enabled for this key, otherwise an {@code
721     * InvalidKeyException} is thrown.
722     * <p>
723     * If this cipher instance needs any algorithm parameters that the key in
724     * the certificate can not provide, the underlying implementation of this
725     * cipher is supposed to generate the required parameters (using its
726     * provider or random values).
727     * <p>
728     * When a cipher instance is initialized by a call to any of the {@code
729     * init} methods, the state of the instance is overridden, means it is
730     * equivalent to creating a new instance and calling it {@code init} method.
731     *
732     * @param opmode
733     *            the operation this cipher instance should be initialized for
734     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
735     *            WRAP_MODE} or {@code UNWRAP_MODE}).
736     * @param certificate
737     *            the certificate.
738     * @throws InvalidKeyException
739     *             if the public key in the certificate can not be used to
740     *             initialize this cipher instance.
741     */
742    public final void init(int opmode, Certificate certificate)
743            throws InvalidKeyException {
744        if (sec_rand == null) {
745            sec_rand = new SecureRandom();
746        }
747        init(opmode, certificate, sec_rand);
748    }
749
750    /**
751     * Initializes this cipher instance with the public key from the specified
752     * certificate and a source of randomness.
753     * <p>
754     * The cipher will be initialized for the specified operation (one of:
755     * encryption, decryption, key wrapping or key unwrapping) depending on
756     * {@code opmode}.
757     * <p>
758     * It the type of the certificate is X.509 and the certificate has a <i>key
759     * usage</i> extension field marked as critical, the specified {@code
760     * opmode} has the be enabled for this key, otherwise an {@code
761     * InvalidKeyException} is thrown.
762     * <p>
763     * If this cipher instance needs any algorithm parameters that the key in
764     * the certificate can not provide, the underlying implementation of this
765     * cipher is supposed to generate the required parameters (using its
766     * provider or random values). Random values are generated using {@code
767     * random}.
768     * <p>
769     * When a cipher instance is initialized by a call to any of the {@code
770     * init} methods, the state of the instance is overridden, means it is
771     * equivalent to creating a new instance and calling it {@code init} method.
772     *
773     * @param opmode
774     *            the operation this cipher instance should be initialized for
775     *            (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
776     *            WRAP_MODE} or {@code UNWRAP_MODE}).
777     * @param certificate
778     *            the certificate.
779     * @param random
780     *            the source of randomness to be used.
781     * @throws InvalidKeyException
782     *             if the public key in the certificate can not be used to
783     *             initialize this cipher instance.
784     */
785    public final void init(int opmode, Certificate certificate,
786            SecureRandom random) throws InvalidKeyException {
787        if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE
788                && opmode != UNWRAP_MODE && opmode != WRAP_MODE) {
789            throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$
790        }
791        if (certificate instanceof X509Certificate) {
792            Set<String> ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
793            boolean critical = false;
794            if (ce != null && !ce.isEmpty()) {
795                for (String oid : ce) {
796                    if (oid.equals("2.5.29.15")) { //KeyUsage OID = 2.5.29.15 //$NON-NLS-1$
797                        critical = true;
798                        break;
799                    }
800                }
801                if (critical) {
802                    boolean[] keyUsage = ((X509Certificate) certificate)
803                            .getKeyUsage();
804                    // As specified in RFC 3280 -
805                    // Internet X.509 Public Key Infrastructure
806                    // Certificate and Certificate Revocation List (CRL) Profile.
807                    // (http://www.ietf.org/rfc/rfc3280.txt)
808                    //
809                    // KeyUsage ::= BIT STRING {digitalSignature (0),
810                    //                          ...
811                    //                          encipherOnly (7),
812                    //                          decipherOnly (8) }
813                    if (keyUsage != null) {
814                        if (opmode == ENCRYPT_MODE && (!keyUsage[7])) {
815                            throw new InvalidKeyException(
816                                    Messages.getString("crypto.1A")); //$NON-NLS-1$
817                        } else if (opmode == DECRYPT_MODE && (!keyUsage[8])) {
818                            throw new InvalidKeyException(
819                                    Messages.getString("crypto.1B")); //$NON-NLS-1$
820                        }
821                    }
822                }
823            }
824        }
825        //        FIXME InvalidKeyException
826        //        if keysize exceeds the maximum allowable keysize
827        //        (jurisdiction policy files)
828        spiImpl.engineInit(opmode, certificate.getPublicKey(), random);
829        mode = opmode;
830    }
831
832    /**
833     * Continues a multi-part transformation (encryption or decryption). The
834     * transformed bytes are returned.
835     *
836     * @param input
837     *            the input bytes to transform.
838     * @return the transformed bytes in a new buffer, or {@code null} if the
839     *         input has zero length.
840     * @throws IllegalStateException
841     *             if this cipher instance is not initialized for encryption or
842     *             decryption.
843     * @throws IllegalArgumentException
844     *             if the input is {@code null}.
845     */
846    public final byte[] update(byte[] input) {
847        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
848            throw new IllegalStateException(
849                    Messages.getString("crypto.1C")); //$NON-NLS-1$
850        }
851        if (input == null) {
852            throw new IllegalArgumentException(Messages.getString("crypto.1D")); //$NON-NLS-1$
853        }
854        if (input.length == 0) {
855            return null;
856        }
857        return spiImpl.engineUpdate(input, 0, input.length);
858    }
859
860    /**
861     * Continues a multi-part transformation (encryption or decryption). The
862     * transformed bytes are returned.
863     *
864     * @param input
865     *            the input bytes to transform.
866     * @param inputOffset
867     *            the offset in the input to start.
868     * @param inputLen
869     *            the length of the input to transform.
870     * @return the transformed bytes in a new buffer, or {@code null} if the
871     *         input has zero length.
872     * @throws IllegalStateException
873     *             if this cipher instance is not initialized for encryption or
874     *             decryption.
875     * @throws IllegalArgumentException
876     *             if the input is {@code null}, or if {@code inputOffset} and
877     *             {@code inputLen} do not specify a valid chunk in the input
878     *             buffer.
879     */
880    public final byte[] update(byte[] input, int inputOffset, int inputLen) {
881        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
882            throw new IllegalStateException(
883                    Messages.getString("crypto.1C")); //$NON-NLS-1$
884        }
885        if (input == null) {
886            throw new IllegalArgumentException(Messages.getString("crypto.1D")); //$NON-NLS-1$
887        }
888        if (inputOffset < 0 || inputLen < 0
889                || inputLen > input.length
890                || inputOffset > input.length - inputLen) {
891            throw new IllegalArgumentException(
892                    Messages.getString("crypto.1E")); //$NON-NLS-1$
893        }
894        if (input.length == 0) {
895            return null;
896        }
897        return spiImpl.engineUpdate(input, inputOffset, inputLen);
898    }
899
900    /**
901     * Continues a multi-part transformation (encryption or decryption). The
902     * transformed bytes are stored in the {@code output} buffer.
903     * <p>
904     * If the size of the {@code output} buffer is too small to hold the result,
905     * a {@code ShortBufferException} is thrown. Use
906     * {@link Cipher#getOutputSize getOutputSize} to check for the size of the
907     * output buffer.
908     *
909     * @param input
910     *            the input bytes to transform.
911     * @param inputOffset
912     *            the offset in the input to start.
913     * @param inputLen
914     *            the length of the input to transform.
915     * @param output
916     *            the output buffer.
917     * @return the number of bytes placed in output.
918     * @throws ShortBufferException
919     *             if the size of the {@code output} buffer is too small.
920     * @throws IllegalStateException
921     *             if this cipher instance is not initialized for encryption or
922     *             decryption.
923     * @throws IllegalArgumentException
924     *             if the input is {@code null}, the output is {@code null}, or
925     *             if {@code inputOffset} and {@code inputLen} do not specify a
926     *             valid chunk in the input buffer.
927     */
928    public final int update(byte[] input, int inputOffset, int inputLen,
929            byte[] output) throws ShortBufferException {
930        return update(input, inputOffset, inputLen, output, 0);
931    }
932
933    /**
934     * Continues a multi-part transformation (encryption or decryption). The
935     * transformed bytes are stored in the {@code output} buffer.
936     * <p>
937     * If the size of the {@code output} buffer is too small to hold the result,
938     * a {@code ShortBufferException} is thrown. Use
939     * {@link Cipher#getOutputSize getOutputSize} to check for the size of the
940     * output buffer.
941     *
942     * @param input
943     *            the input bytes to transform.
944     * @param inputOffset
945     *            the offset in the input to start.
946     * @param inputLen
947     *            the length of the input to transform.
948     * @param output
949     *            the output buffer.
950     * @param outputOffset
951     *            the offset in the output buffer.
952     * @return the number of bytes placed in output.
953     * @throws ShortBufferException
954     *             if the size of the {@code output} buffer is too small.
955     * @throws IllegalStateException
956     *             if this cipher instance is not initialized for encryption or
957     *             decryption.
958     * @throws IllegalArgumentException
959     *             if the input is {@code null}, the output is {@code null}, or
960     *             if {@code inputOffset} and {@code inputLen} do not specify a
961     *             valid chunk in the input buffer.
962     */
963    public final int update(byte[] input, int inputOffset, int inputLen,
964            byte[] output, int outputOffset) throws ShortBufferException {
965        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
966            throw new IllegalStateException(
967                    Messages.getString("crypto.1C")); //$NON-NLS-1$
968        }
969        if (input == null) {
970            throw new IllegalArgumentException(Messages.getString("crypto.1D")); //$NON-NLS-1$
971        }
972        if (output == null) {
973            throw new IllegalArgumentException(Messages.getString("crypto.1F")); //$NON-NLS-1$
974        }
975        if (outputOffset < 0) {
976            throw new IllegalArgumentException(
977                    Messages.getString("crypto.20")); //$NON-NLS-1$
978        }
979        if (inputOffset < 0 || inputLen < 0
980                || inputLen > input.length
981                || inputOffset > input.length - inputLen) {
982            throw new IllegalArgumentException(
983                    Messages.getString("crypto.21")); //$NON-NLS-1$
984        }
985        if (input.length == 0) {
986            return 0;
987        }
988        return spiImpl.engineUpdate(input, inputOffset, inputLen, output,
989                outputOffset);
990    }
991
992    /**
993     * Continues a multi-part transformation (encryption or decryption). The
994     * {@code input.remaining()} bytes starting at {@code input.position()} are
995     * transformed and stored in the {@code output} buffer.
996     * <p>
997     * If the {@code output.remaining()} is too small to hold the transformed
998     * bytes a {@code ShortBufferException} is thrown. Use
999     * {@link Cipher#getOutputSize getOutputSize} to check for the size of the
1000     * output buffer.
1001     *
1002     * @param input
1003     *            the input buffer to transform.
1004     * @param output
1005     *            the output buffer to store the result within.
1006     * @return the number of bytes stored in the output buffer.
1007     * @throws ShortBufferException
1008     *             if the size of the {@code output} buffer is too small.
1009     * @throws IllegalStateException
1010     *             if this cipher instance is not initialized for encryption or
1011     *             decryption.
1012     * @throws IllegalArgumentException
1013     *             if the input buffer and the output buffer are the identical
1014     *             object.
1015     */
1016    public final int update(ByteBuffer input, ByteBuffer output)
1017            throws ShortBufferException {
1018        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1019            throw new IllegalStateException(
1020                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1021        }
1022        if (input == output) {
1023            throw new IllegalArgumentException(
1024                    Messages.getString("crypto.22")); //$NON-NLS-1$
1025        }
1026        return spiImpl.engineUpdate(input, output);
1027    }
1028
1029    /**
1030     * Finishes a multi-part transformation (encryption or decryption).
1031     * <p>
1032     * Processes any bytes that may have been buffered in previous {@code
1033     * update} calls.
1034     *
1035     * @return the final bytes from the transformation.
1036     * @throws IllegalBlockSizeException
1037     *             if the size of the resulting bytes is not a multiple of the
1038     *             cipher block size.
1039     * @throws BadPaddingException
1040     *             if the padding of the data does not match the padding scheme.
1041     * @throws IllegalStateException
1042     *             if this cipher instance is not initialized for encryption or
1043     *             decryption.
1044     */
1045    public final byte[] doFinal() throws IllegalBlockSizeException,
1046            BadPaddingException {
1047        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1048            throw new IllegalStateException(
1049                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1050        }
1051        return spiImpl.engineDoFinal(null, 0, 0);
1052    }
1053
1054    /**
1055     * Finishes a multi-part transformation (encryption or decryption).
1056     * <p>
1057     * Processes any bytes that may have been buffered in previous {@code
1058     * update} calls.
1059     * <p>
1060     * The final transformed bytes are stored in the {@code output} buffer.
1061     *
1062     * @param output
1063     *            the output buffer.
1064     * @param outputOffset
1065     *            the offset in the output buffer.
1066     * @return the number of bytes placed in the output buffer.
1067     * @throws IllegalBlockSizeException
1068     *             if the size of the resulting bytes is not a multiple of the
1069     *             cipher block size.
1070     * @throws ShortBufferException
1071     *             if the size of the {@code output} buffer is too small.
1072     * @throws BadPaddingException
1073     *             if the padding of the data does not match the padding scheme.
1074     * @throws IllegalStateException
1075     *             if this cipher instance is not initialized for encryption or
1076     *             decryption.
1077     */
1078    public final int doFinal(byte[] output, int outputOffset)
1079            throws IllegalBlockSizeException, ShortBufferException,
1080            BadPaddingException {
1081        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1082            throw new IllegalStateException(
1083                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1084        }
1085        if (outputOffset < 0) {
1086            throw new IllegalArgumentException(
1087                    Messages.getString("crypto.20")); //$NON-NLS-1$
1088        }
1089        return spiImpl.engineDoFinal(null, 0, 0, output, outputOffset);
1090    }
1091
1092    /**
1093     * Finishes a multi-part transformation (encryption or decryption).
1094     * <p>
1095     * Processes the bytes in {@code input} buffer, and any bytes that have been
1096     * buffered in previous {@code update} calls.
1097     *
1098     * @param input
1099     *            the input buffer.
1100     * @return the final bytes from the transformation.
1101     * @throws IllegalBlockSizeException
1102     *             if the size of the resulting bytes is not a multiple of the
1103     *             cipher block size.
1104     * @throws BadPaddingException
1105     *             if the padding of the data does not match the padding scheme.
1106     * @throws IllegalStateException
1107     *             if this cipher instance is not initialized for encryption or
1108     *             decryption.
1109     */
1110    public final byte[] doFinal(byte[] input) throws IllegalBlockSizeException,
1111            BadPaddingException {
1112        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1113            throw new IllegalStateException(
1114                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1115        }
1116        return spiImpl.engineDoFinal(input, 0, input.length);
1117    }
1118
1119    /**
1120     * Finishes a multi-part transformation (encryption or decryption).
1121     * <p>
1122     * Processes the {@code inputLen} bytes in {@code input} buffer at {@code
1123     * inputOffset}, and any bytes that have been buffered in previous {@code
1124     * update} calls.
1125     *
1126     * @param input
1127     *            the input buffer.
1128     * @param inputOffset
1129     *            the offset in the input buffer.
1130     * @param inputLen
1131     *            the length of the input
1132     * @return the final bytes from the transformation.
1133     * @throws IllegalBlockSizeException
1134     *             if the size of the resulting bytes is not a multiple of the
1135     *             cipher block size.
1136     * @throws BadPaddingException
1137     *             if the padding of the data does not match the padding scheme.
1138     * @throws IllegalStateException
1139     *             if this cipher instance is not initialized for encryption or
1140     *             decryption.
1141     * @throws IllegalArgumentException
1142     *             if {@code inputOffset} and {@code inputLen} do not specify an
1143     *             valid chunk in the input buffer.
1144     */
1145    public final byte[] doFinal(byte[] input, int inputOffset, int inputLen)
1146            throws IllegalBlockSizeException, BadPaddingException {
1147        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1148            throw new IllegalStateException(
1149                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1150        }
1151        if (inputOffset < 0 || inputLen < 0
1152                || inputOffset + inputLen > input.length) {
1153            throw new IllegalArgumentException(
1154                    Messages.getString("crypto.1E")); //$NON-NLS-1$
1155        }
1156        return spiImpl.engineDoFinal(input, inputOffset, inputLen);
1157    }
1158
1159    /**
1160     * Finishes a multi-part transformation (encryption or decryption).
1161     * <p>
1162     * Processes the {@code inputLen} bytes in {@code input} buffer at {@code
1163     * inputOffset}, and any bytes that have been buffered in previous {@code
1164     * update} calls.
1165     *
1166     * @param input
1167     *            the input buffer.
1168     * @param inputOffset
1169     *            the offset in the input buffer.
1170     * @param inputLen
1171     *            the length of the input.
1172     * @param output
1173     *            the output buffer for the transformed bytes.
1174     * @return the number of bytes placed in the output buffer.
1175     * @throws ShortBufferException
1176     *             if the size of the {@code output} buffer is too small.
1177     * @throws IllegalBlockSizeException
1178     *             if the size of the resulting bytes is not a multiple of the
1179     *             cipher block size.
1180     * @throws BadPaddingException
1181     *             if the padding of the data does not match the padding scheme.
1182     * @throws IllegalStateException
1183     *             if this cipher instance is not initialized for encryption or
1184     *             decryption.
1185     * @throws IllegalArgumentException
1186     *             if {@code inputOffset} and {@code inputLen} do not specify an
1187     *             valid chunk in the input buffer.
1188     */
1189    public final int doFinal(byte[] input, int inputOffset, int inputLen,
1190            byte[] output) throws ShortBufferException,
1191            IllegalBlockSizeException, BadPaddingException {
1192        return doFinal(input, inputOffset, inputLen, output, 0);
1193    }
1194
1195    /**
1196     * Finishes a multi-part transformation (encryption or decryption).
1197     * <p>
1198     * Processes the {@code inputLen} bytes in {@code input} buffer at {@code
1199     * inputOffset}, and any bytes that have been buffered in previous {@code
1200     * update} calls.
1201     *
1202     * @param input
1203     *            the input buffer.
1204     * @param inputOffset
1205     *            the offset in the input buffer.
1206     * @param inputLen
1207     *            the length of the input.
1208     * @param output
1209     *            the output buffer for the transformed bytes.
1210     * @param outputOffset
1211     *            the offset in the output buffer.
1212     * @return the number of bytes placed in the output buffer.
1213     * @throws ShortBufferException
1214     *             if the size of the {@code output} buffer is too small.
1215     * @throws IllegalBlockSizeException
1216     *             if the size of the resulting bytes is not a multiple of the
1217     *             cipher block size.
1218     * @throws BadPaddingException
1219     *             if the padding of the data does not match the padding scheme.
1220     * @throws IllegalStateException
1221     *             if this cipher instance is not initialized for encryption or
1222     *             decryption.
1223     * @throws IllegalArgumentException
1224     *             if {@code inputOffset} and {@code inputLen} do not specify an
1225     *             valid chunk in the input buffer.
1226     */
1227    public final int doFinal(byte[] input, int inputOffset, int inputLen,
1228            byte[] output, int outputOffset) throws ShortBufferException,
1229            IllegalBlockSizeException, BadPaddingException {
1230        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1231            throw new IllegalStateException(
1232                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1233        }
1234        if (inputOffset < 0 || inputLen < 0
1235                || inputOffset + inputLen > input.length) {
1236            throw new IllegalArgumentException(
1237                    Messages.getString("crypto.1E")); //$NON-NLS-1$
1238        }
1239        return spiImpl.engineDoFinal(input, inputOffset, inputLen, output,
1240                outputOffset);
1241    }
1242
1243    /**
1244     * Finishes a multi-part transformation (encryption or decryption).
1245     * <p>
1246     * Processes the {@code input.remaining()} bytes in {@code input} buffer at
1247     * {@code input.position()}, and any bytes that have been buffered in
1248     * previous {@code update} calls. The transformed bytes are placed into
1249     * {@code output} buffer.
1250     *
1251     * @param input
1252     *            the input buffer.
1253     * @param output
1254     *            the output buffer.
1255     * @return the number of bytes placed into the output buffer.
1256     * @throws ShortBufferException
1257     *             if the size of the {@code output} buffer is too small.
1258     * @throws IllegalBlockSizeException
1259     *             if the size of the resulting bytes is not a multiple of the
1260     *             cipher block size.
1261     * @throws BadPaddingException
1262     *             if the padding of the data does not match the padding scheme.
1263     * @throws IllegalArgumentException
1264     *             if the input buffer and the output buffer are the same
1265     *             object.
1266     * @throws IllegalStateException
1267     *             if this cipher instance is not initialized for encryption or
1268     *             decryption.
1269     */
1270    public final int doFinal(ByteBuffer input, ByteBuffer output)
1271            throws ShortBufferException, IllegalBlockSizeException,
1272            BadPaddingException {
1273        if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
1274            throw new IllegalStateException(
1275                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1276        }
1277        if (input == output) {
1278            throw new IllegalArgumentException(
1279                    Messages.getString("crypto.2E")); //$NON-NLS-1$
1280        }
1281        return spiImpl.engineDoFinal(input, output);
1282    }
1283
1284    /**
1285     * Wraps a key using this cipher instance.
1286     *
1287     * @param key
1288     *            the key to wrap.
1289     * @return the wrapped key.
1290     * @throws IllegalBlockSizeException
1291     *             if the size of the resulting bytes is not a multiple of the
1292     *             cipher block size.
1293     * @throws InvalidKeyException
1294     *             if this cipher instance can not wrap this key.
1295     * @throws IllegalStateException
1296     *             if this cipher instance is not initialized for wrapping.
1297     */
1298    public final byte[] wrap(Key key) throws IllegalBlockSizeException,
1299            InvalidKeyException {
1300        if (mode != WRAP_MODE) {
1301            throw new IllegalStateException(
1302                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1303        }
1304        return spiImpl.engineWrap(key);
1305    }
1306
1307    /**
1308     * Unwraps a key using this cipher instance.
1309     *
1310     * @param wrappedKey
1311     *            the wrapped key to unwrap.
1312     * @param wrappedKeyAlgorithm
1313     *            the algorithm for the wrapped key.
1314     * @param wrappedKeyType
1315     *            the type of the wrapped key (one of: {@code SECRET_KEY
1316     *            <code>, <code>PRIVATE_KEY} or {@code PUBLIC_KEY})
1317     * @return the unwrapped key
1318     * @throws InvalidKeyException
1319     *             if the {@code wrappedKey} can not be unwrapped to a key of
1320     *             type {@code wrappedKeyType} for the {@code
1321     *             wrappedKeyAlgorithm}.
1322     * @throws NoSuchAlgorithmException
1323     *             if no provider can be found that can create a key of type
1324     *             {@code wrappedKeyType} for the {@code wrappedKeyAlgorithm}.
1325     * @throws IllegalStateException
1326     *             if this cipher instance is not initialized for unwrapping.
1327     */
1328    public final Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
1329            int wrappedKeyType) throws InvalidKeyException,
1330            NoSuchAlgorithmException {
1331        if (mode != UNWRAP_MODE) {
1332            throw new IllegalStateException(
1333                    Messages.getString("crypto.1C")); //$NON-NLS-1$
1334        }
1335        return spiImpl.engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
1336                wrappedKeyType);
1337    }
1338
1339    /**
1340     * Returns the maximum key length for the specified transformation.
1341     *
1342     * @param transformation
1343     *            the transformation name.
1344     * @return the maximum key length, currently {@code Integer.MAX_VALUE}.
1345     * @throws NoSuchAlgorithmException
1346     *             if no provider for the specified {@code transformation} can
1347     *             be found.
1348     * @throws NullPointerException
1349     *             if {@code transformation} is {@code null}.
1350     */
1351    public static final int getMaxAllowedKeyLength(String transformation)
1352            throws NoSuchAlgorithmException {
1353        if (transformation == null) {
1354            throw new NullPointerException();
1355        }
1356        checkTransformation(transformation);
1357        //FIXME jurisdiction policy files
1358        return Integer.MAX_VALUE;
1359    }
1360
1361    /**
1362     * Returns the maximum cipher parameter value for the specified
1363     * transformation. If there is no maximum limit, {@code null} is returned.
1364     *
1365     * @param transformation
1366     *            the transformation name.
1367     * @return a parameter spec holding the maximum value or {@code null}.
1368     *         Currently {@code null}.
1369     * @throws NoSuchAlgorithmException
1370     *             if no provider for the specified {@code transformation} can
1371     *             be found.
1372     * @throws NullPointerException
1373     *             if {@code transformation} is {@code null}.
1374     */
1375    public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(
1376            String transformation) throws NoSuchAlgorithmException {
1377        if (transformation == null) {
1378            throw new NullPointerException();
1379        }
1380        checkTransformation(transformation);
1381        //FIXME jurisdiction policy files
1382        return null;
1383    }
1384}
1385