jdk/src/share/classes/com/sun/crypto/provider/AESWrapCipher.java
changeset 2 90ce3da70b43
child 3353 ddbd63234844
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package com.sun.crypto.provider;
       
    27 
       
    28 import java.util.Arrays;
       
    29 import java.security.*;
       
    30 import java.security.spec.*;
       
    31 import javax.crypto.*;
       
    32 import javax.crypto.spec.*;
       
    33 
       
    34 /**
       
    35  * This class implements the AES KeyWrap algorithm as defined
       
    36  * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
       
    37  * "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".
       
    38  * Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
       
    39  * can be used for this algorithm.
       
    40  *
       
    41  * @author Valerie Peng
       
    42  *
       
    43  *
       
    44  * @see AESCipher
       
    45  */
       
    46 public final class AESWrapCipher extends CipherSpi {
       
    47 
       
    48     private static final byte[] IV = {
       
    49         (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
       
    50         (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
       
    51     };
       
    52 
       
    53     private static final int blksize = AESConstants.AES_BLOCK_SIZE;
       
    54 
       
    55     /*
       
    56      * internal cipher object which does the real work.
       
    57      */
       
    58     private AESCrypt cipher;
       
    59 
       
    60     /*
       
    61      * are we encrypting or decrypting?
       
    62      */
       
    63     private boolean decrypting = false;
       
    64 
       
    65     /**
       
    66      * Creates an instance of AES KeyWrap cipher with default
       
    67      * mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
       
    68      *
       
    69      * @exception SecurityException if this constructor fails to verify
       
    70      * its own integrity
       
    71      */
       
    72     public AESWrapCipher() {
       
    73         SunJCE.ensureIntegrity(getClass());
       
    74         cipher = new AESCrypt();
       
    75     }
       
    76 
       
    77     /**
       
    78      * Sets the mode of this cipher. Only "ECB" mode is accepted for this
       
    79      * cipher.
       
    80      *
       
    81      * @param mode the cipher mode
       
    82      *
       
    83      * @exception NoSuchAlgorithmException if the requested cipher mode
       
    84      * is not "ECB".
       
    85      */
       
    86     protected void engineSetMode(String mode)
       
    87         throws NoSuchAlgorithmException {
       
    88         if (!mode.equalsIgnoreCase("ECB")) {
       
    89             throw new NoSuchAlgorithmException(mode + " cannot be used");
       
    90         }
       
    91     }
       
    92 
       
    93     /**
       
    94      * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
       
    95      * is accepted for this cipher.
       
    96      *
       
    97      * @param padding the padding mechanism
       
    98      *
       
    99      * @exception NoSuchPaddingException if the requested padding mechanism
       
   100      * is not "NoPadding".
       
   101      */
       
   102     protected void engineSetPadding(String padding)
       
   103         throws NoSuchPaddingException {
       
   104         if (!padding.equalsIgnoreCase("NoPadding")) {
       
   105             throw new NoSuchPaddingException(padding + " cannot be used");
       
   106         }
       
   107     }
       
   108 
       
   109     /**
       
   110      * Returns the block size (in bytes). i.e. 16 bytes.
       
   111      *
       
   112      * @return the block size (in bytes), i.e. 16 bytes.
       
   113      */
       
   114     protected int engineGetBlockSize() {
       
   115         return blksize;
       
   116     }
       
   117 
       
   118     /**
       
   119      * Returns the length in bytes that an output buffer would need to be
       
   120      * given the input length <code>inputLen</code> (in bytes).
       
   121      *
       
   122      * <p>The actual output length of the next <code>update</code> or
       
   123      * <code>doFinal</code> call may be smaller than the length returned
       
   124      * by this method.
       
   125      *
       
   126      * @param inputLen the input length (in bytes)
       
   127      *
       
   128      * @return the required output buffer size (in bytes)
       
   129      */
       
   130     protected int engineGetOutputSize(int inputLen) {
       
   131         // can only return an upper-limit if not initialized yet.
       
   132         int result = 0;
       
   133         if (decrypting) {
       
   134             result = inputLen - 8;
       
   135         } else {
       
   136             result = inputLen + 8;
       
   137         }
       
   138         return (result < 0? 0:result);
       
   139     }
       
   140 
       
   141     /**
       
   142      * Returns the initialization vector (IV) which is null for this cipher.
       
   143      *
       
   144      * @return null for this cipher.
       
   145      */
       
   146     protected byte[] engineGetIV() {
       
   147         return null;
       
   148     }
       
   149 
       
   150     /**
       
   151      * Initializes this cipher with a key and a source of randomness.
       
   152      *
       
   153      * <p>The cipher only supports the following two operation modes:<b>
       
   154      * Cipher.WRAP_MODE, and <b>
       
   155      * Cipher.UNWRAP_MODE.
       
   156      * <p>For modes other than the above two, UnsupportedOperationException
       
   157      * will be thrown.
       
   158      *
       
   159      * @param opmode the operation mode of this cipher. Only
       
   160      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
       
   161      * @param key the secret key.
       
   162      * @param random the source of randomness.
       
   163      *
       
   164      * @exception InvalidKeyException if the given key is inappropriate for
       
   165      * initializing this cipher.
       
   166      */
       
   167     protected void engineInit(int opmode, Key key, SecureRandom random)
       
   168         throws InvalidKeyException {
       
   169         if (opmode == Cipher.WRAP_MODE) {
       
   170             decrypting = false;
       
   171         } else if (opmode == Cipher.UNWRAP_MODE) {
       
   172             decrypting = true;
       
   173         } else {
       
   174             throw new UnsupportedOperationException("This cipher can " +
       
   175                 "only be used for key wrapping and unwrapping");
       
   176         }
       
   177         cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());
       
   178     }
       
   179 
       
   180     /**
       
   181      * Initializes this cipher with a key, a set of algorithm parameters,
       
   182      * and a source of randomness.
       
   183      *
       
   184      * <p>The cipher only supports the following two operation modes:<b>
       
   185      * Cipher.WRAP_MODE, and <b>
       
   186      * Cipher.UNWRAP_MODE.
       
   187      * <p>For modes other than the above two, UnsupportedOperationException
       
   188      * will be thrown.
       
   189      *
       
   190      * @param opmode the operation mode of this cipher. Only
       
   191      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
       
   192      * @param key the secret key.
       
   193      * @param params the algorithm parameters; must be null for this cipher.
       
   194      * @param random the source of randomness.
       
   195      *
       
   196      * @exception InvalidKeyException if the given key is inappropriate for
       
   197      * initializing this cipher
       
   198      * @exception InvalidAlgorithmParameterException if the given algorithm
       
   199      * parameters is not null.
       
   200      */
       
   201     protected void engineInit(int opmode, Key key,
       
   202                               AlgorithmParameterSpec params,
       
   203                               SecureRandom random)
       
   204         throws InvalidKeyException, InvalidAlgorithmParameterException {
       
   205         if (params != null) {
       
   206             throw new InvalidAlgorithmParameterException("This cipher " +
       
   207                 "does not accept any parameters");
       
   208         }
       
   209         engineInit(opmode, key, random);
       
   210     }
       
   211 
       
   212     /**
       
   213      * Initializes this cipher with a key, a set of algorithm parameters,
       
   214      * and a source of randomness.
       
   215      *
       
   216      * <p>The cipher only supports the following two operation modes:<b>
       
   217      * Cipher.WRAP_MODE, and <b>
       
   218      * Cipher.UNWRAP_MODE.
       
   219      * <p>For modes other than the above two, UnsupportedOperationException
       
   220      * will be thrown.
       
   221      *
       
   222      * @param opmode the operation mode of this cipher. Only
       
   223      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
       
   224      * @param key the secret key.
       
   225      * @param params the algorithm parameters; must be null for this cipher.
       
   226      * @param random the source of randomness.
       
   227      *
       
   228      * @exception InvalidKeyException if the given key is inappropriate.
       
   229      * @exception InvalidAlgorithmParameterException if the given algorithm
       
   230      * parameters is not null.
       
   231      */
       
   232     protected void engineInit(int opmode, Key key,
       
   233                               AlgorithmParameters params,
       
   234                               SecureRandom random)
       
   235         throws InvalidKeyException, InvalidAlgorithmParameterException {
       
   236         if (params != null) {
       
   237             throw new InvalidAlgorithmParameterException("This cipher " +
       
   238                 "does not accept any parameters");
       
   239         }
       
   240         engineInit(opmode, key, random);
       
   241     }
       
   242 
       
   243     /**
       
   244      * This operation is not supported by this cipher.
       
   245      * Since it's impossible to initialize this cipher given the
       
   246      * current Cipher.engineInit(...) implementation,
       
   247      * IllegalStateException will always be thrown upon invocation.
       
   248      *
       
   249      * @param in the input buffer.
       
   250      * @param inOffset the offset in <code>in</code> where the input
       
   251      * starts.
       
   252      * @param inLen the input length.
       
   253      *
       
   254      * @return n/a.
       
   255      *
       
   256      * @exception IllegalStateException upon invocation of this method.
       
   257      */
       
   258     protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
       
   259         throw new IllegalStateException("Cipher has not been initialized");
       
   260     }
       
   261 
       
   262     /**
       
   263      * This operation is not supported by this cipher.
       
   264      * Since it's impossible to initialize this cipher given the
       
   265      * current Cipher.engineInit(...) implementation,
       
   266      * IllegalStateException will always be thrown upon invocation.
       
   267      *
       
   268      * @param in the input buffer.
       
   269      * @param inOffset the offset in <code>in</code> where the input
       
   270      * starts.
       
   271      * @param inLen the input length.
       
   272      * @param out the buffer for the result.
       
   273      * @param outOffset the offset in <code>out</code> where the result
       
   274      * is stored.
       
   275      *
       
   276      * @return n/a.
       
   277      *
       
   278      * @exception IllegalStateException upon invocation of this method.
       
   279      */
       
   280     protected int engineUpdate(byte[] in, int inOffset, int inLen,
       
   281                                byte[] out, int outOffset)
       
   282         throws ShortBufferException {
       
   283         throw new IllegalStateException("Cipher has not been initialized");
       
   284     }
       
   285 
       
   286     /**
       
   287      * This operation is not supported by this cipher.
       
   288      * Since it's impossible to initialize this cipher given the
       
   289      * current Cipher.engineInit(...) implementation,
       
   290      * IllegalStateException will always be thrown upon invocation.
       
   291      *
       
   292      * @param in the input buffer
       
   293      * @param inOffset the offset in <code>in</code> where the input
       
   294      * starts
       
   295      * @param inLen the input length.
       
   296      *
       
   297      * @return n/a.
       
   298      *
       
   299      * @exception IllegalStateException upon invocation of this method.
       
   300      */
       
   301     protected byte[] engineDoFinal(byte[] input, int inputOffset,
       
   302                                    int inputLen)
       
   303         throws IllegalBlockSizeException, BadPaddingException {
       
   304         throw new IllegalStateException("Cipher has not been initialized");
       
   305     }
       
   306 
       
   307     /**
       
   308      * This operation is not supported by this cipher.
       
   309      * Since it's impossible to initialize this cipher given the
       
   310      * current Cipher.engineInit(...) implementation,
       
   311      * IllegalStateException will always be thrown upon invocation.
       
   312      *
       
   313      * @param in the input buffer.
       
   314      * @param inOffset the offset in <code>in</code> where the input
       
   315      * starts.
       
   316      * @param inLen the input length.
       
   317      * @param out the buffer for the result.
       
   318      * @param outOffset the ofset in <code>out</code> where the result
       
   319      * is stored.
       
   320      *
       
   321      * @return n/a.
       
   322      *
       
   323      * @exception IllegalStateException upon invocation of this method.
       
   324      */
       
   325     protected int engineDoFinal(byte[] in, int inOffset, int inLen,
       
   326                                 byte[] out, int outOffset)
       
   327         throws IllegalBlockSizeException, ShortBufferException,
       
   328                BadPaddingException {
       
   329         throw new IllegalStateException("Cipher has not been initialized");
       
   330     }
       
   331 
       
   332     /**
       
   333      * Returns the parameters used with this cipher which is always null
       
   334      * for this cipher.
       
   335      *
       
   336      * @return null since this cipher does not use any parameters.
       
   337      */
       
   338     protected AlgorithmParameters engineGetParameters() {
       
   339         return null;
       
   340     }
       
   341 
       
   342     /**
       
   343      * Returns the key size of the given key object in number of bits.
       
   344      *
       
   345      * @param key the key object.
       
   346      *
       
   347      * @return the "effective" key size of the given key object.
       
   348      *
       
   349      * @exception InvalidKeyException if <code>key</code> is invalid.
       
   350      */
       
   351     protected int engineGetKeySize(Key key) throws InvalidKeyException {
       
   352         byte[] encoded = key.getEncoded();
       
   353         if (!AESCrypt.isKeySizeValid(encoded.length)) {
       
   354             throw new InvalidKeyException("Invalid key length: " +
       
   355                                           encoded.length + " bytes");
       
   356         }
       
   357         return encoded.length * 8;
       
   358     }
       
   359 
       
   360     /**
       
   361      * Wrap a key.
       
   362      *
       
   363      * @param key the key to be wrapped.
       
   364      *
       
   365      * @return the wrapped key.
       
   366      *
       
   367      * @exception IllegalBlockSizeException if this cipher is a block
       
   368      * cipher, no padding has been requested, and the length of the
       
   369      * encoding of the key to be wrapped is not a
       
   370      * multiple of the block size.
       
   371      *
       
   372      * @exception InvalidKeyException if it is impossible or unsafe to
       
   373      * wrap the key with this cipher (e.g., a hardware protected key is
       
   374      * being passed to a software only cipher).
       
   375      */
       
   376     protected byte[] engineWrap(Key key)
       
   377         throws IllegalBlockSizeException, InvalidKeyException {
       
   378         byte[] keyVal = key.getEncoded();
       
   379         if ((keyVal == null) || (keyVal.length == 0)) {
       
   380             throw new InvalidKeyException("Cannot get an encoding of " +
       
   381                                           "the key to be wrapped");
       
   382         }
       
   383         byte[] out = new byte[keyVal.length + 8];
       
   384 
       
   385         if (keyVal.length == 8) {
       
   386             System.arraycopy(IV, 0, out, 0, IV.length);
       
   387             System.arraycopy(keyVal, 0, out, IV.length, 8);
       
   388             cipher.encryptBlock(out, 0, out, 0);
       
   389         } else {
       
   390             if (keyVal.length % 8 != 0) {
       
   391                 throw new IllegalBlockSizeException("length of the " +
       
   392                     "to be wrapped key should be multiples of 8 bytes");
       
   393             }
       
   394             System.arraycopy(IV, 0, out, 0, IV.length);
       
   395             System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
       
   396             int N = keyVal.length/8;
       
   397             byte[] buffer = new byte[blksize];
       
   398             for (int j = 0; j < 6; j++) {
       
   399                 for (int i = 1; i <= N; i++) {
       
   400                     int T = i + j*N;
       
   401                     System.arraycopy(out, 0, buffer, 0, IV.length);
       
   402                     System.arraycopy(out, i*8, buffer, IV.length, 8);
       
   403                     cipher.encryptBlock(buffer, 0, buffer, 0);
       
   404                     for (int k = 1; T != 0; k++) {
       
   405                         byte v = (byte) T;
       
   406                         buffer[IV.length - k] ^= v;
       
   407                         T >>>= 8;
       
   408                     }
       
   409                     System.arraycopy(buffer, 0, out, 0, IV.length);
       
   410                     System.arraycopy(buffer, 8, out, 8*i, 8);
       
   411                 }
       
   412             }
       
   413         }
       
   414         return out;
       
   415     }
       
   416 
       
   417     /**
       
   418      * Unwrap a previously wrapped key.
       
   419      *
       
   420      * @param wrappedKey the key to be unwrapped.
       
   421      *
       
   422      * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
       
   423      *
       
   424      * @param wrappedKeyType the type of the wrapped key.
       
   425      * This is one of <code>Cipher.SECRET_KEY</code>,
       
   426      * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
       
   427      *
       
   428      * @return the unwrapped key.
       
   429      *
       
   430      * @exception NoSuchAlgorithmException if no installed providers
       
   431      * can create keys of type <code>wrappedKeyType</code> for the
       
   432      * <code>wrappedKeyAlgorithm</code>.
       
   433      *
       
   434      * @exception InvalidKeyException if <code>wrappedKey</code> does not
       
   435      * represent a wrapped key of type <code>wrappedKeyType</code> for
       
   436      * the <code>wrappedKeyAlgorithm</code>.
       
   437      */
       
   438     protected Key engineUnwrap(byte[] wrappedKey,
       
   439                                String wrappedKeyAlgorithm,
       
   440                                int wrappedKeyType)
       
   441         throws InvalidKeyException, NoSuchAlgorithmException {
       
   442         int wrappedKeyLen = wrappedKey.length;
       
   443         // ensure the wrappedKey length is multiples of 8 bytes and non-zero
       
   444         if (wrappedKeyLen == 0) {
       
   445             throw new InvalidKeyException("The wrapped key is empty");
       
   446         }
       
   447         if (wrappedKeyLen % 8 != 0) {
       
   448             throw new InvalidKeyException
       
   449                 ("The wrapped key has invalid key length");
       
   450         }
       
   451         byte[] out = new byte[wrappedKeyLen - 8];
       
   452         byte[] buffer = new byte[blksize];
       
   453         if (wrappedKeyLen == 16) {
       
   454             cipher.decryptBlock(wrappedKey, 0, buffer, 0);
       
   455             for (int i = 0; i < IV.length; i++) {
       
   456                 if (IV[i] != buffer[i]) {
       
   457                     throw new InvalidKeyException("Integrity check failed");
       
   458                 }
       
   459             }
       
   460             System.arraycopy(buffer, IV.length, out, 0, out.length);
       
   461         } else {
       
   462             System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
       
   463             System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
       
   464             int N = out.length/8;
       
   465             for (int j = 5; j >= 0; j--) {
       
   466                 for (int i = N; i > 0; i--) {
       
   467                     int T = i + j*N;
       
   468                     System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);
       
   469                     for (int k = 1; T != 0; k++) {
       
   470                         byte v = (byte) T;
       
   471                         buffer[IV.length - k] ^= v;
       
   472                         T >>>= 8;
       
   473                     }
       
   474                     cipher.decryptBlock(buffer, 0, buffer, 0);
       
   475                     System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);
       
   476                 }
       
   477             }
       
   478             for (int i = 0; i < IV.length; i++) {
       
   479                 if (IV[i] != buffer[i]) {
       
   480                     throw new InvalidKeyException("Integrity check failed");
       
   481                 }
       
   482             }
       
   483         }
       
   484         return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
       
   485                                           wrappedKeyType);
       
   486     }
       
   487 }