--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/crypto/Cipher.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,2420 @@
+/*
+ * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.crypto;
+
+import java.util.*;
+import java.util.regex.*;
+
+import static java.util.Locale.ENGLISH;
+
+import java.security.*;
+import java.security.Provider.Service;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.crypto.spec.*;
+
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+
+import sun.security.util.Debug;
+import sun.security.jca.*;
+import sun.security.jca.GetInstance.Instance;
+
+/**
+ * This class provides the functionality of a cryptographic cipher for
+ * encryption and decryption. It forms the core of the Java Cryptographic
+ * Extension (JCE) framework.
+ *
+ * <p>In order to create a Cipher object, the application calls the
+ * Cipher's <code>getInstance</code> method, and passes the name of the
+ * requested <i>transformation</i> to it. Optionally, the name of a provider
+ * may be specified.
+ *
+ * <p>A <i>transformation</i> is a string that describes the operation (or
+ * set of operations) to be performed on the given input, to produce some
+ * output. A transformation always includes the name of a cryptographic
+ * algorithm (e.g., <i>DES</i>), and may be followed by a feedback mode and
+ * padding scheme.
+ *
+ * <p> A transformation is of the form:<p>
+ *
+ * <ul>
+ * <li>"<i>algorithm/mode/padding</i>" or
+ * <p>
+ * <li>"<i>algorithm</i>"
+ * </ul>
+ *
+ * <P> (in the latter case,
+ * provider-specific default values for the mode and padding scheme are used).
+ * For example, the following is a valid transformation:<p>
+ *
+ * <pre>
+ * Cipher c = Cipher.getInstance("<i>DES/CBC/PKCS5Padding</i>");
+ * </pre>
+ *
+ * Using modes such as <code>CFB</code> and <code>OFB<code>, block
+ * ciphers can encrypt data in units smaller than the cipher's actual
+ * block size. When requesting such a mode, you may optionally specify
+ * the number of bits to be processed at a time by appending this number
+ * to the mode name as shown in the "<code>DES/CFB8/NoPadding</code>" and
+ * "<code>DES/OFB32/PKCS5Padding</code>" transformations. If no such
+ * number is specified, a provider-specific default is used. (For
+ * example, the SunJCE provider uses a default of 64 bits for DES.)
+ * Thus, block ciphers can be turned into byte-oriented stream ciphers by
+ * using an 8 bit mode such as CFB8 or OFB8.
+ *
+ * @author Jan Luehe
+ * @see KeyGenerator
+ * @see SecretKey
+ * @since 1.4
+ */
+
+public class Cipher {
+
+ private static final Debug debug =
+ Debug.getInstance("jca", "Cipher");
+
+ /**
+ * Constant used to initialize cipher to encryption mode.
+ */
+ public static final int ENCRYPT_MODE = 1;
+
+ /**
+ * Constant used to initialize cipher to decryption mode.
+ */
+ public static final int DECRYPT_MODE = 2;
+
+ /**
+ * Constant used to initialize cipher to key-wrapping mode.
+ */
+ public static final int WRAP_MODE = 3;
+
+ /**
+ * Constant used to initialize cipher to key-unwrapping mode.
+ */
+ public static final int UNWRAP_MODE = 4;
+
+ /**
+ * Constant used to indicate the to-be-unwrapped key is a "public key".
+ */
+ public static final int PUBLIC_KEY = 1;
+
+ /**
+ * Constant used to indicate the to-be-unwrapped key is a "private key".
+ */
+ public static final int PRIVATE_KEY = 2;
+
+ /**
+ * Constant used to indicate the to-be-unwrapped key is a "secret key".
+ */
+ public static final int SECRET_KEY = 3;
+
+ // The provider
+ private Provider provider;
+
+ // The provider implementation (delegate)
+ private CipherSpi spi;
+
+ // The transformation
+ private String transformation;
+
+ // Crypto permission representing the maximum allowable cryptographic
+ // strength that this Cipher object can be used for. (The cryptographic
+ // strength is a function of the keysize and algorithm parameters encoded
+ // in the crypto permission.)
+ private CryptoPermission cryptoPerm;
+
+ // The exemption mechanism that needs to be enforced
+ private ExemptionMechanism exmech;
+
+ // Flag which indicates whether or not this cipher has been initialized
+ private boolean initialized = false;
+
+ // The operation mode - store the operation mode after the
+ // cipher has been initialized.
+ private int opmode = 0;
+
+ // The OID for the KeyUsage extension in an X.509 v3 certificate
+ private static final String KEY_USAGE_EXTENSION_OID = "2.5.29.15";
+
+ // next SPI to try in provider selection
+ // null once provider is selected
+ private CipherSpi firstSpi;
+
+ // next service to try in provider selection
+ // null once provider is selected
+ private Service firstService;
+
+ // remaining services to try in provider selection
+ // null once provider is selected
+ private Iterator serviceIterator;
+
+ // list of transform Strings to lookup in the provider
+ private List transforms;
+
+ private final Object lock;
+
+ /**
+ * Creates a Cipher object.
+ *
+ * @param cipherSpi the delegate
+ * @param provider the provider
+ * @param transformation the transformation
+ */
+ protected Cipher(CipherSpi cipherSpi,
+ Provider provider,
+ String transformation) {
+ // See bug 4341369 & 4334690 for more info.
+ // If the caller is trusted, then okey.
+ // Otherwise throw a NullPointerException.
+ if (!JceSecurityManager.INSTANCE.isCallerTrusted()) {
+ throw new NullPointerException();
+ }
+ this.spi = cipherSpi;
+ this.provider = provider;
+ this.transformation = transformation;
+ this.cryptoPerm = CryptoAllPermission.INSTANCE;
+ this.lock = null;
+ }
+
+ /**
+ * Creates a Cipher object. Called internally and by NullCipher.
+ *
+ * @param cipherSpi the delegate
+ * @param transformation the transformation
+ */
+ Cipher(CipherSpi cipherSpi, String transformation) {
+ this.spi = cipherSpi;
+ this.transformation = transformation;
+ this.cryptoPerm = CryptoAllPermission.INSTANCE;
+ this.lock = null;
+ }
+
+ private Cipher(CipherSpi firstSpi, Service firstService,
+ Iterator serviceIterator, String transformation, List transforms) {
+ this.firstSpi = firstSpi;
+ this.firstService = firstService;
+ this.serviceIterator = serviceIterator;
+ this.transforms = transforms;
+ this.transformation = transformation;
+ this.lock = new Object();
+ }
+
+ private static String[] tokenizeTransformation(String transformation)
+ throws NoSuchAlgorithmException {
+ if (transformation == null) {
+ throw new NoSuchAlgorithmException("No transformation given");
+ }
+ /*
+ * array containing the components of a Cipher transformation:
+ *
+ * index 0: algorithm component (e.g., DES)
+ * index 1: feedback component (e.g., CFB)
+ * index 2: padding component (e.g., PKCS5Padding)
+ */
+ String[] parts = new String[3];
+ int count = 0;
+ StringTokenizer parser = new StringTokenizer(transformation, "/");
+ try {
+ while (parser.hasMoreTokens() && count < 3) {
+ parts[count++] = parser.nextToken().trim();
+ }
+ if (count == 0 || count == 2 || parser.hasMoreTokens()) {
+ throw new NoSuchAlgorithmException("Invalid transformation"
+ + " format:" +
+ transformation);
+ }
+ } catch (NoSuchElementException e) {
+ throw new NoSuchAlgorithmException("Invalid transformation " +
+ "format:" + transformation);
+ }
+ if ((parts[0] == null) || (parts[0].length() == 0)) {
+ throw new NoSuchAlgorithmException("Invalid transformation:" +
+ "algorithm not specified-"
+ + transformation);
+ }
+ return parts;
+ }
+
+ // Provider attribute name for supported chaining mode
+ private final static String ATTR_MODE = "SupportedModes";
+ // Provider attribute name for supported padding names
+ private final static String ATTR_PAD = "SupportedPaddings";
+
+ // constants indicating whether the provider supports
+ // a given mode or padding
+ private final static int S_NO = 0; // does not support
+ private final static int S_MAYBE = 1; // unable to determine
+ private final static int S_YES = 2; // does support
+
+ /**
+ * Nested class to deal with modes and paddings.
+ */
+ private static class Transform {
+ // transform string to lookup in the provider
+ final String transform;
+ // the mode/padding suffix in upper case. for example, if the algorithm
+ // to lookup is "DES/CBC/PKCS5Padding" suffix is "/CBC/PKCS5PADDING"
+ // if loopup is "DES", suffix is the empty string
+ // needed because aliases prevent straight transform.equals()
+ final String suffix;
+ // value to pass to setMode() or null if no such call required
+ final String mode;
+ // value to pass to setPadding() or null if no such call required
+ final String pad;
+ Transform(String alg, String suffix, String mode, String pad) {
+ this.transform = alg + suffix;
+ this.suffix = suffix.toUpperCase(Locale.ENGLISH);
+ this.mode = mode;
+ this.pad = pad;
+ }
+ // set mode and padding for the given SPI
+ void setModePadding(CipherSpi spi) throws NoSuchAlgorithmException,
+ NoSuchPaddingException {
+ if (mode != null) {
+ spi.engineSetMode(mode);
+ }
+ if (pad != null) {
+ spi.engineSetPadding(pad);
+ }
+ }
+ // check whether the given services supports the mode and
+ // padding described by this Transform
+ int supportsModePadding(Service s) {
+ int smode = supportsMode(s);
+ if (smode == S_NO) {
+ return smode;
+ }
+ int spad = supportsPadding(s);
+ // our constants are defined so that Math.min() is a tri-valued AND
+ return Math.min(smode, spad);
+ }
+
+ // separate methods for mode and padding
+ // called directly by Cipher only to throw the correct exception
+ int supportsMode(Service s) {
+ return supports(s, ATTR_MODE, mode);
+ }
+ int supportsPadding(Service s) {
+ return supports(s, ATTR_PAD, pad);
+ }
+
+ private static int supports(Service s, String attrName, String value) {
+ if (value == null) {
+ return S_YES;
+ }
+ String regexp = s.getAttribute(attrName);
+ if (regexp == null) {
+ return S_MAYBE;
+ }
+ return matches(regexp, value) ? S_YES : S_NO;
+ }
+
+ // Map<String,Pattern> for previously compiled patterns
+ // XXX use ConcurrentHashMap once available
+ private final static Map patternCache =
+ Collections.synchronizedMap(new HashMap());
+
+ private static boolean matches(String regexp, String str) {
+ Pattern pattern = (Pattern)patternCache.get(regexp);
+ if (pattern == null) {
+ pattern = Pattern.compile(regexp);
+ patternCache.put(regexp, pattern);
+ }
+ return pattern.matcher(str.toUpperCase(Locale.ENGLISH)).matches();
+ }
+
+ }
+
+ private static List getTransforms(String transformation)
+ throws NoSuchAlgorithmException {
+ String[] parts = tokenizeTransformation(transformation);
+
+ String alg = parts[0];
+ String mode = parts[1];
+ String pad = parts[2];
+ if ((mode != null) && (mode.length() == 0)) {
+ mode = null;
+ }
+ if ((pad != null) && (pad.length() == 0)) {
+ pad = null;
+ }
+
+ if ((mode == null) && (pad == null)) {
+ // DES
+ Transform tr = new Transform(alg, "", null, null);
+ return Collections.singletonList(tr);
+ } else { // if ((mode != null) && (pad != null)) {
+ // DES/CBC/PKCS5Padding
+ List list = new ArrayList(4);
+ list.add(new Transform(alg, "/" + mode + "/" + pad, null, null));
+ list.add(new Transform(alg, "/" + mode, null, pad));
+ list.add(new Transform(alg, "//" + pad, mode, null));
+ list.add(new Transform(alg, "", mode, pad));
+ return list;
+ }
+ }
+
+ // get the transform matching the specified service
+ private static Transform getTransform(Service s, List transforms) {
+ String alg = s.getAlgorithm().toUpperCase(Locale.ENGLISH);
+ for (Iterator t = transforms.iterator(); t.hasNext(); ) {
+ Transform tr = (Transform)t.next();
+ if (alg.endsWith(tr.suffix)) {
+ return tr;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a <code>Cipher</code> object that implements the specified
+ * transformation.
+ *
+ * <p> This method traverses the list of registered security Providers,
+ * starting with the most preferred Provider.
+ * A new Cipher object encapsulating the
+ * CipherSpi implementation from the first
+ * Provider that supports the specified algorithm is returned.
+ *
+ * <p> Note that the list of registered providers may be retrieved via
+ * the {@link Security#getProviders() Security.getProviders()} method.
+ *
+ * @param transformation the name of the transformation, e.g.,
+ * <i>DES/CBC/PKCS5Padding</i>.
+ * See Appendix A in the
+ * <a href=
+ * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+ * Java Cryptography Architecture Reference Guide</a>
+ * for information about standard transformation names.
+ *
+ * @return a cipher that implements the requested transformation.
+ *
+ * @exception NoSuchAlgorithmException if <code>transformation</code>
+ * is null, empty, in an invalid format,
+ * or if no Provider supports a CipherSpi implementation for the
+ * specified algorithm.
+ *
+ * @exception NoSuchPaddingException if <code>transformation</code>
+ * contains a padding scheme that is not available.
+ *
+ * @see java.security.Provider
+ */
+ public static final Cipher getInstance(String transformation)
+ throws NoSuchAlgorithmException, NoSuchPaddingException
+ {
+ List transforms = getTransforms(transformation);
+ List cipherServices = new ArrayList(transforms.size());
+ for (Iterator t = transforms.iterator(); t.hasNext(); ) {
+ Transform transform = (Transform)t.next();
+ cipherServices.add(new ServiceId("Cipher", transform.transform));
+ }
+ List services = GetInstance.getServices(cipherServices);
+ // make sure there is at least one service from a signed provider
+ // and that it can use the specified mode and padding
+ Iterator t = services.iterator();
+ Exception failure = null;
+ while (t.hasNext()) {
+ Service s = (Service)t.next();
+ if (JceSecurity.canUseProvider(s.getProvider()) == false) {
+ continue;
+ }
+ Transform tr = getTransform(s, transforms);
+ if (tr == null) {
+ // should never happen
+ continue;
+ }
+ int canuse = tr.supportsModePadding(s);
+ if (canuse == S_NO) {
+ // does not support mode or padding we need, ignore
+ continue;
+ }
+ if (canuse == S_YES) {
+ return new Cipher(null, s, t, transformation, transforms);
+ } else { // S_MAYBE, try out if it works
+ try {
+ CipherSpi spi = (CipherSpi)s.newInstance(null);
+ tr.setModePadding(spi);
+ return new Cipher(spi, s, t, transformation, transforms);
+ } catch (Exception e) {
+ failure = e;
+ }
+ }
+ }
+ throw new NoSuchAlgorithmException
+ ("Cannot find any provider supporting " + transformation, failure);
+ }
+
+ /**
+ * Returns a <code>Cipher</code> object that implements the specified
+ * transformation.
+ *
+ * <p> A new Cipher object encapsulating the
+ * CipherSpi implementation from the specified provider
+ * is returned. The specified provider must be registered
+ * in the security provider list.
+ *
+ * <p> Note that the list of registered providers may be retrieved via
+ * the {@link Security#getProviders() Security.getProviders()} method.
+ *
+ * @param transformation the name of the transformation,
+ * e.g., <i>DES/CBC/PKCS5Padding</i>.
+ * See Appendix A in the
+ * <a href=
+ * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+ * Java Cryptography Architecture Reference Guide</a>
+ * for information about standard transformation names.
+ *
+ * @param provider the name of the provider.
+ *
+ * @return a cipher that implements the requested transformation.
+ *
+ * @exception NoSuchAlgorithmException if <code>transformation</code>
+ * is null, empty, in an invalid format,
+ * or if a CipherSpi implementation for the specified algorithm
+ * is not available from the specified provider.
+ *
+ * @exception NoSuchProviderException if the specified provider is not
+ * registered in the security provider list.
+ *
+ * @exception NoSuchPaddingException if <code>transformation</code>
+ * contains a padding scheme that is not available.
+ *
+ * @exception IllegalArgumentException if the <code>provider</code>
+ * is null or empty.
+ *
+ * @see java.security.Provider
+ */
+ public static final Cipher getInstance(String transformation,
+ String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ NoSuchPaddingException
+ {
+ if ((provider == null) || (provider.length() == 0)) {
+ throw new IllegalArgumentException("Missing provider");
+ }
+ Provider p = Security.getProvider(provider);
+ if (p == null) {
+ throw new NoSuchProviderException("No such provider: " +
+ provider);
+ }
+ return getInstance(transformation, p);
+ }
+
+ /**
+ * Returns a <code>Cipher</code> object that implements the specified
+ * transformation.
+ *
+ * <p> A new Cipher object encapsulating the
+ * CipherSpi implementation from the specified Provider
+ * object is returned. Note that the specified Provider object
+ * does not have to be registered in the provider list.
+ *
+ * @param transformation the name of the transformation,
+ * e.g., <i>DES/CBC/PKCS5Padding</i>.
+ * See Appendix A in the
+ * <a href=
+ * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+ * Java Cryptography Architecture Reference Guide</a>
+ * for information about standard transformation names.
+ *
+ * @param provider the provider.
+ *
+ * @return a cipher that implements the requested transformation.
+ *
+ * @exception NoSuchAlgorithmException if <code>transformation</code>
+ * is null, empty, in an invalid format,
+ * or if a CipherSpi implementation for the specified algorithm
+ * is not available from the specified Provider object.
+ *
+ * @exception NoSuchPaddingException if <code>transformation</code>
+ * contains a padding scheme that is not available.
+ *
+ * @exception IllegalArgumentException if the <code>provider</code>
+ * is null.
+ *
+ * @see java.security.Provider
+ */
+ public static final Cipher getInstance(String transformation,
+ Provider provider)
+ throws NoSuchAlgorithmException, NoSuchPaddingException
+ {
+ if (provider == null) {
+ throw new IllegalArgumentException("Missing provider");
+ }
+ Exception failure = null;
+ List transforms = getTransforms(transformation);
+ boolean providerChecked = false;
+ String paddingError = null;
+ for (Iterator t = transforms.iterator(); t.hasNext();) {
+ Transform tr = (Transform)t.next();
+ Service s = provider.getService("Cipher", tr.transform);
+ if (s == null) {
+ continue;
+ }
+ if (providerChecked == false) {
+ // for compatibility, first do the lookup and then verify
+ // the provider. this makes the difference between a NSAE
+ // and a SecurityException if the
+ // provider does not support the algorithm.
+ Exception ve = JceSecurity.getVerificationResult(provider);
+ if (ve != null) {
+ String msg = "JCE cannot authenticate the provider "
+ + provider.getName();
+ throw new SecurityException(msg, ve);
+ }
+ providerChecked = true;
+ }
+ if (tr.supportsMode(s) == S_NO) {
+ continue;
+ }
+ if (tr.supportsPadding(s) == S_NO) {
+ paddingError = tr.pad;
+ continue;
+ }
+ try {
+ CipherSpi spi = (CipherSpi)s.newInstance(null);
+ tr.setModePadding(spi);
+ Cipher cipher = new Cipher(spi, transformation);
+ cipher.provider = s.getProvider();
+ cipher.initCryptoPermission();
+ return cipher;
+ } catch (Exception e) {
+ failure = e;
+ }
+ }
+
+ // throw NoSuchPaddingException if the problem is with padding
+ if (failure instanceof NoSuchPaddingException) {
+ throw (NoSuchPaddingException)failure;
+ }
+ if (paddingError != null) {
+ throw new NoSuchPaddingException
+ ("Padding not supported: " + paddingError);
+ }
+ throw new NoSuchAlgorithmException
+ ("No such algorithm: " + transformation, failure);
+ }
+
+ // If the requested crypto service is export-controlled,
+ // determine the maximum allowable keysize.
+ private void initCryptoPermission() throws NoSuchAlgorithmException {
+ if (JceSecurity.isRestricted() == false) {
+ cryptoPerm = CryptoAllPermission.INSTANCE;
+ exmech = null;
+ return;
+ }
+ cryptoPerm = getConfiguredPermission(transformation);
+ // Instantiate the exemption mechanism (if required)
+ String exmechName = cryptoPerm.getExemptionMechanism();
+ if (exmechName != null) {
+ exmech = ExemptionMechanism.getInstance(exmechName);
+ }
+ }
+
+ // max number of debug warnings to print from chooseFirstProvider()
+ private static int warnCount = 10;
+
+ /**
+ * Choose the Spi from the first provider available. Used if
+ * delayed provider selection is not possible because init()
+ * is not the first method called.
+ */
+ void chooseFirstProvider() {
+ if (spi != null) {
+ return;
+ }
+ synchronized (lock) {
+ if (spi != null) {
+ return;
+ }
+ if (debug != null) {
+ int w = --warnCount;
+ if (w >= 0) {
+ debug.println("Cipher.init() not first method "
+ + "called, disabling delayed provider selection");
+ if (w == 0) {
+ debug.println("Further warnings of this type will "
+ + "be suppressed");
+ }
+ new Exception("Call trace").printStackTrace();
+ }
+ }
+ Exception lastException = null;
+ while ((firstService != null) || serviceIterator.hasNext()) {
+ Service s;
+ CipherSpi thisSpi;
+ if (firstService != null) {
+ s = firstService;
+ thisSpi = firstSpi;
+ firstService = null;
+ firstSpi = null;
+ } else {
+ s = (Service)serviceIterator.next();
+ thisSpi = null;
+ }
+ if (JceSecurity.canUseProvider(s.getProvider()) == false) {
+ continue;
+ }
+ Transform tr = getTransform(s, transforms);
+ if (tr == null) {
+ // should never happen
+ continue;
+ }
+ if (tr.supportsModePadding(s) == S_NO) {
+ continue;
+ }
+ try {
+ if (thisSpi == null) {
+ Object obj = s.newInstance(null);
+ if (obj instanceof CipherSpi == false) {
+ continue;
+ }
+ thisSpi = (CipherSpi)obj;
+ }
+ tr.setModePadding(thisSpi);
+ initCryptoPermission();
+ spi = thisSpi;
+ provider = s.getProvider();
+ // not needed any more
+ firstService = null;
+ serviceIterator = null;
+ transforms = null;
+ return;
+ } catch (Exception e) {
+ lastException = e;
+ }
+ }
+ ProviderException e = new ProviderException
+ ("Could not construct CipherSpi instance");
+ if (lastException != null) {
+ e.initCause(lastException);
+ }
+ throw e;
+ }
+ }
+
+ private final static int I_KEY = 1;
+ private final static int I_PARAMSPEC = 2;
+ private final static int I_PARAMS = 3;
+ private final static int I_CERT = 4;
+
+ private void implInit(CipherSpi thisSpi, int type, int opmode, Key key,
+ AlgorithmParameterSpec paramSpec, AlgorithmParameters params,
+ SecureRandom random) throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
+ switch (type) {
+ case I_KEY:
+ checkCryptoPerm(thisSpi, key);
+ thisSpi.engineInit(opmode, key, random);
+ break;
+ case I_PARAMSPEC:
+ checkCryptoPerm(thisSpi, key, paramSpec);
+ thisSpi.engineInit(opmode, key, paramSpec, random);
+ break;
+ case I_PARAMS:
+ checkCryptoPerm(thisSpi, key, params);
+ thisSpi.engineInit(opmode, key, params, random);
+ break;
+ case I_CERT:
+ checkCryptoPerm(thisSpi, key);
+ thisSpi.engineInit(opmode, key, random);
+ break;
+ default:
+ throw new AssertionError("Internal Cipher error: " + type);
+ }
+ }
+
+ private void chooseProvider(int initType, int opmode, Key key,
+ AlgorithmParameterSpec paramSpec,
+ AlgorithmParameters params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ synchronized (lock) {
+ if (spi != null) {
+ implInit(spi, initType, opmode, key, paramSpec, params, random);
+ return;
+ }
+ Exception lastException = null;
+ while ((firstService != null) || serviceIterator.hasNext()) {
+ Service s;
+ CipherSpi thisSpi;
+ if (firstService != null) {
+ s = firstService;
+ thisSpi = firstSpi;
+ firstService = null;
+ firstSpi = null;
+ } else {
+ s = (Service)serviceIterator.next();
+ thisSpi = null;
+ }
+ // if provider says it does not support this key, ignore it
+ if (s.supportsParameter(key) == false) {
+ continue;
+ }
+ if (JceSecurity.canUseProvider(s.getProvider()) == false) {
+ continue;
+ }
+ Transform tr = getTransform(s, transforms);
+ if (tr == null) {
+ // should never happen
+ continue;
+ }
+ if (tr.supportsModePadding(s) == S_NO) {
+ continue;
+ }
+ try {
+ if (thisSpi == null) {
+ thisSpi = (CipherSpi)s.newInstance(null);
+ }
+ tr.setModePadding(thisSpi);
+ initCryptoPermission();
+ implInit(thisSpi, initType, opmode, key, paramSpec,
+ params, random);
+ provider = s.getProvider();
+ this.spi = thisSpi;
+ firstService = null;
+ serviceIterator = null;
+ transforms = null;
+ return;
+ } catch (Exception e) {
+ // NoSuchAlgorithmException from newInstance()
+ // InvalidKeyException from init()
+ // RuntimeException (ProviderException) from init()
+ // SecurityException from crypto permission check
+ if (lastException == null) {
+ lastException = e;
+ }
+ }
+ }
+ // no working provider found, fail
+ if (lastException instanceof InvalidKeyException) {
+ throw (InvalidKeyException)lastException;
+ }
+ if (lastException instanceof InvalidAlgorithmParameterException) {
+ throw (InvalidAlgorithmParameterException)lastException;
+ }
+ if (lastException instanceof RuntimeException) {
+ throw (RuntimeException)lastException;
+ }
+ String kName = (key != null) ? key.getClass().getName() : "(null)";
+ throw new InvalidKeyException
+ ("No installed provider supports this key: "
+ + kName, lastException);
+ }
+ }
+
+ /**
+ * Returns the provider of this <code>Cipher</code> object.
+ *
+ * @return the provider of this <code>Cipher</code> object
+ */
+ public final Provider getProvider() {
+ chooseFirstProvider();
+ return this.provider;
+ }
+
+ /**
+ * Returns the algorithm name of this <code>Cipher</code> object.
+ *
+ * <p>This is the same name that was specified in one of the
+ * <code>getInstance</code> calls that created this <code>Cipher</code>
+ * object..
+ *
+ * @return the algorithm name of this <code>Cipher</code> object.
+ */
+ public final String getAlgorithm() {
+ return this.transformation;
+ }
+
+ /**
+ * Returns the block size (in bytes).
+ *
+ * @return the block size (in bytes), or 0 if the underlying algorithm is
+ * not a block cipher
+ */
+ public final int getBlockSize() {
+ chooseFirstProvider();
+ return spi.engineGetBlockSize();
+ }
+
+ /**
+ * Returns the length in bytes that an output buffer would need to be in
+ * order to hold the result of the next <code>update</code> or
+ * <code>doFinal</code> operation, given the input length
+ * <code>inputLen</code> (in bytes).
+ *
+ * <p>This call takes into account any unprocessed (buffered) data from a
+ * previous <code>update</code> call, and padding.
+ *
+ * <p>The actual output length of the next <code>update</code> or
+ * <code>doFinal</code> call may be smaller than the length returned by
+ * this method.
+ *
+ * @param inputLen the input length (in bytes)
+ *
+ * @return the required output buffer size (in bytes)
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not yet been initialized)
+ */
+ public final int getOutputSize(int inputLen) {
+
+ if (!initialized && !(this instanceof NullCipher)) {
+ throw new IllegalStateException("Cipher not initialized");
+ }
+ if (inputLen < 0) {
+ throw new IllegalArgumentException("Input size must be equal " +
+ "to or greater than zero");
+ }
+ chooseFirstProvider();
+ return spi.engineGetOutputSize(inputLen);
+ }
+
+ /**
+ * Returns the initialization vector (IV) in a new buffer.
+ *
+ * <p>This is useful in the case where a random IV was created,
+ * or in the context of password-based encryption or
+ * decryption, where the IV is derived from a user-supplied password.
+ *
+ * @return the initialization vector in a new buffer, or null if the
+ * underlying algorithm does not use an IV, or if the IV has not yet
+ * been set.
+ */
+ public final byte[] getIV() {
+ chooseFirstProvider();
+ return spi.engineGetIV();
+ }
+
+ /**
+ * Returns the parameters used with this cipher.
+ *
+ * <p>The returned parameters may be the same that were used to initialize
+ * this cipher, or may contain a combination of default and random
+ * parameter values used by the underlying cipher implementation if this
+ * cipher requires algorithm parameters but was not initialized with any.
+ *
+ * @return the parameters used with this cipher, or null if this cipher
+ * does not use any parameters.
+ */
+ public final AlgorithmParameters getParameters() {
+ chooseFirstProvider();
+ return spi.engineGetParameters();
+ }
+
+ /**
+ * Returns the exemption mechanism object used with this cipher.
+ *
+ * @return the exemption mechanism object used with this cipher, or
+ * null if this cipher does not use any exemption mechanism.
+ */
+ public final ExemptionMechanism getExemptionMechanism() {
+ chooseFirstProvider();
+ return exmech;
+ }
+
+ //
+ // Crypto permission check code below
+ //
+ private void checkCryptoPerm(CipherSpi checkSpi, Key key)
+ throws InvalidKeyException {
+ if (cryptoPerm == CryptoAllPermission.INSTANCE) {
+ return;
+ }
+ // Check if key size and default parameters are within legal limits
+ AlgorithmParameterSpec params;
+ try {
+ params = getAlgorithmParameterSpec(checkSpi.engineGetParameters());
+ } catch (InvalidParameterSpecException ipse) {
+ throw new InvalidKeyException
+ ("Unsupported default algorithm parameters");
+ }
+ if (!passCryptoPermCheck(checkSpi, key, params)) {
+ throw new InvalidKeyException(
+ "Illegal key size or default parameters");
+ }
+ }
+
+ private void checkCryptoPerm(CipherSpi checkSpi, Key key,
+ AlgorithmParameterSpec params) throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
+ if (cryptoPerm == CryptoAllPermission.INSTANCE) {
+ return;
+ }
+ // Determine keysize and check if it is within legal limits
+ if (!passCryptoPermCheck(checkSpi, key, null)) {
+ throw new InvalidKeyException("Illegal key size");
+ }
+ if ((params != null) && (!passCryptoPermCheck(checkSpi, key, params))) {
+ throw new InvalidAlgorithmParameterException("Illegal parameters");
+ }
+ }
+
+ private void checkCryptoPerm(CipherSpi checkSpi, Key key,
+ AlgorithmParameters params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (cryptoPerm == CryptoAllPermission.INSTANCE) {
+ return;
+ }
+ // Convert the specified parameters into specs and then delegate.
+ AlgorithmParameterSpec pSpec;
+ try {
+ pSpec = getAlgorithmParameterSpec(params);
+ } catch (InvalidParameterSpecException ipse) {
+ throw new InvalidAlgorithmParameterException
+ ("Failed to retrieve algorithm parameter specification");
+ }
+ checkCryptoPerm(checkSpi, key, pSpec);
+ }
+
+ private boolean passCryptoPermCheck(CipherSpi checkSpi, Key key,
+ AlgorithmParameterSpec params)
+ throws InvalidKeyException {
+ String em = cryptoPerm.getExemptionMechanism();
+ int keySize = checkSpi.engineGetKeySize(key);
+ // Use the "algorithm" component of the cipher
+ // transformation so that the perm check would
+ // work when the key has the "aliased" algo.
+ String algComponent;
+ int index = transformation.indexOf('/');
+ if (index != -1) {
+ algComponent = transformation.substring(0, index);
+ } else {
+ algComponent = transformation;
+ }
+ CryptoPermission checkPerm =
+ new CryptoPermission(algComponent, keySize, params, em);
+
+ if (!cryptoPerm.implies(checkPerm)) {
+ if (debug != null) {
+ debug.println("Crypto Permission check failed");
+ debug.println("granted: " + cryptoPerm);
+ debug.println("requesting: " + checkPerm);
+ }
+ return false;
+ }
+ if (exmech == null) {
+ return true;
+ }
+ try {
+ if (!exmech.isCryptoAllowed(key)) {
+ if (debug != null) {
+ debug.println(exmech.getName() + " isn't enforced");
+ }
+ return false;
+ }
+ } catch (ExemptionMechanismException eme) {
+ if (debug != null) {
+ debug.println("Cannot determine whether "+
+ exmech.getName() + " has been enforced");
+ eme.printStackTrace();
+ }
+ return false;
+ }
+ return true;
+ }
+
+ // check if opmode is one of the defined constants
+ // throw InvalidParameterExeption if not
+ private static void checkOpmode(int opmode) {
+ if ((opmode < ENCRYPT_MODE) || (opmode > UNWRAP_MODE)) {
+ throw new InvalidParameterException("Invalid operation mode");
+ }
+ }
+
+ /**
+ * Initializes this cipher with a key.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If this cipher requires any algorithm parameters that cannot be
+ * derived from the given <code>key</code>, the underlying cipher
+ * implementation is supposed to generate the required parameters itself
+ * (using provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidKeyException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them using the {@link SecureRandom <code>SecureRandom</code>}
+ * implementation of the highest-priority
+ * installed provider as the source of randomness.
+ * (If none of the installed providers supply an implementation of
+ * SecureRandom, a system-provided source of randomness will be used.)
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of
+ * the following:
+ * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
+ * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
+ * @param key the key
+ *
+ * @exception InvalidKeyException if the given key is inappropriate for
+ * initializing this cipher, or if this cipher is being initialized for
+ * decryption and requires algorithm parameters that cannot be
+ * determined from the given key, or if the given key has a keysize that
+ * exceeds the maximum allowable keysize (as determined from the
+ * configured jurisdiction policy files).
+ */
+ public final void init(int opmode, Key key) throws InvalidKeyException {
+ init(opmode, key, JceSecurity.RANDOM);
+ }
+
+ /**
+ * Initializes this cipher with a key and a source of randomness.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If this cipher requires any algorithm parameters that cannot be
+ * derived from the given <code>key</code>, the underlying cipher
+ * implementation is supposed to generate the required parameters itself
+ * (using provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidKeyException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them from <code>random</code>.
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following:
+ * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
+ * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
+ * @param key the encryption key
+ * @param random the source of randomness
+ *
+ * @exception InvalidKeyException if the given key is inappropriate for
+ * initializing this cipher, or if this cipher is being initialized for
+ * decryption and requires algorithm parameters that cannot be
+ * determined from the given key, or if the given key has a keysize that
+ * exceeds the maximum allowable keysize (as determined from the
+ * configured jurisdiction policy files).
+ */
+ public final void init(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException
+ {
+ initialized = false;
+ checkOpmode(opmode);
+
+ if (spi != null) {
+ checkCryptoPerm(spi, key);
+ spi.engineInit(opmode, key, random);
+ } else {
+ try {
+ chooseProvider(I_KEY, opmode, key, null, null, random);
+ } catch (InvalidAlgorithmParameterException e) {
+ // should never occur
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ initialized = true;
+ this.opmode = opmode;
+ }
+
+ /**
+ * Initializes this cipher with a key and a set of algorithm
+ * parameters.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If this cipher requires any algorithm parameters and
+ * <code>params</code> is null, the underlying cipher implementation is
+ * supposed to generate the required parameters itself (using
+ * provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidAlgorithmParameterException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them using the {@link SecureRandom <code>SecureRandom</code>}
+ * implementation of the highest-priority
+ * installed provider as the source of randomness.
+ * (If none of the installed providers supply an implementation of
+ * SecureRandom, a system-provided source of randomness will be used.)
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following:
+ * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
+ * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
+ * @param key the encryption key
+ * @param params the algorithm parameters
+ *
+ * @exception InvalidKeyException if the given key is inappropriate for
+ * initializing this cipher, or its keysize exceeds the maximum allowable
+ * keysize (as determined from the configured jurisdiction policy files).
+ * @exception InvalidAlgorithmParameterException if the given algorithm
+ * parameters are inappropriate for this cipher,
+ * or this cipher is being initialized for decryption and requires
+ * algorithm parameters and <code>params</code> is null, or the given
+ * algorithm parameters imply a cryptographic strength that would exceed
+ * the legal limits (as determined from the configured jurisdiction
+ * policy files).
+ */
+ public final void init(int opmode, Key key, AlgorithmParameterSpec params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ init(opmode, key, params, JceSecurity.RANDOM);
+ }
+
+ /**
+ * Initializes this cipher with a key, a set of algorithm
+ * parameters, and a source of randomness.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If this cipher requires any algorithm parameters and
+ * <code>params</code> is null, the underlying cipher implementation is
+ * supposed to generate the required parameters itself (using
+ * provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidAlgorithmParameterException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them from <code>random</code>.
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following:
+ * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
+ * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
+ * @param key the encryption key
+ * @param params the algorithm parameters
+ * @param random the source of randomness
+ *
+ * @exception InvalidKeyException if the given key is inappropriate for
+ * initializing this cipher, or its keysize exceeds the maximum allowable
+ * keysize (as determined from the configured jurisdiction policy files).
+ * @exception InvalidAlgorithmParameterException if the given algorithm
+ * parameters are inappropriate for this cipher,
+ * or this cipher is being initialized for decryption and requires
+ * algorithm parameters and <code>params</code> is null, or the given
+ * algorithm parameters imply a cryptographic strength that would exceed
+ * the legal limits (as determined from the configured jurisdiction
+ * policy files).
+ */
+ public final void init(int opmode, Key key, AlgorithmParameterSpec params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ initialized = false;
+ checkOpmode(opmode);
+
+ if (spi != null) {
+ checkCryptoPerm(spi, key, params);
+ spi.engineInit(opmode, key, params, random);
+ } else {
+ chooseProvider(I_PARAMSPEC, opmode, key, params, null, random);
+ }
+
+ initialized = true;
+ this.opmode = opmode;
+ }
+
+ /**
+ * Initializes this cipher with a key and a set of algorithm
+ * parameters.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If this cipher requires any algorithm parameters and
+ * <code>params</code> is null, the underlying cipher implementation is
+ * supposed to generate the required parameters itself (using
+ * provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidAlgorithmParameterException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them using the {@link SecureRandom <code>SecureRandom</code>}
+ * implementation of the highest-priority
+ * installed provider as the source of randomness.
+ * (If none of the installed providers supply an implementation of
+ * SecureRandom, a system-provided source of randomness will be used.)
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following: <code>ENCRYPT_MODE</code>,
+ * <code>DECRYPT_MODE</code>, <code>WRAP_MODE</code>
+ * or <code>UNWRAP_MODE</code>)
+ * @param key the encryption key
+ * @param params the algorithm parameters
+ *
+ * @exception InvalidKeyException if the given key is inappropriate for
+ * initializing this cipher, or its keysize exceeds the maximum allowable
+ * keysize (as determined from the configured jurisdiction policy files).
+ * @exception InvalidAlgorithmParameterException if the given algorithm
+ * parameters are inappropriate for this cipher,
+ * or this cipher is being initialized for decryption and requires
+ * algorithm parameters and <code>params</code> is null, or the given
+ * algorithm parameters imply a cryptographic strength that would exceed
+ * the legal limits (as determined from the configured jurisdiction
+ * policy files).
+ */
+ public final void init(int opmode, Key key, AlgorithmParameters params)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ init(opmode, key, params, JceSecurity.RANDOM);
+ }
+
+ /**
+ * Initializes this cipher with a key, a set of algorithm
+ * parameters, and a source of randomness.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If this cipher requires any algorithm parameters and
+ * <code>params</code> is null, the underlying cipher implementation is
+ * supposed to generate the required parameters itself (using
+ * provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidAlgorithmParameterException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them from <code>random</code>.
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following: <code>ENCRYPT_MODE</code>,
+ * <code>DECRYPT_MODE</code>, <code>WRAP_MODE</code>
+ * or <code>UNWRAP_MODE</code>)
+ * @param key the encryption key
+ * @param params the algorithm parameters
+ * @param random the source of randomness
+ *
+ * @exception InvalidKeyException if the given key is inappropriate for
+ * initializing this cipher, or its keysize exceeds the maximum allowable
+ * keysize (as determined from the configured jurisdiction policy files).
+ * @exception InvalidAlgorithmParameterException if the given algorithm
+ * parameters are inappropriate for this cipher,
+ * or this cipher is being initialized for decryption and requires
+ * algorithm parameters and <code>params</code> is null, or the given
+ * algorithm parameters imply a cryptographic strength that would exceed
+ * the legal limits (as determined from the configured jurisdiction
+ * policy files).
+ */
+ public final void init(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException
+ {
+ initialized = false;
+ checkOpmode(opmode);
+
+ if (spi != null) {
+ checkCryptoPerm(spi, key, params);
+ spi.engineInit(opmode, key, params, random);
+ } else {
+ chooseProvider(I_PARAMS, opmode, key, null, params, random);
+ }
+
+ initialized = true;
+ this.opmode = opmode;
+ }
+
+ /**
+ * Initializes this cipher with the public key from the given certificate.
+ * <p> The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping or key unwrapping, depending
+ * on the value of <code>opmode</code>.
+ *
+ * <p>If the certificate is of type X.509 and has a <i>key usage</i>
+ * extension field marked as critical, and the value of the <i>key usage</i>
+ * extension field implies that the public key in
+ * the certificate and its corresponding private key are not
+ * supposed to be used for the operation represented by the value
+ * of <code>opmode</code>,
+ * an <code>InvalidKeyException</code>
+ * is thrown.
+ *
+ * <p> If this cipher requires any algorithm parameters that cannot be
+ * derived from the public key in the given certificate, the underlying
+ * cipher
+ * implementation is supposed to generate the required parameters itself
+ * (using provider-specific default or ramdom values) if it is being
+ * initialized for encryption or key wrapping, and raise an <code>
+ * InvalidKeyException</code> if it is being initialized for decryption or
+ * key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them using the
+ * <code>SecureRandom</code>
+ * implementation of the highest-priority
+ * installed provider as the source of randomness.
+ * (If none of the installed providers supply an implementation of
+ * SecureRandom, a system-provided source of randomness will be used.)
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following:
+ * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
+ * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
+ * @param certificate the certificate
+ *
+ * @exception InvalidKeyException if the public key in the given
+ * certificate is inappropriate for initializing this cipher, or this
+ * cipher is being initialized for decryption or unwrapping keys and
+ * requires algorithm parameters that cannot be determined from the
+ * public key in the given certificate, or the keysize of the public key
+ * in the given certificate has a keysize that exceeds the maximum
+ * allowable keysize (as determined by the configured jurisdiction policy
+ * files).
+ */
+ public final void init(int opmode, Certificate certificate)
+ throws InvalidKeyException
+ {
+ init(opmode, certificate, JceSecurity.RANDOM);
+ }
+
+ /**
+ * Initializes this cipher with the public key from the given certificate
+ * and
+ * a source of randomness.
+ *
+ * <p>The cipher is initialized for one of the following four operations:
+ * encryption, decryption, key wrapping
+ * or key unwrapping, depending on
+ * the value of <code>opmode</code>.
+ *
+ * <p>If the certificate is of type X.509 and has a <i>key usage</i>
+ * extension field marked as critical, and the value of the <i>key usage</i>
+ * extension field implies that the public key in
+ * the certificate and its corresponding private key are not
+ * supposed to be used for the operation represented by the value of
+ * <code>opmode</code>,
+ * an <code>InvalidKeyException</code>
+ * is thrown.
+ *
+ * <p>If this cipher requires any algorithm parameters that cannot be
+ * derived from the public key in the given <code>certificate</code>,
+ * the underlying cipher
+ * implementation is supposed to generate the required parameters itself
+ * (using provider-specific default or random values) if it is being
+ * initialized for encryption or key wrapping, and raise an
+ * <code>InvalidKeyException</code> if it is being
+ * initialized for decryption or key unwrapping.
+ * The generated parameters can be retrieved using
+ * {@link #getParameters() getParameters} or
+ * {@link #getIV() getIV} (if the parameter is an IV).
+ *
+ * <p>If this cipher (including its underlying feedback or padding scheme)
+ * requires any random bytes (e.g., for parameter generation), it will get
+ * them from <code>random</code>.
+ *
+ * <p>Note that when a Cipher object is initialized, it loses all
+ * previously-acquired state. In other words, initializing a Cipher is
+ * equivalent to creating a new instance of that Cipher and initializing
+ * it.
+ *
+ * @param opmode the operation mode of this cipher (this is one of the
+ * following:
+ * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>,
+ * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
+ * @param certificate the certificate
+ * @param random the source of randomness
+ *
+ * @exception InvalidKeyException if the public key in the given
+ * certificate is inappropriate for initializing this cipher, or this
+ * cipher is being initialized for decryption or unwrapping keys and
+ * requires algorithm parameters that cannot be determined from the
+ * public key in the given certificate, or the keysize of the public key
+ * in the given certificate has a keysize that exceeds the maximum
+ * allowable keysize (as determined by the configured jurisdiction policy
+ * files).
+ */
+ public final void init(int opmode, Certificate certificate,
+ SecureRandom random)
+ throws InvalidKeyException
+ {
+ initialized = false;
+ checkOpmode(opmode);
+
+ // Check key usage if the certificate is of
+ // type X.509.
+ if (certificate instanceof java.security.cert.X509Certificate) {
+ // Check whether the cert has a key usage extension
+ // marked as a critical extension.
+ X509Certificate cert = (X509Certificate)certificate;
+ Set critSet = cert.getCriticalExtensionOIDs();
+
+ if (critSet != null && !critSet.isEmpty()
+ && critSet.contains(KEY_USAGE_EXTENSION_OID)) {
+ boolean[] keyUsageInfo = cert.getKeyUsage();
+ // keyUsageInfo[2] is for keyEncipherment;
+ // keyUsageInfo[3] is for dataEncipherment.
+ if ((keyUsageInfo != null) &&
+ (((opmode == Cipher.ENCRYPT_MODE) &&
+ (keyUsageInfo.length > 3) &&
+ (keyUsageInfo[3] == false)) ||
+ ((opmode == Cipher.WRAP_MODE) &&
+ (keyUsageInfo.length > 2) &&
+ (keyUsageInfo[2] == false)))) {
+ throw new InvalidKeyException("Wrong key usage");
+ }
+ }
+ }
+
+ PublicKey publicKey =
+ (certificate==null? null:certificate.getPublicKey());
+
+ if (spi != null) {
+ checkCryptoPerm(spi, publicKey);
+ spi.engineInit(opmode, publicKey, random);
+ } else {
+ try {
+ chooseProvider(I_CERT, opmode, publicKey, null, null, random);
+ } catch (InvalidAlgorithmParameterException e) {
+ // should never occur
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ initialized = true;
+ this.opmode = opmode;
+ }
+
+ /**
+ * Ensures that Cipher is in a valid state for update() and doFinal()
+ * calls - should be initialized and in ENCRYPT_MODE or DECRYPT_MODE.
+ * @throws IllegalStateException if Cipher object is not in valid state.
+ */
+ private void checkCipherState() {
+ if (!(this instanceof NullCipher)) {
+ if (!initialized) {
+ throw new IllegalStateException("Cipher not initialized");
+ }
+ if ((opmode != Cipher.ENCRYPT_MODE) &&
+ (opmode != Cipher.DECRYPT_MODE)) {
+ throw new IllegalStateException("Cipher not initialized " +
+ "for encryption/decryption");
+ }
+ }
+ }
+
+ /**
+ * Continues a multiple-part encryption or decryption operation
+ * (depending on how this cipher was initialized), processing another data
+ * part.
+ *
+ * <p>The bytes in the <code>input</code> buffer are processed, and the
+ * result is stored in a new buffer.
+ *
+ * <p>If <code>input</code> has a length of zero, this method returns
+ * <code>null</code>.
+ *
+ * @param input the input buffer
+ *
+ * @return the new buffer with the result, or null if the underlying
+ * cipher is a block cipher and the input data is too short to result in a
+ * new block.
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ */
+ public final byte[] update(byte[] input) {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null) {
+ throw new IllegalArgumentException("Null input buffer");
+ }
+
+ chooseFirstProvider();
+ if (input.length == 0) {
+ return null;
+ }
+ return spi.engineUpdate(input, 0, input.length);
+ }
+
+ /**
+ * Continues a multiple-part encryption or decryption operation
+ * (depending on how this cipher was initialized), processing another data
+ * part.
+ *
+ * <p>The first <code>inputLen</code> bytes in the <code>input</code>
+ * buffer, starting at <code>inputOffset</code> inclusive, are processed,
+ * and the result is stored in a new buffer.
+ *
+ * <p>If <code>inputLen</code> is zero, this method returns
+ * <code>null</code>.
+ *
+ * @param input the input buffer
+ * @param inputOffset the offset in <code>input</code> where the input
+ * starts
+ * @param inputLen the input length
+ *
+ * @return the new buffer with the result, or null if the underlying
+ * cipher is a block cipher and the input data is too short to result in a
+ * new block.
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ */
+ public final byte[] update(byte[] input, int inputOffset, int inputLen) {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null || inputOffset < 0
+ || inputLen > (input.length - inputOffset) || inputLen < 0) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ if (inputLen == 0) {
+ return null;
+ }
+ return spi.engineUpdate(input, inputOffset, inputLen);
+ }
+
+ /**
+ * Continues a multiple-part encryption or decryption operation
+ * (depending on how this cipher was initialized), processing another data
+ * part.
+ *
+ * <p>The first <code>inputLen</code> bytes in the <code>input</code>
+ * buffer, starting at <code>inputOffset</code> inclusive, are processed,
+ * and the result is stored in the <code>output</code> buffer.
+ *
+ * <p>If the <code>output</code> buffer is too small to hold the result,
+ * a <code>ShortBufferException</code> is thrown. In this case, repeat this
+ * call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>If <code>inputLen</code> is zero, this method returns
+ * a length of zero.
+ *
+ * <p>Note: this method should be copy-safe, which means the
+ * <code>input</code> and <code>output</code> buffers can reference
+ * the same byte array and no unprocessed input data is overwritten
+ * when the result is copied into the output buffer.
+ *
+ * @param input the input buffer
+ * @param inputOffset the offset in <code>input</code> where the input
+ * starts
+ * @param inputLen the input length
+ * @param output the buffer for the result
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception ShortBufferException if the given output buffer is too small
+ * to hold the result
+ */
+ public final int update(byte[] input, int inputOffset, int inputLen,
+ byte[] output)
+ throws ShortBufferException {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null || inputOffset < 0
+ || inputLen > (input.length - inputOffset) || inputLen < 0) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ if (inputLen == 0) {
+ return 0;
+ }
+ return spi.engineUpdate(input, inputOffset, inputLen,
+ output, 0);
+ }
+
+ /**
+ * Continues a multiple-part encryption or decryption operation
+ * (depending on how this cipher was initialized), processing another data
+ * part.
+ *
+ * <p>The first <code>inputLen</code> bytes in the <code>input</code>
+ * buffer, starting at <code>inputOffset</code> inclusive, are processed,
+ * and the result is stored in the <code>output</code> buffer, starting at
+ * <code>outputOffset</code> inclusive.
+ *
+ * <p>If the <code>output</code> buffer is too small to hold the result,
+ * a <code>ShortBufferException</code> is thrown. In this case, repeat this
+ * call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>If <code>inputLen</code> is zero, this method returns
+ * a length of zero.
+ *
+ * <p>Note: this method should be copy-safe, which means the
+ * <code>input</code> and <code>output</code> buffers can reference
+ * the same byte array and no unprocessed input data is overwritten
+ * when the result is copied into the output buffer.
+ *
+ * @param input the input buffer
+ * @param inputOffset the offset in <code>input</code> where the input
+ * starts
+ * @param inputLen the input length
+ * @param output the buffer for the result
+ * @param outputOffset the offset in <code>output</code> where the result
+ * is stored
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception ShortBufferException if the given output buffer is too small
+ * to hold the result
+ */
+ public final int update(byte[] input, int inputOffset, int inputLen,
+ byte[] output, int outputOffset)
+ throws ShortBufferException {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null || inputOffset < 0
+ || inputLen > (input.length - inputOffset) || inputLen < 0
+ || outputOffset < 0) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ if (inputLen == 0) {
+ return 0;
+ }
+ return spi.engineUpdate(input, inputOffset, inputLen,
+ output, outputOffset);
+ }
+
+ /**
+ * Continues a multiple-part encryption or decryption operation
+ * (depending on how this cipher was initialized), processing another data
+ * part.
+ *
+ * <p>All <code>input.remaining()</code> bytes starting at
+ * <code>input.position()</code> are processed. The result is stored
+ * in the output buffer.
+ * Upon return, the input buffer's position will be equal
+ * to its limit; its limit will not have changed. The output buffer's
+ * position will have advanced by n, where n is the value returned
+ * by this method; the output buffer's limit will not have changed.
+ *
+ * <p>If <code>output.remaining()</code> bytes are insufficient to
+ * hold the result, a <code>ShortBufferException</code> is thrown.
+ * In this case, repeat this call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>Note: this method should be copy-safe, which means the
+ * <code>input</code> and <code>output</code> buffers can reference
+ * the same block of memory and no unprocessed input data is overwritten
+ * when the result is copied into the output buffer.
+ *
+ * @param input the input ByteBuffer
+ * @param output the output ByteByffer
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalArgumentException if input and output are the
+ * same object
+ * @exception ReadOnlyBufferException if the output buffer is read-only
+ * @exception ShortBufferException if there is insufficient space in the
+ * output buffer
+ * @since 1.5
+ */
+ public final int update(ByteBuffer input, ByteBuffer output)
+ throws ShortBufferException {
+ checkCipherState();
+
+ if ((input == null) || (output == null)) {
+ throw new IllegalArgumentException("Buffers must not be null");
+ }
+ if (input == output) {
+ throw new IllegalArgumentException("Input and output buffers must "
+ + "not be the same object, consider using buffer.duplicate()");
+ }
+ if (output.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ chooseFirstProvider();
+ return spi.engineUpdate(input, output);
+ }
+
+ /**
+ * Finishes a multiple-part encryption or decryption operation, depending
+ * on how this cipher was initialized.
+ *
+ * <p>Input data that may have been buffered during a previous
+ * <code>update</code> operation is processed, with padding (if requested)
+ * being applied.
+ * The result is stored in a new buffer.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * @return the new buffer with the result
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ */
+ public final byte[] doFinal()
+ throws IllegalBlockSizeException, BadPaddingException {
+ checkCipherState();
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(null, 0, 0);
+ }
+
+ /**
+ * Finishes a multiple-part encryption or decryption operation, depending
+ * on how this cipher was initialized.
+ *
+ * <p>Input data that may have been buffered during a previous
+ * <code>update</code> operation is processed, with padding (if requested)
+ * being applied.
+ * The result is stored in the <code>output</code> buffer, starting at
+ * <code>outputOffset</code> inclusive.
+ *
+ * <p>If the <code>output</code> buffer is too small to hold the result,
+ * a <code>ShortBufferException</code> is thrown. In this case, repeat this
+ * call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * @param output the buffer for the result
+ * @param outputOffset the offset in <code>output</code> where the result
+ * is stored
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception ShortBufferException if the given output buffer is too small
+ * to hold the result
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ */
+ public final int doFinal(byte[] output, int outputOffset)
+ throws IllegalBlockSizeException, ShortBufferException,
+ BadPaddingException {
+ checkCipherState();
+
+ // Input sanity check
+ if ((output == null) || (outputOffset < 0)) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(null, 0, 0, output, outputOffset);
+ }
+
+ /**
+ * Encrypts or decrypts data in a single-part operation, or finishes a
+ * multiple-part operation. The data is encrypted or decrypted,
+ * depending on how this cipher was initialized.
+ *
+ * <p>The bytes in the <code>input</code> buffer, and any input bytes that
+ * may have been buffered during a previous <code>update</code> operation,
+ * are processed, with padding (if requested) being applied.
+ * The result is stored in a new buffer.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * @param input the input buffer
+ *
+ * @return the new buffer with the result
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ */
+ public final byte[] doFinal(byte[] input)
+ throws IllegalBlockSizeException, BadPaddingException {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null) {
+ throw new IllegalArgumentException("Null input buffer");
+ }
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(input, 0, input.length);
+ }
+
+ /**
+ * Encrypts or decrypts data in a single-part operation, or finishes a
+ * multiple-part operation. The data is encrypted or decrypted,
+ * depending on how this cipher was initialized.
+ *
+ * <p>The first <code>inputLen</code> bytes in the <code>input</code>
+ * buffer, starting at <code>inputOffset</code> inclusive, and any input
+ * bytes that may have been buffered during a previous <code>update</code>
+ * operation, are processed, with padding (if requested) being applied.
+ * The result is stored in a new buffer.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * @param input the input buffer
+ * @param inputOffset the offset in <code>input</code> where the input
+ * starts
+ * @param inputLen the input length
+ *
+ * @return the new buffer with the result
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ */
+ public final byte[] doFinal(byte[] input, int inputOffset, int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null || inputOffset < 0
+ || inputLen > (input.length - inputOffset) || inputLen < 0) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(input, inputOffset, inputLen);
+ }
+
+ /**
+ * Encrypts or decrypts data in a single-part operation, or finishes a
+ * multiple-part operation. The data is encrypted or decrypted,
+ * depending on how this cipher was initialized.
+ *
+ * <p>The first <code>inputLen</code> bytes in the <code>input</code>
+ * buffer, starting at <code>inputOffset</code> inclusive, and any input
+ * bytes that may have been buffered during a previous <code>update</code>
+ * operation, are processed, with padding (if requested) being applied.
+ * The result is stored in the <code>output</code> buffer.
+ *
+ * <p>If the <code>output</code> buffer is too small to hold the result,
+ * a <code>ShortBufferException</code> is thrown. In this case, repeat this
+ * call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * <p>Note: this method should be copy-safe, which means the
+ * <code>input</code> and <code>output</code> buffers can reference
+ * the same byte array and no unprocessed input data is overwritten
+ * when the result is copied into the output buffer.
+ *
+ * @param input the input buffer
+ * @param inputOffset the offset in <code>input</code> where the input
+ * starts
+ * @param inputLen the input length
+ * @param output the buffer for the result
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception ShortBufferException if the given output buffer is too small
+ * to hold the result
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ */
+ public final int doFinal(byte[] input, int inputOffset, int inputLen,
+ byte[] output)
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null || inputOffset < 0
+ || inputLen > (input.length - inputOffset) || inputLen < 0) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(input, inputOffset, inputLen,
+ output, 0);
+ }
+
+ /**
+ * Encrypts or decrypts data in a single-part operation, or finishes a
+ * multiple-part operation. The data is encrypted or decrypted,
+ * depending on how this cipher was initialized.
+ *
+ * <p>The first <code>inputLen</code> bytes in the <code>input</code>
+ * buffer, starting at <code>inputOffset</code> inclusive, and any input
+ * bytes that may have been buffered during a previous
+ * <code>update</code> operation, are processed, with padding
+ * (if requested) being applied.
+ * The result is stored in the <code>output</code> buffer, starting at
+ * <code>outputOffset</code> inclusive.
+ *
+ * <p>If the <code>output</code> buffer is too small to hold the result,
+ * a <code>ShortBufferException</code> is thrown. In this case, repeat this
+ * call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * <p>Note: this method should be copy-safe, which means the
+ * <code>input</code> and <code>output</code> buffers can reference
+ * the same byte array and no unprocessed input data is overwritten
+ * when the result is copied into the output buffer.
+ *
+ * @param input the input buffer
+ * @param inputOffset the offset in <code>input</code> where the input
+ * starts
+ * @param inputLen the input length
+ * @param output the buffer for the result
+ * @param outputOffset the offset in <code>output</code> where the result
+ * is stored
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception ShortBufferException if the given output buffer is too small
+ * to hold the result
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ */
+ public final int doFinal(byte[] input, int inputOffset, int inputLen,
+ byte[] output, int outputOffset)
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ checkCipherState();
+
+ // Input sanity check
+ if (input == null || inputOffset < 0
+ || inputLen > (input.length - inputOffset) || inputLen < 0
+ || outputOffset < 0) {
+ throw new IllegalArgumentException("Bad arguments");
+ }
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(input, inputOffset, inputLen,
+ output, outputOffset);
+ }
+
+ /**
+ * Encrypts or decrypts data in a single-part operation, or finishes a
+ * multiple-part operation. The data is encrypted or decrypted,
+ * depending on how this cipher was initialized.
+ *
+ * <p>All <code>input.remaining()</code> bytes starting at
+ * <code>input.position()</code> are processed. The result is stored
+ * in the output buffer.
+ * Upon return, the input buffer's position will be equal
+ * to its limit; its limit will not have changed. The output buffer's
+ * position will have advanced by n, where n is the value returned
+ * by this method; the output buffer's limit will not have changed.
+ *
+ * <p>If <code>output.remaining()</code> bytes are insufficient to
+ * hold the result, a <code>ShortBufferException</code> is thrown.
+ * In this case, repeat this call with a larger output buffer. Use
+ * {@link #getOutputSize(int) getOutputSize} to determine how big
+ * the output buffer should be.
+ *
+ * <p>Upon finishing, this method resets this cipher object to the state
+ * it was in when previously initialized via a call to <code>init</code>.
+ * That is, the object is reset and available to encrypt or decrypt
+ * (depending on the operation mode that was specified in the call to
+ * <code>init</code>) more data.
+ *
+ * <p>Note: if any exception is thrown, this cipher object may need to
+ * be reset before it can be used again.
+ *
+ * <p>Note: this method should be copy-safe, which means the
+ * <code>input</code> and <code>output</code> buffers can reference
+ * the same byte array and no unprocessed input data is overwritten
+ * when the result is copied into the output buffer.
+ *
+ * @param input the input ByteBuffer
+ * @param output the output ByteBuffer
+ *
+ * @return the number of bytes stored in <code>output</code>
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized)
+ * @exception IllegalArgumentException if input and output are the
+ * same object
+ * @exception ReadOnlyBufferException if the output buffer is read-only
+ * @exception IllegalBlockSizeException if this cipher is a block cipher,
+ * no padding has been requested (only in encryption mode), and the total
+ * input length of the data processed by this cipher is not a multiple of
+ * block size; or if this encryption algorithm is unable to
+ * process the input data provided.
+ * @exception ShortBufferException if there is insufficient space in the
+ * output buffer
+ * @exception BadPaddingException if this cipher is in decryption mode,
+ * and (un)padding has been requested, but the decrypted data is not
+ * bounded by the appropriate padding bytes
+ * @since 1.5
+ */
+ public final int doFinal(ByteBuffer input, ByteBuffer output)
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ checkCipherState();
+
+ if ((input == null) || (output == null)) {
+ throw new IllegalArgumentException("Buffers must not be null");
+ }
+ if (input == output) {
+ throw new IllegalArgumentException("Input and output buffers must "
+ + "not be the same object, consider using buffer.duplicate()");
+ }
+ if (output.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ chooseFirstProvider();
+ return spi.engineDoFinal(input, output);
+ }
+
+ /**
+ * Wrap a key.
+ *
+ * @param key the key to be wrapped.
+ *
+ * @return the wrapped key.
+ *
+ * @exception IllegalStateException if this cipher is in a wrong
+ * state (e.g., has not been initialized).
+ *
+ * @exception IllegalBlockSizeException if this cipher is a block
+ * cipher, no padding has been requested, and the length of the
+ * encoding of the key to be wrapped is not a
+ * multiple of the block size.
+ *
+ * @exception InvalidKeyException if it is impossible or unsafe to
+ * wrap the key with this cipher (e.g., a hardware protected key is
+ * being passed to a software-only cipher).
+ */
+ public final byte[] wrap(Key key)
+ throws IllegalBlockSizeException, InvalidKeyException {
+ if (!(this instanceof NullCipher)) {
+ if (!initialized) {
+ throw new IllegalStateException("Cipher not initialized");
+ }
+ if (opmode != Cipher.WRAP_MODE) {
+ throw new IllegalStateException("Cipher not initialized " +
+ "for wrapping keys");
+ }
+ }
+
+ chooseFirstProvider();
+ return spi.engineWrap(key);
+ }
+
+ /**
+ * Unwrap a previously wrapped key.
+ *
+ * @param wrappedKey the key to be unwrapped.
+ *
+ * @param wrappedKeyAlgorithm the algorithm associated with the wrapped
+ * key.
+ *
+ * @param wrappedKeyType the type of the wrapped key. This must be one of
+ * <code>SECRET_KEY</code>, <code>PRIVATE_KEY</code>, or
+ * <code>PUBLIC_KEY</code>.
+ *
+ * @return the unwrapped key.
+ *
+ * @exception IllegalStateException if this cipher is in a wrong state
+ * (e.g., has not been initialized).
+ *
+ * @exception NoSuchAlgorithmException if no installed providers
+ * can create keys of type <code>wrappedKeyType</code> for the
+ * <code>wrappedKeyAlgorithm</code>.
+ *
+ * @exception InvalidKeyException if <code>wrappedKey</code> does not
+ * represent a wrapped key of type <code>wrappedKeyType</code> for
+ * the <code>wrappedKeyAlgorithm</code>.
+ */
+ public final Key unwrap(byte[] wrappedKey,
+ String wrappedKeyAlgorithm,
+ int wrappedKeyType)
+ throws InvalidKeyException, NoSuchAlgorithmException {
+
+ if (!(this instanceof NullCipher)) {
+ if (!initialized) {
+ throw new IllegalStateException("Cipher not initialized");
+ }
+ if (opmode != Cipher.UNWRAP_MODE) {
+ throw new IllegalStateException("Cipher not initialized " +
+ "for unwrapping keys");
+ }
+ }
+ if ((wrappedKeyType != SECRET_KEY) &&
+ (wrappedKeyType != PRIVATE_KEY) &&
+ (wrappedKeyType != PUBLIC_KEY)) {
+ throw new InvalidParameterException("Invalid key type");
+ }
+
+ chooseFirstProvider();
+ return spi.engineUnwrap(wrappedKey,
+ wrappedKeyAlgorithm,
+ wrappedKeyType);
+ }
+
+ private AlgorithmParameterSpec getAlgorithmParameterSpec(
+ AlgorithmParameters params)
+ throws InvalidParameterSpecException {
+ if (params == null) {
+ return null;
+ }
+
+ String alg = params.getAlgorithm().toUpperCase(Locale.ENGLISH);
+
+ if (alg.equalsIgnoreCase("RC2")) {
+ return params.getParameterSpec(RC2ParameterSpec.class);
+ }
+
+ if (alg.equalsIgnoreCase("RC5")) {
+ return params.getParameterSpec(RC5ParameterSpec.class);
+ }
+
+ if (alg.startsWith("PBE")) {
+ return params.getParameterSpec(PBEParameterSpec.class);
+ }
+
+ if (alg.startsWith("DES")) {
+ return params.getParameterSpec(IvParameterSpec.class);
+ }
+ return null;
+ }
+
+ private static CryptoPermission getConfiguredPermission(
+ String transformation) throws NullPointerException,
+ NoSuchAlgorithmException {
+ if (transformation == null) throw new NullPointerException();
+ String[] parts = tokenizeTransformation(transformation);
+ return JceSecurityManager.INSTANCE.getCryptoPermission(parts[0]);
+ }
+
+ /**
+ * Returns the maximum key length for the specified transformation
+ * according to the installed JCE jurisdiction policy files. If
+ * JCE unlimited strength jurisdiction policy files are installed,
+ * Integer.MAX_VALUE will be returned.
+ * For more information on default key size in JCE jurisdiction
+ * policy files, please see Appendix E in the
+ * <a href=
+ * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppE">
+ * Java Cryptography Architecture Reference Guide</a>.
+ *
+ * @param transformation the cipher transformation.
+ * @return the maximum key length in bits or Integer.MAX_VALUE.
+ * @exception NullPointerException if <code>transformation</code> is null.
+ * @exception NoSuchAlgorithmException if <code>transformation</code>
+ * is not a valid transformation, i.e. in the form of "algorithm" or
+ * "algorithm/mode/padding".
+ * @since 1.5
+ */
+ public static final int getMaxAllowedKeyLength(String transformation)
+ throws NoSuchAlgorithmException {
+ CryptoPermission cp = getConfiguredPermission(transformation);
+ return cp.getMaxKeySize();
+ }
+
+ /**
+ * Returns an AlgorithmParameterSpec object which contains
+ * the maximum cipher parameter value according to the
+ * jurisdiction policy file. If JCE unlimited strength jurisdiction
+ * policy files are installed or there is no maximum limit on the
+ * parameters for the specified transformation in the policy file,
+ * null will be returned.
+ *
+ * @param transformation the cipher transformation.
+ * @return an AlgorithmParameterSpec which holds the maximum
+ * value or null.
+ * @exception NullPointerException if <code>transformation</code>
+ * is null.
+ * @exception NoSuchAlgorithmException if <code>transformation</code>
+ * is not a valid transformation, i.e. in the form of "algorithm" or
+ * "algorithm/mode/padding".
+ * @since 1.5
+ */
+ public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(
+ String transformation) throws NoSuchAlgorithmException {
+ CryptoPermission cp = getConfiguredPermission(transformation);
+ return cp.getAlgorithmParameterSpec();
+ }
+}