0N/A/*
2362N/A * Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.crypto.provider;
0N/A
0N/Aimport java.util.Arrays;
0N/Aimport java.security.*;
0N/Aimport java.security.spec.*;
0N/Aimport javax.crypto.*;
0N/Aimport javax.crypto.spec.*;
0N/A
0N/A/**
0N/A * This class implements the CMS DESede KeyWrap algorithm as defined
0N/A * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
0N/A * "XML Encryption Syntax and Processing" section 5.6.2
0N/A * "CMS Triple DES Key Wrap".
0N/A * Note: only <code>CBC</code> mode and <code>NoPadding</code> padding
0N/A * scheme can be used for this algorithm.
0N/A *
0N/A * @author Valerie Peng
0N/A *
0N/A *
0N/A * @see DESedeCipher
0N/A */
0N/Apublic final class DESedeWrapCipher extends CipherSpi {
0N/A
0N/A private static final byte[] IV2 = {
0N/A (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c,
0N/A (byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x05
0N/A };
0N/A
0N/A /*
0N/A * internal cipher object which does the real work.
0N/A */
0N/A private FeedbackCipher cipher;
0N/A
0N/A /*
0N/A * iv for (re-)initializing the internal cipher object.
0N/A */
0N/A private byte[] iv = null;
0N/A
0N/A /*
0N/A * key for re-initializing the internal cipher object.
0N/A */
0N/A private Key cipherKey = null;
0N/A
0N/A /*
0N/A * are we encrypting or decrypting?
0N/A */
0N/A private boolean decrypting = false;
0N/A
0N/A /**
0N/A * Creates an instance of CMS DESede KeyWrap cipher with default
0N/A * mode, i.e. "CBC" and padding scheme, i.e. "NoPadding".
0N/A */
0N/A public DESedeWrapCipher() {
0N/A cipher = new CipherBlockChaining(new DESedeCrypt());
0N/A }
0N/A
0N/A /**
0N/A * Sets the mode of this cipher. Only "CBC" mode is accepted for this
0N/A * cipher.
0N/A *
0N/A * @param mode the cipher mode.
0N/A *
0N/A * @exception NoSuchAlgorithmException if the requested cipher mode
0N/A * is not "CBC".
0N/A */
0N/A protected void engineSetMode(String mode)
0N/A throws NoSuchAlgorithmException {
0N/A if (!mode.equalsIgnoreCase("CBC")) {
0N/A throw new NoSuchAlgorithmException(mode + " cannot be used");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
0N/A * is accepted for this cipher.
0N/A *
0N/A * @param padding the padding mechanism.
0N/A *
0N/A * @exception NoSuchPaddingException if the requested padding mechanism
0N/A * is not "NoPadding".
0N/A */
0N/A protected void engineSetPadding(String padding)
0N/A throws NoSuchPaddingException {
0N/A if (!padding.equalsIgnoreCase("NoPadding")) {
0N/A throw new NoSuchPaddingException(padding + " cannot be used");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the block size (in bytes), i.e. 8 bytes.
0N/A *
0N/A * @return the block size (in bytes), i.e. 8 bytes.
0N/A */
0N/A protected int engineGetBlockSize() {
0N/A return DESConstants.DES_BLOCK_SIZE;
0N/A }
0N/A
0N/A /**
0N/A * Returns the length in bytes that an output buffer would need to be
0N/A * given the input length <code>inputLen</code> (in bytes).
0N/A *
0N/A * <p>The actual output length of the next <code>update</code> or
0N/A * <code>doFinal</code> call may be smaller than the length returned
0N/A * by this method.
0N/A *
0N/A * @param inputLen the input length (in bytes).
0N/A *
0N/A * @return the required output buffer size (in bytes).
0N/A */
0N/A protected int engineGetOutputSize(int inputLen) {
0N/A // can only return an upper-limit if not initialized yet.
0N/A int result = 0;
0N/A if (decrypting) {
0N/A result = inputLen - 16;
0N/A } else {
0N/A result = inputLen + 16;
0N/A }
0N/A return (result < 0? 0:result);
0N/A }
0N/A
0N/A /**
0N/A * Returns the initialization vector (IV) in a new buffer.
0N/A *
0N/A * @return the initialization vector, or null if the underlying
0N/A * algorithm does not use an IV, or if the IV has not yet
0N/A * been set.
0N/A */
0N/A protected byte[] engineGetIV() {
0N/A return (iv == null? null:(byte[]) iv.clone());
0N/A }
0N/A
0N/A /**
0N/A * Initializes this cipher with a key and a source of randomness.
0N/A *
0N/A * <p>The cipher only supports the following two operation modes:<b>
0N/A * Cipher.WRAP_MODE, and <b>
0N/A * Cipher.UNWRAP_MODE.
0N/A * <p>For modes other than the above two, UnsupportedOperationException
0N/A * will be thrown.
0N/A * <p>If this cipher requires an initialization vector (IV), it will get
0N/A * it from <code>random</code>.
0N/A *
0N/A * @param opmode the operation mode of this cipher. Only
0N/A * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
0N/A * @param key the secret key.
0N/A * @param random the source of randomness.
0N/A *
0N/A * @exception InvalidKeyException if the given key is inappropriate
0N/A * or if parameters are required but not supplied.
0N/A */
0N/A protected void engineInit(int opmode, Key key, SecureRandom random)
0N/A throws InvalidKeyException {
0N/A try {
0N/A engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
0N/A } catch (InvalidAlgorithmParameterException iape) {
0N/A // should never happen
0N/A InvalidKeyException ike =
0N/A new InvalidKeyException("Parameters required");
0N/A ike.initCause(iape);
0N/A throw ike;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Initializes this cipher with a key, a set of algorithm parameters,
0N/A * and a source of randomness.
0N/A *
0N/A * <p>The cipher only supports the following two operation modes:<b>
0N/A * Cipher.WRAP_MODE, and <b>
0N/A * Cipher.UNWRAP_MODE.
0N/A * <p>For modes other than the above two, UnsupportedOperationException
0N/A * will be thrown.
0N/A * <p>If this cipher requires an initialization vector (IV), it will get
0N/A * it from <code>random</code>.
0N/A *
0N/A * @param opmode the operation mode of this cipher. Only
0N/A * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
0N/A * @param key the secret key.
0N/A * @param params the algorithm parameters.
0N/A * @param random the source of randomness.
0N/A *
0N/A * @exception InvalidKeyException if the given key is inappropriate.
0N/A * @exception InvalidAlgorithmParameterException if the given algorithm
0N/A * parameters are inappropriate for this cipher.
0N/A */
0N/A protected void engineInit(int opmode, Key key,
0N/A AlgorithmParameterSpec params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A byte[] currIv = null;
0N/A if (opmode == Cipher.WRAP_MODE) {
0N/A decrypting = false;
0N/A if (params == null) {
0N/A iv = new byte[8];
0N/A if (random == null) {
0N/A random = SunJCE.RANDOM;
0N/A }
0N/A random.nextBytes(iv);
0N/A }
0N/A else if (params instanceof IvParameterSpec) {
0N/A iv = ((IvParameterSpec) params).getIV();
0N/A } else {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("Wrong parameter type: IV expected");
0N/A }
0N/A currIv = iv;
0N/A } else if (opmode == Cipher.UNWRAP_MODE) {
0N/A if (params != null) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("No parameter accepted for unwrapping keys");
0N/A }
0N/A iv = null;
0N/A decrypting = true;
0N/A currIv = IV2;
0N/A } else {
0N/A throw new UnsupportedOperationException("This cipher can " +
0N/A "only be used for key wrapping and unwrapping");
0N/A }
0N/A cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(),
0N/A currIv);
0N/A cipherKey = key;
0N/A }
0N/A
0N/A /**
0N/A * Initializes this cipher with a key, a set of algorithm parameters,
0N/A * and a source of randomness.
0N/A *
0N/A * <p>The cipher only supports the following two operation modes:<b>
0N/A * Cipher.WRAP_MODE, and <b>
0N/A * Cipher.UNWRAP_MODE.
0N/A * <p>For modes other than the above two, UnsupportedOperationException
0N/A * will be thrown.
0N/A * <p>If this cipher requires an initialization vector (IV), it will get
0N/A * it from <code>random</code>.
0N/A *
0N/A * @param opmode the operation mode of this cipher. Only
0N/A * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
0N/A * @param key the secret key.
0N/A * @param params the algorithm parameters.
0N/A * @param random the source of randomness.
0N/A *
0N/A * @exception InvalidKeyException if the given key is inappropriate.
0N/A * @exception InvalidAlgorithmParameterException if the given algorithm
0N/A * parameters are inappropriate for this cipher.
0N/A */
0N/A protected void engineInit(int opmode, Key key,
0N/A AlgorithmParameters params,
0N/A SecureRandom random)
0N/A throws InvalidKeyException, InvalidAlgorithmParameterException {
0N/A IvParameterSpec ivSpec = null;
0N/A if (params != null) {
0N/A try {
0N/A DESedeParameters paramsEng = new DESedeParameters();
0N/A paramsEng.engineInit(params.getEncoded());
0N/A ivSpec = (IvParameterSpec)
0N/A paramsEng.engineGetParameterSpec(IvParameterSpec.class);
0N/A } catch (Exception ex) {
0N/A InvalidAlgorithmParameterException iape =
0N/A new InvalidAlgorithmParameterException
0N/A ("Wrong parameter type: IV expected");
0N/A iape.initCause(ex);
0N/A throw iape;
0N/A }
0N/A }
0N/A engineInit(opmode, key, ivSpec, random);
0N/A }
0N/A
0N/A /**
0N/A * This operation is not supported by this cipher.
0N/A * Since it's impossible to initialize this cipher given the
0N/A * current Cipher.engineInit(...) implementation,
0N/A * IllegalStateException will always be thrown upon invocation.
0N/A *
0N/A * @param in the input buffer.
0N/A * @param inOffset the offset in <code>in</code> where the input
0N/A * starts.
0N/A * @param inLen the input length.
0N/A *
0N/A * @return n/a.
0N/A *
0N/A * @exception IllegalStateException upon invocation of this method.
0N/A */
0N/A protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
0N/A throw new IllegalStateException("Cipher has not been initialized");
0N/A }
0N/A
0N/A /**
0N/A * This operation is not supported by this cipher.
0N/A * Since it's impossible to initialize this cipher given the
0N/A * current Cipher.engineInit(...) implementation,
0N/A * IllegalStateException will always be thrown upon invocation.
0N/A *
0N/A * @param in the input buffer.
0N/A * @param inOffset the offset in <code>in</code> where the input
0N/A * starts.
0N/A * @param inLen the input length.
0N/A * @param out the buffer for the result.
0N/A * @param outOffset the offset in <code>out</code> where the result
0N/A * is stored.
0N/A *
0N/A * @return n/a.
0N/A *
0N/A * @exception IllegalStateException upon invocation of this method.
0N/A */
0N/A protected int engineUpdate(byte[] in, int inOffset, int inLen,
0N/A byte[] out, int outOffset)
0N/A throws ShortBufferException {
0N/A throw new IllegalStateException("Cipher has not been initialized");
0N/A }
0N/A
0N/A /**
0N/A * This operation is not supported by this cipher.
0N/A * Since it's impossible to initialize this cipher given the
0N/A * current Cipher.engineInit(...) implementation,
0N/A * IllegalStateException will always be thrown upon invocation.
0N/A *
0N/A * @param in the input buffer.
0N/A * @param inOffset the offset in <code>in</code> where the input
0N/A * starts.
0N/A * @param inLen the input length.
0N/A *
0N/A * @return the new buffer with the result.
0N/A *
0N/A * @exception IllegalStateException upon invocation of this method.
0N/A */
0N/A protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen)
0N/A throws IllegalBlockSizeException, BadPaddingException {
0N/A throw new IllegalStateException("Cipher has not been initialized");
0N/A }
0N/A
0N/A /**
0N/A * This operation is not supported by this cipher.
0N/A * Since it's impossible to initialize this cipher given the
0N/A * current Cipher.engineInit(...) implementation,
0N/A * IllegalStateException will always be thrown upon invocation.
0N/A *
0N/A * @param in the input buffer.
0N/A * @param inOffset the offset in <code>in</code> where the input
0N/A * starts.
0N/A * @param inLen the input length.
0N/A * @param out the buffer for the result.
0N/A * @param outOffset the ofset in <code>out</code> where the result
0N/A * is stored.
0N/A *
0N/A * @return the number of bytes stored in <code>out</code>.
0N/A *
0N/A * @exception IllegalStateException upon invocation of this method.
0N/A */
0N/A protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
0N/A byte[] output, int outputOffset)
0N/A throws IllegalBlockSizeException, ShortBufferException,
0N/A BadPaddingException {
0N/A throw new IllegalStateException("Cipher has not been initialized");
0N/A }
0N/A
0N/A /**
0N/A * Returns the parameters used with this cipher.
0N/A * Note that null maybe returned if this cipher does not use any
0N/A * parameters or when it has not be set, e.g. initialized with
0N/A * UNWRAP_MODE but wrapped key data has not been given.
0N/A *
0N/A * @return the parameters used with this cipher; can be null.
0N/A */
0N/A protected AlgorithmParameters engineGetParameters() {
0N/A AlgorithmParameters params = null;
0N/A if (iv != null) {
0N/A String algo = cipherKey.getAlgorithm();
0N/A try {
0N/A params = AlgorithmParameters.getInstance(algo, "SunJCE");
0N/A } catch (NoSuchAlgorithmException nsae) {
0N/A // should never happen
0N/A throw new RuntimeException("Cannot find " + algo +
0N/A " AlgorithmParameters implementation in SunJCE provider");
0N/A } catch (NoSuchProviderException nspe) {
0N/A // should never happen
0N/A throw new RuntimeException("Cannot find SunJCE provider");
0N/A }
0N/A try {
0N/A params.init(new IvParameterSpec(iv));
0N/A } catch (InvalidParameterSpecException ipse) {
0N/A // should never happen
0N/A throw new RuntimeException("IvParameterSpec not supported");
0N/A }
0N/A }
0N/A return params;
0N/A }
0N/A
0N/A /**
0N/A * Returns the key size of the given key object in number of bits.
0N/A * This cipher always return the same key size as the DESede ciphers.
0N/A *
0N/A * @param key the key object.
0N/A *
0N/A * @return the "effective" key size of the given key object.
0N/A *
0N/A * @exception InvalidKeyException if <code>key</code> is invalid.
0N/A */
0N/A protected int engineGetKeySize(Key key) throws InvalidKeyException {
0N/A byte[] encoded = key.getEncoded();
0N/A if (encoded.length != 24) {
0N/A throw new InvalidKeyException("Invalid key length: " +
0N/A encoded.length + " bytes");
0N/A }
0N/A // Return the effective key length
0N/A return 112;
0N/A }
0N/A
0N/A /**
0N/A * Wrap a key.
0N/A *
0N/A * @param key the key to be wrapped.
0N/A *
0N/A * @return the wrapped key.
0N/A *
0N/A * @exception IllegalBlockSizeException if this cipher is a block
0N/A * cipher, no padding has been requested, and the length of the
0N/A * encoding of the key to be wrapped is not a
0N/A * multiple of the block size.
0N/A *
0N/A * @exception InvalidKeyException if it is impossible or unsafe to
0N/A * wrap the key with this cipher (e.g., a hardware protected key is
0N/A * being passed to a software only cipher).
0N/A */
0N/A protected byte[] engineWrap(Key key)
0N/A throws IllegalBlockSizeException, InvalidKeyException {
0N/A byte[] keyVal = key.getEncoded();
0N/A if ((keyVal == null) || (keyVal.length == 0)) {
0N/A throw new InvalidKeyException("Cannot get an encoding of " +
0N/A "the key to be wrapped");
0N/A }
0N/A
0N/A byte[] cks = getChecksum(keyVal);
0N/A byte[] out = new byte[iv.length + keyVal.length + cks.length];
0N/A
0N/A System.arraycopy(keyVal, 0, out, iv.length, keyVal.length);
0N/A System.arraycopy(cks, 0, out, iv.length+keyVal.length, cks.length);
0N/A cipher.encrypt(out, iv.length, keyVal.length+cks.length,
0N/A out, iv.length);
0N/A
0N/A System.arraycopy(iv, 0, out, 0, iv.length);
0N/A // reverse the array content
0N/A for (int i = 0; i < out.length/2; i++) {
0N/A byte temp = out[i];
0N/A out[i] = out[out.length-1-i];
0N/A out[out.length-1-i] = temp;
0N/A }
0N/A try {
0N/A cipher.init(false, cipherKey.getAlgorithm(),
0N/A cipherKey.getEncoded(), IV2);
0N/A } catch (InvalidKeyException ike) {
0N/A // should never happen
0N/A throw new RuntimeException("Internal cipher key is corrupted");
0N/A }
0N/A cipher.encrypt(out, 0, out.length, out, 0);
0N/A
0N/A // restore cipher state to prior to this call
0N/A try {
0N/A cipher.init(decrypting, cipherKey.getAlgorithm(),
0N/A cipherKey.getEncoded(), iv);
0N/A } catch (InvalidKeyException ike) {
0N/A // should never happen
0N/A throw new RuntimeException("Internal cipher key is corrupted");
0N/A }
0N/A return out;
0N/A }
0N/A
0N/A /**
0N/A * Unwrap a previously wrapped key.
0N/A *
0N/A * @param wrappedKey the key to be unwrapped.
0N/A *
0N/A * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
0N/A *
0N/A * @param wrappedKeyType the type of the wrapped key.
0N/A * This is one of <code>Cipher.SECRET_KEY</code>,
0N/A * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
0N/A *
0N/A * @return the unwrapped key.
0N/A *
0N/A * @exception NoSuchAlgorithmException if no installed providers
0N/A * can create keys of type <code>wrappedKeyType</code> for the
0N/A * <code>wrappedKeyAlgorithm</code>.
0N/A *
0N/A * @exception InvalidKeyException if <code>wrappedKey</code> does not
0N/A * represent a wrapped key of type <code>wrappedKeyType</code> for
0N/A * the <code>wrappedKeyAlgorithm</code>.
0N/A */
0N/A protected Key engineUnwrap(byte[] wrappedKey,
0N/A String wrappedKeyAlgorithm,
0N/A int wrappedKeyType)
0N/A throws InvalidKeyException, NoSuchAlgorithmException {
0N/A if (wrappedKey.length == 0) {
0N/A throw new InvalidKeyException("The wrapped key is empty");
0N/A }
0N/A byte[] buffer = new byte[wrappedKey.length];
0N/A cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);
0N/A
0N/A // reverse array content
0N/A for (int i = 0; i < buffer.length/2; i++) {
0N/A byte temp = buffer[i];
0N/A buffer[i] = buffer[buffer.length-1-i];
0N/A buffer[buffer.length-1-i] = temp;
0N/A }
0N/A iv = new byte[IV2.length];
0N/A System.arraycopy(buffer, 0, iv, 0, iv.length);
0N/A cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
0N/A iv);
0N/A cipher.decrypt(buffer, iv.length, buffer.length-iv.length,
0N/A buffer, iv.length);
0N/A int origLen = buffer.length - iv.length - 8;
0N/A byte[] cks = getChecksum(buffer, iv.length, origLen);
0N/A int offset = iv.length + origLen;
0N/A for (int i = 0; i < cks.length; i++) {
0N/A if (buffer[offset + i] != cks[i]) {
0N/A throw new InvalidKeyException("Checksum comparison failed");
0N/A }
0N/A }
0N/A // restore cipher state to prior to this call
0N/A cipher.init(decrypting, cipherKey.getAlgorithm(),
0N/A cipherKey.getEncoded(), IV2);
0N/A byte[] out = new byte[origLen];
0N/A System.arraycopy(buffer, iv.length, out, 0, out.length);
0N/A return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
0N/A wrappedKeyType);
0N/A }
0N/A
0N/A private static final byte[] getChecksum(byte[] in) {
0N/A return getChecksum(in, 0, in.length);
0N/A }
0N/A private static final byte[] getChecksum(byte[] in, int offset, int len) {
0N/A MessageDigest md = null;
0N/A try {
0N/A md = MessageDigest.getInstance("SHA1");
0N/A } catch (NoSuchAlgorithmException nsae) {
0N/A throw new RuntimeException("SHA1 message digest not available");
0N/A }
0N/A md.update(in, offset, len);
0N/A byte[] cks = new byte[8];
0N/A System.arraycopy(md.digest(), 0, cks, 0, cks.length);
0N/A return cks;
0N/A }
0N/A}