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 AES 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.3 "AES Key Wrap".
0N/A * Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
0N/A * can be used for this algorithm.
0N/A *
0N/A * @author Valerie Peng
0N/A *
0N/A *
0N/A * @see AESCipher
0N/A */
0N/Apublic final class AESWrapCipher extends CipherSpi {
0N/A
0N/A private static final byte[] IV = {
0N/A (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
0N/A (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
0N/A };
0N/A
0N/A private static final int blksize = AESConstants.AES_BLOCK_SIZE;
0N/A
0N/A /*
0N/A * internal cipher object which does the real work.
0N/A */
0N/A private AESCrypt cipher;
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 AES KeyWrap cipher with default
0N/A * mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
0N/A */
0N/A public AESWrapCipher() {
0N/A cipher = new AESCrypt();
0N/A }
0N/A
0N/A /**
0N/A * Sets the mode of this cipher. Only "ECB" 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 "ECB".
0N/A */
0N/A protected void engineSetMode(String mode)
0N/A throws NoSuchAlgorithmException {
0N/A if (!mode.equalsIgnoreCase("ECB")) {
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. 16 bytes.
0N/A *
0N/A * @return the block size (in bytes), i.e. 16 bytes.
0N/A */
0N/A protected int engineGetBlockSize() {
0N/A return blksize;
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 - 8;
0N/A } else {
0N/A result = inputLen + 8;
0N/A }
0N/A return (result < 0? 0:result);
0N/A }
0N/A
0N/A /**
0N/A * Returns the initialization vector (IV) which is null for this cipher.
0N/A *
0N/A * @return null for this cipher.
0N/A */
0N/A protected byte[] engineGetIV() {
0N/A return null;
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 *
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 for
0N/A * initializing this cipher.
0N/A */
0N/A protected void engineInit(int opmode, Key key, SecureRandom random)
0N/A throws InvalidKeyException {
0N/A if (opmode == Cipher.WRAP_MODE) {
0N/A decrypting = false;
0N/A } else if (opmode == Cipher.UNWRAP_MODE) {
0N/A decrypting = true;
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 }
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 *
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; must be null for this cipher.
0N/A * @param random the source of randomness.
0N/A *
0N/A * @exception InvalidKeyException if the given key is inappropriate for
0N/A * initializing this cipher
0N/A * @exception InvalidAlgorithmParameterException if the given algorithm
0N/A * parameters is not null.
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 if (params != null) {
0N/A throw new InvalidAlgorithmParameterException("This cipher " +
0N/A "does not accept any parameters");
0N/A }
0N/A engineInit(opmode, key, random);
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 *
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; must be null for this cipher.
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 is not null.
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 if (params != null) {
0N/A throw new InvalidAlgorithmParameterException("This cipher " +
0N/A "does not accept any parameters");
0N/A }
0N/A engineInit(opmode, key, 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 n/a.
0N/A *
0N/A * @exception IllegalStateException upon invocation of this method.
0N/A */
0N/A protected byte[] engineDoFinal(byte[] input, int inputOffset,
0N/A int inputLen)
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 n/a.
0N/A *
0N/A * @exception IllegalStateException upon invocation of this method.
0N/A */
0N/A protected int engineDoFinal(byte[] in, int inOffset, int inLen,
0N/A byte[] out, int outOffset)
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 which is always null
0N/A * for this cipher.
0N/A *
0N/A * @return null since this cipher does not use any parameters.
0N/A */
0N/A protected AlgorithmParameters engineGetParameters() {
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the key size of the given key object in number of bits.
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 (!AESCrypt.isKeySizeValid(encoded.length)) {
0N/A throw new InvalidKeyException("Invalid key length: " +
0N/A encoded.length + " bytes");
0N/A }
0N/A return encoded.length * 8;
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 byte[] out = new byte[keyVal.length + 8];
0N/A
0N/A if (keyVal.length == 8) {
0N/A System.arraycopy(IV, 0, out, 0, IV.length);
0N/A System.arraycopy(keyVal, 0, out, IV.length, 8);
0N/A cipher.encryptBlock(out, 0, out, 0);
0N/A } else {
0N/A if (keyVal.length % 8 != 0) {
0N/A throw new IllegalBlockSizeException("length of the " +
0N/A "to be wrapped key should be multiples of 8 bytes");
0N/A }
0N/A System.arraycopy(IV, 0, out, 0, IV.length);
0N/A System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
0N/A int N = keyVal.length/8;
0N/A byte[] buffer = new byte[blksize];
0N/A for (int j = 0; j < 6; j++) {
0N/A for (int i = 1; i <= N; i++) {
0N/A int T = i + j*N;
0N/A System.arraycopy(out, 0, buffer, 0, IV.length);
0N/A System.arraycopy(out, i*8, buffer, IV.length, 8);
0N/A cipher.encryptBlock(buffer, 0, buffer, 0);
0N/A for (int k = 1; T != 0; k++) {
0N/A byte v = (byte) T;
0N/A buffer[IV.length - k] ^= v;
0N/A T >>>= 8;
0N/A }
0N/A System.arraycopy(buffer, 0, out, 0, IV.length);
0N/A System.arraycopy(buffer, 8, out, 8*i, 8);
0N/A }
0N/A }
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 int wrappedKeyLen = wrappedKey.length;
0N/A // ensure the wrappedKey length is multiples of 8 bytes and non-zero
0N/A if (wrappedKeyLen == 0) {
0N/A throw new InvalidKeyException("The wrapped key is empty");
0N/A }
0N/A if (wrappedKeyLen % 8 != 0) {
0N/A throw new InvalidKeyException
0N/A ("The wrapped key has invalid key length");
0N/A }
0N/A byte[] out = new byte[wrappedKeyLen - 8];
0N/A byte[] buffer = new byte[blksize];
0N/A if (wrappedKeyLen == 16) {
0N/A cipher.decryptBlock(wrappedKey, 0, buffer, 0);
0N/A for (int i = 0; i < IV.length; i++) {
0N/A if (IV[i] != buffer[i]) {
0N/A throw new InvalidKeyException("Integrity check failed");
0N/A }
0N/A }
0N/A System.arraycopy(buffer, IV.length, out, 0, out.length);
0N/A } else {
0N/A System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
0N/A System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
0N/A int N = out.length/8;
0N/A for (int j = 5; j >= 0; j--) {
0N/A for (int i = N; i > 0; i--) {
0N/A int T = i + j*N;
0N/A System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);
0N/A for (int k = 1; T != 0; k++) {
0N/A byte v = (byte) T;
0N/A buffer[IV.length - k] ^= v;
0N/A T >>>= 8;
0N/A }
0N/A cipher.decryptBlock(buffer, 0, buffer, 0);
0N/A System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);
0N/A }
0N/A }
0N/A for (int i = 0; i < IV.length; i++) {
0N/A if (IV[i] != buffer[i]) {
0N/A throw new InvalidKeyException("Integrity check failed");
0N/A }
0N/A }
0N/A }
0N/A return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
0N/A wrappedKeyType);
0N/A }
0N/A}