2677 lines · 110.0 KB · Java
RawDownload
/* * 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.