summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/crypto/Cipher.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/crypto/Cipher.java')
-rw-r--r--libjava/classpath/javax/crypto/Cipher.java1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/libjava/classpath/javax/crypto/Cipher.java b/libjava/classpath/javax/crypto/Cipher.java
new file mode 100644
index 000000000..9b7d8f9fe
--- /dev/null
+++ b/libjava/classpath/javax/crypto/Cipher.java
@@ -0,0 +1,1148 @@
+/* Cipher.java -- Interface to a cryptographic cipher.
+ Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.crypto;
+
+import gnu.java.security.Engine;
+
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.StringTokenizer;
+
+/**
+ * <p>This class implements a cryptographic cipher for transforming
+ * data.</p>
+ *
+ * <p>Ciphers cannot be instantiated directly; rather one of the
+ * <code>getInstance</code> must be used to instantiate a given
+ * <i>transformation</i>, optionally with a specific provider.</p>
+ *
+ * <p>A transformation is of the form:</p>
+ *
+ * <ul>
+ * <li><i>algorithm</i>/<i>mode</i>/<i>padding</i>, or</li>
+ * <li><i>algorithm</i>
+ * </ul>
+ *
+ * <p>where <i>algorithm</i> is the base name of a cryptographic cipher
+ * (such as "AES"), <i>mode</i> is the abbreviated name of a block
+ * cipher mode (such as "CBC" for cipher block chaining mode), and
+ * <i>padding</i> is the name of a padding scheme (such as
+ * "PKCS5Padding"). If only the algorithm name is supplied, then the
+ * provider-specific default mode and padding will be used.</p>
+ *
+ * <p>An example transformation is:</p>
+ *
+ * <blockquote><code>Cipher c =
+ * Cipher.getInstance("AES/CBC/PKCS5Padding");</code></blockquote>
+ *
+ * <p>Finally, when requesting a block cipher in stream cipher mode
+ * (such as <acronym title="Advanced Encryption Standard">AES</acronym>
+ * in OFB or CFB mode) the number of bits to be processed
+ * at a time may be specified by appending it to the name of the mode;
+ * e.g. <code>"AES/OFB8/NoPadding"</code>. If no such number is
+ * specified a provider-specific default value is used.</p>
+ *
+ * @author Casey Marshall (csm@gnu.org)
+ * @see java.security.KeyGenerator
+ * @see javax.crypto.SecretKey
+ */
+public class Cipher
+{
+
+ // Constants and variables.
+ // ------------------------------------------------------------------------
+
+ private static final String SERVICE = "Cipher";
+
+ /**
+ * The decryption operation mode.
+ */
+ public static final int DECRYPT_MODE = 2;
+
+ /**
+ * The encryption operation mode.
+ */
+ public static final int ENCRYPT_MODE = 1;
+
+ /**
+ * Constant for when the key to be unwrapped is a private key.
+ */
+ public static final int PRIVATE_KEY = 2;
+
+ /**
+ * Constant for when the key to be unwrapped is a public key.
+ */
+ public static final int PUBLIC_KEY = 1;
+
+ /**
+ * Constant for when the key to be unwrapped is a secret key.
+ */
+ public static final int SECRET_KEY = 3;
+
+ /**
+ * The key unwrapping operation mode.
+ */
+ public static final int UNWRAP_MODE = 4;
+
+ /**
+ * The key wrapping operation mode.
+ */
+ public static final int WRAP_MODE = 3;
+
+ /**
+ * The uninitialized state. This state signals that any of the
+ * <code>init</code> methods have not been called, and therefore no
+ * transformations can be done.
+ */
+ private static final int INITIAL_STATE = 0;
+
+ /** The underlying cipher service provider interface. */
+ private CipherSpi cipherSpi;
+
+ /** The provider from which this instance came. */
+ private Provider provider;
+
+ /** The transformation requested. */
+ private String transformation;
+
+ /** Our current state (encrypting, wrapping, etc.) */
+ private int state;
+
+ /**
+ * Creates a new cipher instance for the given transformation.
+ * <p>
+ * The installed providers are tried in order for an implementation, and the
+ * first appropriate instance is returned. If no installed provider can
+ * provide the implementation, an appropriate exception is thrown.
+ *
+ * @param transformation The transformation to create.
+ * @return An appropriate cipher for this transformation.
+ * @throws NoSuchAlgorithmException If no installed provider can supply the
+ * appropriate cipher or mode.
+ * @throws NoSuchPaddingException If no installed provider can supply the
+ * appropriate padding.
+ */
+ public static final Cipher getInstance(String transformation)
+ throws NoSuchAlgorithmException, NoSuchPaddingException
+ {
+ Provider[] p = Security.getProviders();
+ NoSuchAlgorithmException lastException = null;
+ NoSuchPaddingException lastPaddingException = null;
+ for (int i = 0; i < p.length; i++)
+ try
+ {
+ return getInstance(transformation, p[i]);
+ }
+ catch (NoSuchAlgorithmException x)
+ {
+ lastException = x;
+ lastPaddingException = null;
+ }
+ catch (NoSuchPaddingException x)
+ {
+ lastPaddingException = x;
+ }
+ if (lastPaddingException != null)
+ throw lastPaddingException;
+ if (lastException != null)
+ throw lastException;
+ throw new NoSuchAlgorithmException(transformation);
+ }
+
+ /**
+ * Creates a new cipher instance for the given transformation and the named
+ * provider.
+ *
+ * @param transformation The transformation to create.
+ * @param provider The name of the provider to use.
+ * @return An appropriate cipher for this transformation.
+ * @throws NoSuchAlgorithmException If the provider cannot supply the
+ * appropriate cipher or mode.
+ * @throws NoSuchProviderException If the named provider is not installed.
+ * @throws NoSuchPaddingException If the provider cannot supply the
+ * appropriate padding.
+ * @throws IllegalArgumentException if either <code>transformation</code> or
+ * <code>provider</code> is <code>null</code>.
+ */
+ public static final Cipher getInstance(String transformation, String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ NoSuchPaddingException
+ {
+ if (provider == null)
+ throw new IllegalArgumentException("provider MUST NOT be null");
+ Provider p = Security.getProvider(provider);
+ if (p == null)
+ throw new NoSuchProviderException(provider);
+ return getInstance(transformation, p);
+ }
+
+ /**
+ * Creates a new cipher instance for a given transformation from a given
+ * provider.
+ *
+ * @param transformation The transformation to create.
+ * @param provider The provider to use.
+ * @return An appropriate cipher for this transformation.
+ * @throws NoSuchAlgorithmException If the given provider cannot supply the
+ * appropriate cipher or mode.
+ * @throws NoSuchPaddingException If the given provider cannot supply the
+ * appropriate padding scheme.
+ */
+ public static final Cipher getInstance(String transformation,
+ Provider provider)
+ throws NoSuchAlgorithmException, NoSuchPaddingException
+ {
+ StringBuilder sb = new StringBuilder().append("Cipher transformation [")
+ .append(transformation).append("] from provider [")
+ .append(provider).append("] ");
+ Throwable cause;
+ Object spi;
+ CipherSpi result;
+ if (transformation.indexOf('/') < 0)
+ {
+ try
+ {
+ spi = Engine.getInstance(SERVICE, transformation, provider);
+ return new Cipher((CipherSpi) spi, provider, transformation);
+ }
+ catch (Exception e)
+ {
+ if (e instanceof NoSuchAlgorithmException)
+ throw (NoSuchAlgorithmException) e;
+ cause = e;
+ }
+ }
+ else
+ {
+ StringTokenizer tok = new StringTokenizer(transformation, "/");
+ if (tok.countTokens() != 3)
+ throw new NoSuchAlgorithmException(sb.append("is malformed").toString());
+
+ String alg = tok.nextToken();
+ String mode = tok.nextToken();
+ String pad = tok.nextToken();
+ try
+ {
+ spi = Engine.getInstance(SERVICE, transformation, provider);
+ return new Cipher((CipherSpi) spi, provider, transformation);
+ }
+ catch (Exception e)
+ {
+ cause = e;
+ }
+
+ try
+ {
+ spi = Engine.getInstance(SERVICE, alg + '/' + mode, provider);
+ result = (CipherSpi) spi;
+ result.engineSetPadding(pad);
+ return new Cipher(result, provider, transformation);
+ }
+ catch (Exception e)
+ {
+ if (e instanceof NoSuchPaddingException)
+ throw (NoSuchPaddingException) e;
+ cause = e;
+ }
+
+ try
+ {
+ spi = Engine.getInstance(SERVICE, alg + "//" + pad, provider);
+ result = (CipherSpi) spi;
+ result.engineSetMode(mode);
+ return new Cipher(result, provider, transformation);
+ }
+ catch (Exception e)
+ {
+ cause = e;
+ }
+
+ try
+ {
+ spi = Engine.getInstance(SERVICE, alg, provider);
+ result = (CipherSpi) spi;
+ result.engineSetMode(mode);
+ result.engineSetPadding(pad);
+ return new Cipher(result, provider, transformation);
+ }
+ catch (Exception e)
+ {
+ if (e instanceof NoSuchPaddingException)
+ throw (NoSuchPaddingException) e;
+ cause = e;
+ }
+ }
+ sb.append("could not be created");
+ NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString());
+ x.initCause(cause);
+ throw x;
+ }
+
+ /**
+ * Create a cipher.
+ *
+ * @param cipherSpi The underlying implementation of the cipher.
+ * @param provider The provider of this cipher implementation.
+ * @param transformation The transformation this cipher performs.
+ */
+ protected
+ Cipher(CipherSpi cipherSpi, Provider provider, String transformation)
+ {
+ this.cipherSpi = cipherSpi;
+ this.provider = provider;
+ this.transformation = transformation;
+ state = INITIAL_STATE;
+ }
+
+ /**
+ * Get the name that this cipher instance was created with; this is
+ * equivalent to the "transformation" argument given to any of the
+ * {@link #getInstance()} methods.
+ *
+ * @return The cipher name.
+ */
+ public final String getAlgorithm()
+ {
+ return transformation;
+ }
+
+ /**
+ * Return the size of blocks, in bytes, that this cipher processes.
+ *
+ * @return The block size.
+ */
+ public final int getBlockSize()
+ {
+ if (cipherSpi != null)
+ {
+ return cipherSpi.engineGetBlockSize();
+ }
+ return 1;
+ }
+
+ /**
+ * Return the currently-operating {@link ExemptionMechanism}.
+ *
+ * @return null, currently.
+ */
+ public final ExemptionMechanism getExemptionMechanism()
+ {
+ return null;
+ }
+
+ /**
+ * Return the <i>initialization vector</i> that this instance was
+ * initialized with.
+ *
+ * @return The IV.
+ */
+ public final byte[] getIV()
+ {
+ if (cipherSpi != null)
+ {
+ return cipherSpi.engineGetIV();
+ }
+ return null;
+ }
+
+ /**
+ * Return the {@link java.security.AlgorithmParameters} that this
+ * instance was initialized with.
+ *
+ * @return The parameters.
+ */
+ public final AlgorithmParameters getParameters()
+ {
+ if (cipherSpi != null) {
+ return cipherSpi.engineGetParameters();
+ }
+ return null;
+ }
+
+ /**
+ * Return this cipher's provider.
+ *
+ * @return The provider.
+ */
+ public final Provider getProvider()
+ {
+ return provider;
+ }
+
+ /**
+ * Finishes a multi-part transformation, and returns the final
+ * transformed bytes.
+ *
+ * @return The final transformed bytes.
+ * @throws java.lang.IllegalStateException If this instance has not
+ * been initialized, or if a <tt>doFinal</tt> call has already
+ * been made.
+ * @throws javax.crypto.IllegalBlockSizeException If this instance has
+ * no padding and the input is not a multiple of this cipher's
+ * block size.
+ * @throws javax.crypto.BadPaddingException If this instance is
+ * decrypting and the padding bytes do not match this
+ * instance's padding scheme.
+ */
+ public final byte[] doFinal()
+ throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
+ {
+ return doFinal(new byte[0], 0, 0);
+ }
+
+ /**
+ * Finishes a multi-part transformation or does an entire
+ * transformation on the input, and returns the transformed bytes.
+ *
+ * @param input The final input bytes.
+ * @return The final transformed bytes.
+ * @throws java.lang.IllegalStateException If this instance has not
+ * been initialized, or if a <tt>doFinal</tt> call has already
+ * been made.
+ * @throws javax.crypto.IllegalBlockSizeException If this instance has
+ * no padding and the input is not a multiple of this cipher's
+ * block size.
+ * @throws javax.crypto.BadPaddingException If this instance is
+ * decrypting and the padding bytes do not match this
+ * instance's padding scheme.
+ */
+ public final byte[] doFinal(byte[] input)
+ throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
+ {
+ return doFinal(input, 0, input.length);
+ }
+
+ /**
+ * Finishes a multi-part transformation or does an entire
+ * transformation on the input, and returns the transformed bytes.
+ *
+ * @param input The final input bytes.
+ * @param inputOffset The index in the input bytes to start.
+ * @param inputLength The number of bytes to read from the input.
+ * @return The final transformed bytes.
+ * @throws java.lang.IllegalStateException If this instance has not
+ * been initialized, or if a <tt>doFinal</tt> call has already
+ * been made.
+ * @throws javax.crypto.IllegalBlockSizeException If this instance has
+ * no padding and the input is not a multiple of this cipher's
+ * block size.
+ * @throws javax.crypto.BadPaddingException If this instance is
+ * decrypting and the padding bytes do not match this
+ * instance's padding scheme.
+ */
+ public final byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+ throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
+ {
+ if (cipherSpi == null)
+ {
+ byte[] b = new byte[inputLength];
+ System.arraycopy(input, inputOffset, b, 0, inputLength);
+ return b;
+ }
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ {
+ throw new IllegalStateException("neither encrypting nor decrypting");
+ }
+ return cipherSpi.engineDoFinal(input, inputOffset, inputLength);
+ }
+
+ /**
+ * Finishes a multi-part transformation and stores the transformed
+ * bytes into the given array.
+ *
+ * @param output The destination for the transformed bytes.
+ * @param outputOffset The offset in <tt>output</tt> to start storing
+ * bytes.
+ * @return The number of bytes placed into the output array.
+ * @throws java.lang.IllegalStateException If this instance has not
+ * been initialized, or if a <tt>doFinal</tt> call has already
+ * been made.
+ * @throws javax.crypto.IllegalBlockSizeException If this instance has
+ * no padding and the input is not a multiple of this cipher's
+ * block size.
+ * @throws javax.crypto.BadPaddingException If this instance is
+ * decrypting and the padding bytes do not match this
+ * instance's padding scheme.
+ * @throws javax.crypto.ShortBufferException If the output array is
+ * not large enough to hold the transformed bytes.
+ */
+ public final int doFinal(byte[] output, int outputOffset)
+ throws IllegalStateException, IllegalBlockSizeException, BadPaddingException,
+ ShortBufferException
+ {
+ if (cipherSpi == null)
+ {
+ return 0;
+ }
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ {
+ throw new IllegalStateException("neither encrypting nor decrypting");
+ }
+ return cipherSpi.engineDoFinal(new byte[0], 0, 0, output, outputOffset);
+ }
+
+ /**
+ * Finishes a multi-part transformation or transforms a portion of a
+ * byte array, and stores the result in the given byte array.
+ *
+ * @param input The input bytes.
+ * @param inputOffset The index in <tt>input</tt> to start.
+ * @param inputLength The number of bytes to transform.
+ * @param output The output buffer.
+ * @param outputOffset The index in <tt>output</tt> to start.
+ * @return The number of bytes placed into the output array.
+ * @throws java.lang.IllegalStateException If this instance has not
+ * been initialized, or if a <tt>doFinal</tt> call has already
+ * been made.
+ * @throws javax.crypto.IllegalBlockSizeException If this instance has
+ * no padding and the input is not a multiple of this cipher's
+ * block size.
+ * @throws javax.crypto.BadPaddingException If this instance is
+ * decrypting and the padding bytes do not match this
+ * instance's padding scheme.
+ * @throws javax.crypto.ShortBufferException If the output array is
+ * not large enough to hold the transformed bytes.
+ */
+ public final int doFinal(byte[] input, int inputOffset, int inputLength,
+ byte[] output, int outputOffset)
+ throws IllegalStateException, IllegalBlockSizeException, BadPaddingException,
+ ShortBufferException
+ {
+ if (cipherSpi == null)
+ {
+ if (inputLength > output.length - outputOffset)
+ {
+ throw new ShortBufferException();
+ }
+ System.arraycopy(input, inputOffset, output, outputOffset, inputLength);
+ return inputLength;
+ }
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ {
+ throw new IllegalStateException("neither encrypting nor decrypting");
+ }
+ return cipherSpi.engineDoFinal(input, inputOffset, inputLength,
+ output, outputOffset);
+ }
+
+ public final int doFinal(byte[] input, int inputOffset, int inputLength,
+ byte[] output)
+ throws IllegalStateException, IllegalBlockSizeException, BadPaddingException,
+ ShortBufferException
+ {
+ return doFinal(input, inputOffset, inputLength, output, 0);
+ }
+
+ /**
+ * Finishes a multi-part transformation with, or completely
+ * transforms, a byte buffer, and stores the result into the output
+ * buffer.
+ *
+ * @param input The input buffer.
+ * @param output The output buffer.
+ * @return The number of bytes stored into the output buffer.
+ * @throws IllegalArgumentException If the input and output buffers
+ * are the same object.
+ * @throws IllegalStateException If this cipher was not initialized
+ * for encryption or decryption.
+ * @throws ReadOnlyBufferException If the output buffer is not
+ * writable.
+ * @throws IllegalBlockSizeException If this cipher requires a total
+ * input that is a multiple of its block size to complete this
+ * transformation.
+ * @throws ShortBufferException If the output buffer is not large
+ * enough to hold the transformed bytes.
+ * @throws BadPaddingException If the cipher is a block cipher with
+ * a padding scheme, and the decrypted bytes do not end with a
+ * valid padding.
+ * @since 1.5
+ */
+ public final int doFinal (ByteBuffer input, ByteBuffer output)
+ throws ReadOnlyBufferException, ShortBufferException,
+ BadPaddingException, IllegalBlockSizeException
+ {
+ if (input == output)
+ throw new IllegalArgumentException
+ ("input and output buffers cannot be the same");
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ throw new IllegalStateException
+ ("not initialized for encrypting or decrypting");
+ return cipherSpi.engineDoFinal (input, output);
+ }
+
+ /**
+ * Returns the size an output buffer needs to be if this cipher is
+ * updated with a number of bytes.
+ *
+ * @param inputLength The input length.
+ * @return The output length given this input length.
+ * @throws java.lang.IllegalStateException If this instance has not
+ * been initialized, or if a <tt>doFinal</tt> call has already
+ * been made.
+ */
+ public final int getOutputSize(int inputLength) throws IllegalStateException
+ {
+ if (cipherSpi == null)
+ return inputLength;
+ return cipherSpi.engineGetOutputSize(inputLength);
+ }
+
+ /**
+ * <p>Initialize this cipher with the public key from the given
+ * certificate.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>As per the Java 1.4 specification, if <code>cert</code> is an
+ * instance of an {@link java.security.cert.X509Certificate} and its
+ * <i>key usage</i> extension field is incompatible with
+ * <code>opmode</code> then an {@link
+ * java.security.InvalidKeyException} is thrown.</p>
+ *
+ * <p>If this cipher requires any random bytes (for example for an
+ * initilization vector) than the {@link java.security.SecureRandom}
+ * with the highest priority is used as the source of these bytes.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param certificate The certificate.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the certificate's public key, or if the
+ * public key cannot be used as described above.
+ */
+ public final void init(int opmode, Certificate certificate)
+ throws InvalidKeyException
+ {
+ init(opmode, certificate, new SecureRandom());
+ }
+
+ /**
+ * <p>Initialize this cipher with the supplied key.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>If this cipher requires any random bytes (for example for an
+ * initilization vector) than the {@link java.security.SecureRandom}
+ * with the highest priority is used as the source of these bytes.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param key The key.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the given key.
+ */
+ public final void init(int opmode, Key key) throws InvalidKeyException
+ {
+ if (cipherSpi != null)
+ {
+ cipherSpi.engineInit(opmode, key, new SecureRandom());
+ }
+ state = opmode;
+ }
+
+ /**
+ * <p>Initialize this cipher with the public key from the given
+ * certificate and the specified source of randomness.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>As per the Java 1.4 specification, if <code>cert</code> is an
+ * instance of an {@link java.security.cert.X509Certificate} and its
+ * <i>key usage</i> extension field is incompatible with
+ * <code>opmode</code> then an {@link
+ * java.security.InvalidKeyException} is thrown.</p>
+ *
+ * <p>If this cipher requires any random bytes (for example for an
+ * initilization vector) than the {@link java.security.SecureRandom}
+ * with the highest priority is used as the source of these bytes.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param certificate The certificate.
+ * @param random The source of randomness.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the certificate's public key, or if the
+ * public key cannot be used as described above.
+ */
+ public final void
+ init(int opmode, Certificate certificate, SecureRandom random)
+ throws InvalidKeyException
+ {
+ if (certificate instanceof X509Certificate)
+ {
+ boolean[] keyInfo = ((X509Certificate) certificate).getKeyUsage();
+ if (keyInfo != null)
+ {
+ switch (opmode)
+ {
+ case DECRYPT_MODE:
+ if (!keyInfo[3])
+ {
+ throw new InvalidKeyException(
+ "the certificate's key cannot be used for transforming data");
+ }
+ if (keyInfo[7])
+ {
+ throw new InvalidKeyException(
+ "the certificate's key can only be used for encryption");
+ }
+ break;
+
+ case ENCRYPT_MODE:
+ if (!keyInfo[3])
+ {
+ throw new InvalidKeyException(
+ "the certificate's key cannot be used for transforming data");
+ }
+ if (keyInfo[8])
+ {
+ throw new InvalidKeyException(
+ "the certificate's key can only be used for decryption");
+ }
+ break;
+
+ case UNWRAP_MODE:
+ if (!keyInfo[2] || keyInfo[7])
+ {
+ throw new InvalidKeyException(
+ "the certificate's key cannot be used for key unwrapping");
+ }
+ break;
+
+ case WRAP_MODE:
+ if (!keyInfo[2] || keyInfo[8])
+ {
+ throw new InvalidKeyException(
+ "the certificate's key cannot be used for key wrapping");
+ }
+ break;
+ }
+ }
+ }
+ init(opmode, certificate.getPublicKey(), random);
+ }
+
+ /**
+ * <p>Initialize this cipher with the supplied key and source of
+ * randomness.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param key The key.
+ * @param random The source of randomness to use.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the given key.
+ */
+ public final void init(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException
+ {
+ if (cipherSpi != null)
+ {
+ cipherSpi.engineInit(opmode, key, random);
+ }
+ state = opmode;
+ }
+
+ /**
+ * <p>Initialize this cipher with the supplied key and parameters.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>If this cipher requires any random bytes (for example for an
+ * initilization vector) then the {@link java.security.SecureRandom}
+ * with the highest priority is used as the source of these bytes.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param key The key.
+ * @param params The algorithm parameters to initialize this instance
+ * with.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the given key.
+ * @throws java.security.InvalidAlgorithmParameterException If the
+ * supplied parameters are inappropriate for this cipher.
+ */
+ public final void init(int opmode, Key key, AlgorithmParameters params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ init(opmode, key, params, new SecureRandom());
+ }
+
+ /**
+ * <p>Initialize this cipher with the supplied key and parameters.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>If this cipher requires any random bytes (for example for an
+ * initilization vector) then the {@link java.security.SecureRandom}
+ * with the highest priority is used as the source of these bytes.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param key The key.
+ * @param params The algorithm parameters to initialize this instance
+ * with.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the given key.
+ * @throws java.security.InvalidAlgorithmParameterException If the
+ * supplied parameters are inappropriate for this cipher.
+ */
+ public final void init(int opmode, Key key, AlgorithmParameterSpec params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ init(opmode, key, params, new SecureRandom());
+ }
+
+ /**
+ * <p>Initialize this cipher with the supplied key, parameters, and
+ * source of randomness.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param key The key.
+ * @param params The algorithm parameters to initialize this instance
+ * with.
+ * @param random The source of randomness to use.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the given key.
+ * @throws java.security.InvalidAlgorithmParameterException If the
+ * supplied parameters are inappropriate for this cipher.
+ */
+ public final void init(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ if (cipherSpi != null)
+ {
+ cipherSpi.engineInit(opmode, key, params, random);
+ }
+ state = opmode;
+ }
+
+ /**
+ * <p>Initialize this cipher with the supplied key, parameters, and
+ * source of randomness.</p>
+ *
+ * <p>The cipher will be initialized for encryption, decryption, key
+ * wrapping, or key unwrapping, depending upon whether the
+ * <code>opmode</code> argument is {@link #ENCRYPT_MODE}, {@link
+ * #DECRYPT_MODE}, {@link #WRAP_MODE}, or {@link #UNWRAP_MODE},
+ * respectively.</p>
+ *
+ * <p>A call to any of the <code>init</code> methods overrides the
+ * state of the instance, and is equivalent to creating a new instance
+ * and calling its <code>init</code> method.</p>
+ *
+ * @param opmode The operation mode to use.
+ * @param key The key.
+ * @param params The algorithm parameters to initialize this instance
+ * with.
+ * @param random The source of randomness to use.
+ * @throws java.security.InvalidKeyException If the underlying cipher
+ * instance rejects the given key.
+ * @throws java.security.InvalidAlgorithmParameterException If the
+ * supplied parameters are inappropriate for this cipher.
+ */
+ public final void init(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ if (cipherSpi != null)
+ {
+ cipherSpi.engineInit(opmode, key, params, random);
+ }
+ state = opmode;
+ }
+
+ /**
+ * Unwrap a previously-wrapped key.
+ *
+ * @param wrappedKey The wrapped key.
+ * @param wrappedKeyAlgorithm The algorithm with which the key was
+ * wrapped.
+ * @param wrappedKeyType The type of key (public, private, or
+ * secret) that this wrapped key respresents.
+ * @return The unwrapped key.
+ * @throws java.lang.IllegalStateException If this instance has not be
+ * initialized for unwrapping.
+ * @throws java.security.InvalidKeyException If <code>wrappedKey</code>
+ * is not a wrapped key, if the algorithm cannot unwrap this
+ * key, or if the unwrapped key's type differs from the
+ * specified type.
+ * @throws java.security.NoSuchAlgorithmException If
+ * <code>wrappedKeyAlgorithm</code> is not a valid algorithm
+ * name.
+ */
+ public final Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+ int wrappedKeyType)
+ throws IllegalStateException, InvalidKeyException, NoSuchAlgorithmException
+ {
+ if (cipherSpi == null)
+ {
+ return null;
+ }
+ if (state != UNWRAP_MODE)
+ {
+ throw new IllegalStateException("instance is not for unwrapping");
+ }
+ return cipherSpi.engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
+ wrappedKeyType);
+ }
+
+ /**
+ * Continue a multi-part transformation on an entire byte array,
+ * returning the transformed bytes.
+ *
+ * @param input The input bytes.
+ * @return The transformed bytes.
+ * @throws java.lang.IllegalStateException If this cipher was not
+ * initialized for encryption or decryption.
+ */
+ public final byte[] update(byte[] input) throws IllegalStateException
+ {
+ return update(input, 0, input.length);
+ }
+
+ /**
+ * Continue a multi-part transformation on part of a byte array,
+ * returning the transformed bytes.
+ *
+ * @param input The input bytes.
+ * @param inputOffset The index in the input to start.
+ * @param inputLength The number of bytes to transform.
+ * @return The transformed bytes.
+ * @throws java.lang.IllegalStateException If this cipher was not
+ * initialized for encryption or decryption.
+ */
+ public final byte[] update(byte[] input, int inputOffset, int inputLength)
+ throws IllegalStateException
+ {
+ if (cipherSpi == null)
+ {
+ byte[] b = new byte[inputLength];
+ System.arraycopy(input, inputOffset, b, 0, inputLength);
+ return b;
+ }
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ {
+ throw new IllegalStateException(
+ "cipher is not for encrypting or decrypting");
+ }
+ return cipherSpi.engineUpdate(input, inputOffset, inputLength);
+ }
+
+ /**
+ * Continue a multi-part transformation on part of a byte array,
+ * placing the transformed bytes into the given array.
+ *
+ * @param input The input bytes.
+ * @param inputOffset The index in the input to start.
+ * @param inputLength The number of bytes to transform.
+ * @param output The output byte array.
+ * @return The number of transformed bytes.
+ * @throws java.lang.IllegalStateException If this cipher was not
+ * initialized for encryption or decryption.
+ * @throws javax.security.ShortBufferException If there is not enough
+ * room in the output array to hold the transformed bytes.
+ */
+ public final int update(byte[] input, int inputOffset, int inputLength,
+ byte[] output)
+ throws IllegalStateException, ShortBufferException
+ {
+ return update(input, inputOffset, inputLength, output, 0);
+ }
+
+ /**
+ * Continue a multi-part transformation on part of a byte array,
+ * placing the transformed bytes into the given array.
+ *
+ * @param input The input bytes.
+ * @param inputOffset The index in the input to start.
+ * @param inputLength The number of bytes to transform.
+ * @param output The output byte array.
+ * @param outputOffset The index in the output array to start.
+ * @return The number of transformed bytes.
+ * @throws java.lang.IllegalStateException If this cipher was not
+ * initialized for encryption or decryption.
+ * @throws javax.security.ShortBufferException If there is not enough
+ * room in the output array to hold the transformed bytes.
+ */
+ public final int update(byte[] input, int inputOffset, int inputLength,
+ byte[] output, int outputOffset)
+ throws IllegalStateException, ShortBufferException
+ {
+ if (cipherSpi == null)
+ {
+ if (inputLength > output.length - outputOffset)
+ {
+ throw new ShortBufferException();
+ }
+ System.arraycopy(input, inputOffset, output, outputOffset, inputLength);
+ return inputLength;
+ }
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ {
+ throw new IllegalStateException(
+ "cipher is not for encrypting or decrypting");
+ }
+ return cipherSpi.engineUpdate(input, inputOffset, inputLength,
+ output, outputOffset);
+ }
+
+ /**
+ * Continue a multi-part transformation on a byte buffer, storing
+ * the transformed bytes into another buffer.
+ *
+ * @param input The input buffer.
+ * @param output The output buffer.
+ * @return The number of bytes stored in <i>output</i>.
+ * @throws IllegalArgumentException If the two buffers are the same
+ * object.
+ * @throws IllegalStateException If this cipher was not initialized
+ * for encrypting or decrypting.
+ * @throws ReadOnlyBufferException If the output buffer is not
+ * writable.
+ * @throws ShortBufferException If the output buffer does not have
+ * enough available space for the transformed bytes.
+ * @since 1.5
+ */
+ public final int update (ByteBuffer input, ByteBuffer output)
+ throws ReadOnlyBufferException, ShortBufferException
+ {
+ if (input == output)
+ throw new IllegalArgumentException
+ ("input and output buffers must be different");
+ if (state != ENCRYPT_MODE && state != DECRYPT_MODE)
+ throw new IllegalStateException
+ ("not initialized for encryption or decryption");
+ return cipherSpi.engineUpdate (input, output);
+ }
+
+ /**
+ * Wrap a key.
+ *
+ * @param key The key to wrap.
+ * @return The wrapped key.
+ * @throws java.lang.IllegalStateException If this instance was not
+ * initialized for key wrapping.
+ * @throws javax.crypto.IllegalBlockSizeException If this instance has
+ * no padding and the key is not a multiple of the block size.
+ * @throws java.security.InvalidKeyException If this instance cannot
+ * wrap this key.
+ */
+ public final byte[] wrap(Key key)
+ throws IllegalStateException, IllegalBlockSizeException, InvalidKeyException
+ {
+ if (cipherSpi == null)
+ {
+ return null;
+ }
+ if (state != WRAP_MODE)
+ {
+ throw new IllegalStateException("instance is not for key wrapping");
+ }
+ return cipherSpi.engineWrap(key);
+ }
+}