/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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. * <p> * Modes such as Authenticated Encryption with Associated Data (AEAD) * provide authenticity assurances for both confidential data and * Additional Associated Data (AAD) that is not encrypted. (Please see * <a href="http://www.ietf.org/rfc/rfc5116.txt"> RFC 5116 </a> for more * information on AEAD and AEAD algorithms such as GCM/CCM.) Both * confidential and AAD data can be used when calculating the * authentication tag (similar to a {@link Mac}). This tag is appended * to the ciphertext during encryption, and is verified on decryption. * <p> * AEAD modes such as GCM/CCM perform all AAD authenticity calculations * before starting the ciphertext authenticity calculations. To avoid * implementations having to internally buffer ciphertext, all AAD data * must be supplied to GCM/CCM implementations (via the {@code * updateAAD} methods) <b>before</b> the ciphertext is processed (via * the {@code update} and {@code doFinal} methods). * * <pre> * GCMParameterSpec s = new GCMParameterSpec(...); * cipher.init(..., s); * * // If the GCMParameterSpec is needed again * cipher.getParameters().getParameterSpec(GCMParameterSpec.class)); * * cipher.updateAAD(...); // AAD * cipher.update(...); // Multi-part update * cipher.doFinal(...); // conclusion of operation * </pre> * Every implementation of the Java platform is required to support * the following standard <code>Cipher</code> transformations with the keysizes * in parentheses: * <ul> * <li><tt>AES/CBC/NoPadding</tt> (128)</li> * <li><tt>AES/CBC/PKCS5Padding</tt> (128)</li> * <li><tt>AES/ECB/NoPadding</tt> (128)</li> * <li><tt>AES/ECB/PKCS5Padding</tt> (128)</li> * <li><tt>DES/CBC/NoPadding</tt> (56)</li> * <li><tt>DES/CBC/PKCS5Padding</tt> (56)</li> * <li><tt>DES/ECB/NoPadding</tt> (56)</li> * <li><tt>DES/ECB/PKCS5Padding</tt> (56)</li> * <li><tt>DESede/CBC/NoPadding</tt> (168)</li> * <li><tt>DESede/CBC/PKCS5Padding</tt> (168)</li> * <li><tt>DESede/ECB/NoPadding</tt> (168)</li> * <li><tt>DESede/ECB/PKCS5Padding</tt> (168)</li> * <li><tt>RSA/ECB/PKCS1Padding</tt> (1024, 2048)</li> * <li><tt>RSA/ECB/OAEPWithSHA-1AndMGF1Padding</tt> (1024, 2048)</li> * <li><tt>RSA/ECB/OAEPWithSHA-256AndMGF1Padding</tt> (1024, 2048)</li> * </ul> * These transformations are described in the * <a href="{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher"> * Cipher section</a> of the * Java Cryptography Architecture Standard Algorithm Name Documentation. * Consult the release documentation for your implementation to see if any * other transformations are supported. * * @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 the Cipher section in the <a href= * "{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher"> * Java Cryptography Architecture Standard Algorithm Name Documentation</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; }Lines 1–500 of 2677. Load more
/* * Copyright (c) 1997, 2011, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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. * <p> * Modes such as Authenticated Encryption with Associated Data (AEAD) * provide authenticity assurances for both confidential data and * Additional Associated Data (AAD) that is not encrypted. (Please see * <a href="http://www.ietf.org/rfc/rfc5116.txt"> RFC 5116 </a> for more * information on AEAD and AEAD algorithms such as GCM/CCM.) Both * confidential and AAD data can be used when calculating the * authentication tag (similar to a {@link Mac}). This tag is appended * to the ciphertext during encryption, and is verified on decryption. * <p> * AEAD modes such as GCM/CCM perform all AAD authenticity calculations * before starting the ciphertext authenticity calculations. To avoid * implementations having to internally buffer ciphertext, all AAD data * must be supplied to GCM/CCM implementations (via the {@code * updateAAD} methods) <b>before</b> the ciphertext is processed (via * the {@code update} and {@code doFinal} methods). * * <pre> * GCMParameterSpec s = new GCMParameterSpec(...); * cipher.init(..., s); * * // If the GCMParameterSpec is needed again * cipher.getParameters().getParameterSpec(GCMParameterSpec.class)); * * cipher.updateAAD(...); // AAD * cipher.update(...); // Multi-part update * cipher.doFinal(...); // conclusion of operation * </pre> * Every implementation of the Java platform is required to support * the following standard <code>Cipher</code> transformations with the keysizes * in parentheses: * <ul> * <li><tt>AES/CBC/NoPadding</tt> (128)</li> * <li><tt>AES/CBC/PKCS5Padding</tt> (128)</li> * <li><tt>AES/ECB/NoPadding</tt> (128)</li> * <li><tt>AES/ECB/PKCS5Padding</tt> (128)</li> * <li><tt>DES/CBC/NoPadding</tt> (56)</li> * <li><tt>DES/CBC/PKCS5Padding</tt> (56)</li> * <li><tt>DES/ECB/NoPadding</tt> (56)</li> * <li><tt>DES/ECB/PKCS5Padding</tt> (56)</li> * <li><tt>DESede/CBC/NoPadding</tt> (168)</li> * <li><tt>DESede/CBC/PKCS5Padding</tt> (168)</li> * <li><tt>DESede/ECB/NoPadding</tt> (168)</li> * <li><tt>DESede/ECB/PKCS5Padding</tt> (168)</li> * <li><tt>RSA/ECB/PKCS1Padding</tt> (1024, 2048)</li> * <li><tt>RSA/ECB/OAEPWithSHA-1AndMGF1Padding</tt> (1024, 2048)</li> * <li><tt>RSA/ECB/OAEPWithSHA-256AndMGF1Padding</tt> (1024, 2048)</li> * </ul> * These transformations are described in the * <a href="{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher"> * Cipher section</a> of the * Java Cryptography Architecture Standard Algorithm Name Documentation. * Consult the release documentation for your implementation to see if any * other transformations are supported. * * @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 the Cipher section in the <a href= * "{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher"> * Java Cryptography Architecture Standard Algorithm Name Documentation</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; }