943 lines · 34.8 KB · Java
RawDownload
/* * Copyright (c) 2002, 2007, 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 com.sun.crypto.provider; import java.util.Locale; import java.security.*;import java.security.spec.*;import javax.crypto.*;import javax.crypto.spec.*;import javax.crypto.BadPaddingException; /** * This class represents the symmetric algorithms in its various modes * (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, <code>CBC</code>, * <code>PCBC</code>, <code>CTR</code>, and <code>CTS</code>) and * padding schemes (<code>PKCS5Padding</code>, <code>NoPadding</code>, * <code>ISO10126Padding</code>). * * @author Gigi Ankeny * @author Jan Luehe * @see ElectronicCodeBook * @see CipherFeedback * @see OutputFeedback * @see CipherBlockChaining * @see PCBC * @see CounterMode * @see CipherTextStealing */ final class CipherCore {     /*     * internal buffer     */    private byte[] buffer = null;     /*     * internal buffer     */    private int blockSize = 0;     /*     * unit size (number of input bytes that can be processed at a time)     */    private int unitBytes = 0;     /*     * index of the content size left in the buffer     */    private int buffered = 0;     /*     * minimum number of bytes in the buffer required for     * FeedbackCipher.encryptFinal()/decryptFinal() call.     * update() must buffer this many bytes before before starting     * to encrypt/decrypt data.     * currently, only CTS mode has a non-zero value due to its special     * handling on the last two blocks (the last one may be incomplete).     */    private int minBytes = 0;     /*     * number of bytes needed to make the total input length a multiple     * of the blocksize (this is used in feedback mode, when the number of     * input bytes that are processed at a time is different from the block     * size)     */    private int diffBlocksize = 0;     /*     * padding class     */    private Padding padding = null;     /*     * internal cipher engine     */    private FeedbackCipher cipher = null;     /*     * the cipher mode     */    private int cipherMode = ECB_MODE;     /*     * are we encrypting or decrypting?     */    private boolean decrypting = false;     /*     * Block Mode constants     */    private static final int ECB_MODE = 0;    private static final int CBC_MODE = 1;    private static final int CFB_MODE = 2;    private static final int OFB_MODE = 3;    private static final int PCBC_MODE = 4;    private static final int CTR_MODE = 5;    private static final int CTS_MODE = 6;     /**     * Creates an instance of CipherCore with default ECB mode and     * PKCS5Padding.     */    CipherCore(SymmetricCipher impl, int blkSize) {        blockSize = blkSize;        unitBytes = blkSize;        diffBlocksize = blkSize;         /*         * The buffer should be usable for all cipher mode and padding         * schemes. Thus, it has to be at least (blockSize+1) for CTS.         * In decryption mode, it also hold the possible padding block.         */        buffer = new byte[blockSize*2];         // set mode and padding        cipher = new ElectronicCodeBook(impl);        padding = new PKCS5Padding(blockSize);    }     /**     * Sets the mode of this cipher.     *     * @param mode the cipher mode     *     * @exception NoSuchAlgorithmException if the requested cipher mode does     * not exist     */    void setMode(String mode) throws NoSuchAlgorithmException {        if (mode == null)            throw new NoSuchAlgorithmException("null mode");         String modeUpperCase = mode.toUpperCase(Locale.ENGLISH);         if (modeUpperCase.equals("ECB")) {            return;        }         SymmetricCipher rawImpl = cipher.getEmbeddedCipher();        if (modeUpperCase.equals("CBC")) {            cipherMode = CBC_MODE;            cipher = new CipherBlockChaining(rawImpl);        }        else if (modeUpperCase.equals("CTS")) {            cipherMode = CTS_MODE;            cipher = new CipherTextStealing(rawImpl);            minBytes = blockSize+1;            padding = null;        }        else if (modeUpperCase.equals("CTR")) {            cipherMode = CTR_MODE;            cipher = new CounterMode(rawImpl);            unitBytes = 1;            padding = null;        }        else if (modeUpperCase.startsWith("CFB")) {            cipherMode = CFB_MODE;            unitBytes = getNumOfUnit(mode, "CFB".length(), blockSize);            cipher = new CipherFeedback(rawImpl, unitBytes);        }        else if (modeUpperCase.startsWith("OFB")) {            cipherMode = OFB_MODE;            unitBytes = getNumOfUnit(mode, "OFB".length(), blockSize);            cipher = new OutputFeedback(rawImpl, unitBytes);        }        else if (modeUpperCase.equals("PCBC")) {            cipherMode = PCBC_MODE;            cipher = new PCBC(rawImpl);        }        else {            throw new NoSuchAlgorithmException("Cipher mode: " + mode                                               + " not found");        }    }     private static int getNumOfUnit(String mode, int offset, int blockSize)        throws NoSuchAlgorithmException {        int result = blockSize; // use blockSize as default value        if (mode.length() > offset) {            int numInt;            try {                Integer num = Integer.valueOf(mode.substring(offset));                numInt = num.intValue();                result = numInt >> 3;            } catch (NumberFormatException e) {                throw new NoSuchAlgorithmException                    ("Algorithm mode: " + mode + " not implemented");            }            if ((numInt % 8 != 0) || (result > blockSize)) {                throw new NoSuchAlgorithmException                    ("Invalid algorithm mode: " + mode);            }        }        return result;    }     /**     * Sets the padding mechanism of this cipher.     *     * @param padding the padding mechanism     *     * @exception NoSuchPaddingException if the requested padding mechanism     * does not exist     */    void setPadding(String paddingScheme)        throws NoSuchPaddingException    {        if (paddingScheme == null) {            throw new NoSuchPaddingException("null padding");        }        if (paddingScheme.equalsIgnoreCase("NoPadding")) {            padding = null;        } else if (paddingScheme.equalsIgnoreCase("ISO10126Padding")) {            padding = new ISO10126Padding(blockSize);        } else if (!paddingScheme.equalsIgnoreCase("PKCS5Padding")) {            throw new NoSuchPaddingException("Padding: " + paddingScheme                                             + " not implemented");        }        if ((padding != null) &&            ((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE))) {            padding = null;            throw new NoSuchPaddingException                ((cipherMode == CTR_MODE? "CTR":"CTS") +                 " mode must be used with NoPadding");        }    }     /**     * 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)     */    int getOutputSize(int inputLen) {        int totalLen = buffered + inputLen;         if (padding == null)            return totalLen;         if (decrypting)            return totalLen;         if (unitBytes != blockSize) {            if (totalLen < diffBlocksize)                return diffBlocksize;            else                return (totalLen + blockSize -                        ((totalLen - diffBlocksize) % blockSize));        } else {            return totalLen + padding.padLength(totalLen);        }    }     /**     * Returns the initialization vector (IV) in a new buffer.     *     * <p>This is useful in the case where a random IV has been created     * (see <a href = "#init">init</a>),     * or in the context of password-based encryption or     * decryption, where the IV is derived from a user-provided 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.     */    byte[] getIV() {        byte[] iv = cipher.getIV();        return (iv == null) ? null : (byte[])iv.clone();    }     /**     * 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 the default set of parameters or a set of     * randomly generated parameters used by the underlying cipher     * implementation (provided that the underlying cipher implementation     * uses a default set of parameters or creates new parameters if it needs     * parameters but was not initialized with any).     *     * @return the parameters used with this cipher, or null if this cipher     * does not use any parameters.     */    AlgorithmParameters getParameters(String algName) {        AlgorithmParameters params = null;        if (cipherMode == ECB_MODE) return null;        byte[] iv = getIV();        if (iv != null) {            AlgorithmParameterSpec ivSpec;            if (algName.equals("RC2")) {                RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();                ivSpec = new RC2ParameterSpec(rawImpl.getEffectiveKeyBits(),                                              iv);            } else {                ivSpec = new IvParameterSpec(iv);            }            try {                params = AlgorithmParameters.getInstance(algName, "SunJCE");            } catch (NoSuchAlgorithmException nsae) {                // should never happen                throw new RuntimeException("Cannot find " + algName +                    " AlgorithmParameters implementation in SunJCE provider");            } catch (NoSuchProviderException nspe) {                // should never happen                throw new RuntimeException("Cannot find SunJCE provider");            }            try {                params.init(ivSpec);            } catch (InvalidParameterSpecException ipse) {                // should never happen                throw new RuntimeException("IvParameterSpec not supported");            }        }        return params;    }     /**     * 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 an initialization vector (IV), it will get     * it from <code>random</code>.     * This behaviour should only be used in encryption or key wrapping     * mode, however.     * When initializing a cipher that requires an IV for decryption or     * key unwrapping, the IV     * (same IV that was used for encryption or key wrapping) must be provided     * explicitly as a     * parameter, in order to get the correct result.     *     * <p>This method also cleans existing buffer and other related state     * information.     *     * @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 secret key     * @param random the source of randomness     *     * @exception InvalidKeyException if the given key is inappropriate for     * initializing this cipher     */    void init(int opmode, Key key, SecureRandom random)            throws InvalidKeyException {        try {            init(opmode, key, (AlgorithmParameterSpec)null, random);        } catch (InvalidAlgorithmParameterException e) {            throw new InvalidKeyException(e.getMessage());        }    }     /**     * 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 (including its underlying feedback or padding scheme)     * requires any random bytes, it will get them from <code>random</code>.     *     * @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     * @exception InvalidAlgorithmParameterException if the given algorithm     * parameters are inappropriate for this cipher     */    void init(int opmode, Key key, AlgorithmParameterSpec params,            SecureRandom random)            throws InvalidKeyException, InvalidAlgorithmParameterException {        decrypting = (opmode == Cipher.DECRYPT_MODE)                  || (opmode == Cipher.UNWRAP_MODE);         byte[] keyBytes = getKeyBytes(key);         byte[] ivBytes;        if (params == null) {            ivBytes = null;        } else if (params instanceof IvParameterSpec) {            ivBytes = ((IvParameterSpec)params).getIV();            if ((ivBytes == null) || (ivBytes.length != blockSize)) {                throw new InvalidAlgorithmParameterException                    ("Wrong IV length: must be " + blockSize +                    " bytes long");            }        } else if (params instanceof RC2ParameterSpec) {            ivBytes = ((RC2ParameterSpec)params).getIV();            if ((ivBytes != null) && (ivBytes.length != blockSize)) {                throw new InvalidAlgorithmParameterException                    ("Wrong IV length: must be " + blockSize +                    " bytes long");            }        } else {            throw new InvalidAlgorithmParameterException("Wrong parameter "                                                         + "type: IV "                                                         + "expected");        }         if (cipherMode == ECB_MODE) {            if (ivBytes != null) {                throw new InvalidAlgorithmParameterException                                                ("ECB mode cannot use IV");            }        } else if (ivBytes == null) {            if (decrypting) {                throw new InvalidAlgorithmParameterException("Parameters "                                                             + "missing");            }            if (random == null) {                random = SunJCE.RANDOM;            }            ivBytes = new byte[blockSize];            random.nextBytes(ivBytes);        }         buffered = 0;        diffBlocksize = blockSize;         String algorithm = key.getAlgorithm();         cipher.init(decrypting, algorithm, keyBytes, ivBytes);    }     void init(int opmode, Key key, AlgorithmParameters params,              SecureRandom random)        throws InvalidKeyException, InvalidAlgorithmParameterException {        IvParameterSpec ivSpec = null;        if (params != null) {            try {                ivSpec = (IvParameterSpec)params.getParameterSpec                    (IvParameterSpec.class);            } catch (InvalidParameterSpecException ipse) {                throw new InvalidAlgorithmParameterException("Wrong parameter "                                                             + "type: IV "                                                             + "expected");            }        }        init(opmode, key, ivSpec, random);    }     /**     * Return the key bytes of the specified key. Throw an InvalidKeyException     * if the key is not usable.     */    static byte[] getKeyBytes(Key key) throws InvalidKeyException {        if (key == null) {            throw new InvalidKeyException("No key given");        }        // note: key.getFormat() may return null        if (!"RAW".equalsIgnoreCase(key.getFormat())) {            throw new InvalidKeyException("Wrong format: RAW bytes needed");        }

Lines 1–500 of 943.