Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]
    1   /*
    2    * Copyright (c) 2004, 2009, Oracle and/or its affiliates. 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.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * 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       public AESWrapCipher() {
   70           cipher = new AESCrypt();
   71       }
   72   
   73       /**
   74        * Sets the mode of this cipher. Only "ECB" mode is accepted for this
   75        * cipher.
   76        *
   77        * @param mode the cipher mode
   78        *
   79        * @exception NoSuchAlgorithmException if the requested cipher mode
   80        * is not "ECB".
   81        */
   82       protected void engineSetMode(String mode)
   83           throws NoSuchAlgorithmException {
   84           if (!mode.equalsIgnoreCase("ECB")) {
   85               throw new NoSuchAlgorithmException(mode + " cannot be used");
   86           }
   87       }
   88   
   89       /**
   90        * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
   91        * is accepted for this cipher.
   92        *
   93        * @param padding the padding mechanism
   94        *
   95        * @exception NoSuchPaddingException if the requested padding mechanism
   96        * is not "NoPadding".
   97        */
   98       protected void engineSetPadding(String padding)
   99           throws NoSuchPaddingException {
  100           if (!padding.equalsIgnoreCase("NoPadding")) {
  101               throw new NoSuchPaddingException(padding + " cannot be used");
  102           }
  103       }
  104   
  105       /**
  106        * Returns the block size (in bytes). i.e. 16 bytes.
  107        *
  108        * @return the block size (in bytes), i.e. 16 bytes.
  109        */
  110       protected int engineGetBlockSize() {
  111           return blksize;
  112       }
  113   
  114       /**
  115        * Returns the length in bytes that an output buffer would need to be
  116        * given the input length <code>inputLen</code> (in bytes).
  117        *
  118        * <p>The actual output length of the next <code>update</code> or
  119        * <code>doFinal</code> call may be smaller than the length returned
  120        * by this method.
  121        *
  122        * @param inputLen the input length (in bytes)
  123        *
  124        * @return the required output buffer size (in bytes)
  125        */
  126       protected int engineGetOutputSize(int inputLen) {
  127           // can only return an upper-limit if not initialized yet.
  128           int result = 0;
  129           if (decrypting) {
  130               result = inputLen - 8;
  131           } else {
  132               result = inputLen + 8;
  133           }
  134           return (result < 0? 0:result);
  135       }
  136   
  137       /**
  138        * Returns the initialization vector (IV) which is null for this cipher.
  139        *
  140        * @return null for this cipher.
  141        */
  142       protected byte[] engineGetIV() {
  143           return null;
  144       }
  145   
  146       /**
  147        * Initializes this cipher with a key and a source of randomness.
  148        *
  149        * <p>The cipher only supports the following two operation modes:<b>
  150        * Cipher.WRAP_MODE, and <b>
  151        * Cipher.UNWRAP_MODE.
  152        * <p>For modes other than the above two, UnsupportedOperationException
  153        * will be thrown.
  154        *
  155        * @param opmode the operation mode of this cipher. Only
  156        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
  157        * @param key the secret key.
  158        * @param random the source of randomness.
  159        *
  160        * @exception InvalidKeyException if the given key is inappropriate for
  161        * initializing this cipher.
  162        */
  163       protected void engineInit(int opmode, Key key, SecureRandom random)
  164           throws InvalidKeyException {
  165           if (opmode == Cipher.WRAP_MODE) {
  166               decrypting = false;
  167           } else if (opmode == Cipher.UNWRAP_MODE) {
  168               decrypting = true;
  169           } else {
  170               throw new UnsupportedOperationException("This cipher can " +
  171                   "only be used for key wrapping and unwrapping");
  172           }
  173           cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());
  174       }
  175   
  176       /**
  177        * Initializes this cipher with a key, a set of algorithm parameters,
  178        * and a source of randomness.
  179        *
  180        * <p>The cipher only supports the following two operation modes:<b>
  181        * Cipher.WRAP_MODE, and <b>
  182        * Cipher.UNWRAP_MODE.
  183        * <p>For modes other than the above two, UnsupportedOperationException
  184        * will be thrown.
  185        *
  186        * @param opmode the operation mode of this cipher. Only
  187        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
  188        * @param key the secret key.
  189        * @param params the algorithm parameters; must be null for this cipher.
  190        * @param random the source of randomness.
  191        *
  192        * @exception InvalidKeyException if the given key is inappropriate for
  193        * initializing this cipher
  194        * @exception InvalidAlgorithmParameterException if the given algorithm
  195        * parameters is not null.
  196        */
  197       protected void engineInit(int opmode, Key key,
  198                                 AlgorithmParameterSpec params,
  199                                 SecureRandom random)
  200           throws InvalidKeyException, InvalidAlgorithmParameterException {
  201           if (params != null) {
  202               throw new InvalidAlgorithmParameterException("This cipher " +
  203                   "does not accept any parameters");
  204           }
  205           engineInit(opmode, key, random);
  206       }
  207   
  208       /**
  209        * Initializes this cipher with a key, a set of algorithm parameters,
  210        * and a source of randomness.
  211        *
  212        * <p>The cipher only supports the following two operation modes:<b>
  213        * Cipher.WRAP_MODE, and <b>
  214        * Cipher.UNWRAP_MODE.
  215        * <p>For modes other than the above two, UnsupportedOperationException
  216        * will be thrown.
  217        *
  218        * @param opmode the operation mode of this cipher. Only
  219        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
  220        * @param key the secret key.
  221        * @param params the algorithm parameters; must be null for this cipher.
  222        * @param random the source of randomness.
  223        *
  224        * @exception InvalidKeyException if the given key is inappropriate.
  225        * @exception InvalidAlgorithmParameterException if the given algorithm
  226        * parameters is not null.
  227        */
  228       protected void engineInit(int opmode, Key key,
  229                                 AlgorithmParameters params,
  230                                 SecureRandom random)
  231           throws InvalidKeyException, InvalidAlgorithmParameterException {
  232           if (params != null) {
  233               throw new InvalidAlgorithmParameterException("This cipher " +
  234                   "does not accept any parameters");
  235           }
  236           engineInit(opmode, key, random);
  237       }
  238   
  239       /**
  240        * This operation is not supported by this cipher.
  241        * Since it's impossible to initialize this cipher given the
  242        * current Cipher.engineInit(...) implementation,
  243        * IllegalStateException will always be thrown upon invocation.
  244        *
  245        * @param in the input buffer.
  246        * @param inOffset the offset in <code>in</code> where the input
  247        * starts.
  248        * @param inLen the input length.
  249        *
  250        * @return n/a.
  251        *
  252        * @exception IllegalStateException upon invocation of this method.
  253        */
  254       protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
  255           throw new IllegalStateException("Cipher has not been initialized");
  256       }
  257   
  258       /**
  259        * This operation is not supported by this cipher.
  260        * Since it's impossible to initialize this cipher given the
  261        * current Cipher.engineInit(...) implementation,
  262        * IllegalStateException will always be thrown upon invocation.
  263        *
  264        * @param in the input buffer.
  265        * @param inOffset the offset in <code>in</code> where the input
  266        * starts.
  267        * @param inLen the input length.
  268        * @param out the buffer for the result.
  269        * @param outOffset the offset in <code>out</code> where the result
  270        * is stored.
  271        *
  272        * @return n/a.
  273        *
  274        * @exception IllegalStateException upon invocation of this method.
  275        */
  276       protected int engineUpdate(byte[] in, int inOffset, int inLen,
  277                                  byte[] out, int outOffset)
  278           throws ShortBufferException {
  279           throw new IllegalStateException("Cipher has not been initialized");
  280       }
  281   
  282       /**
  283        * This operation is not supported by this cipher.
  284        * Since it's impossible to initialize this cipher given the
  285        * current Cipher.engineInit(...) implementation,
  286        * IllegalStateException will always be thrown upon invocation.
  287        *
  288        * @param in the input buffer
  289        * @param inOffset the offset in <code>in</code> where the input
  290        * starts
  291        * @param inLen the input length.
  292        *
  293        * @return n/a.
  294        *
  295        * @exception IllegalStateException upon invocation of this method.
  296        */
  297       protected byte[] engineDoFinal(byte[] input, int inputOffset,
  298                                      int inputLen)
  299           throws IllegalBlockSizeException, BadPaddingException {
  300           throw new IllegalStateException("Cipher has not been initialized");
  301       }
  302   
  303       /**
  304        * This operation is not supported by this cipher.
  305        * Since it's impossible to initialize this cipher given the
  306        * current Cipher.engineInit(...) implementation,
  307        * IllegalStateException will always be thrown upon invocation.
  308        *
  309        * @param in the input buffer.
  310        * @param inOffset the offset in <code>in</code> where the input
  311        * starts.
  312        * @param inLen the input length.
  313        * @param out the buffer for the result.
  314        * @param outOffset the ofset in <code>out</code> where the result
  315        * is stored.
  316        *
  317        * @return n/a.
  318        *
  319        * @exception IllegalStateException upon invocation of this method.
  320        */
  321       protected int engineDoFinal(byte[] in, int inOffset, int inLen,
  322                                   byte[] out, int outOffset)
  323           throws IllegalBlockSizeException, ShortBufferException,
  324                  BadPaddingException {
  325           throw new IllegalStateException("Cipher has not been initialized");
  326       }
  327   
  328       /**
  329        * Returns the parameters used with this cipher which is always null
  330        * for this cipher.
  331        *
  332        * @return null since this cipher does not use any parameters.
  333        */
  334       protected AlgorithmParameters engineGetParameters() {
  335           return null;
  336       }
  337   
  338       /**
  339        * Returns the key size of the given key object in number of bits.
  340        *
  341        * @param key the key object.
  342        *
  343        * @return the "effective" key size of the given key object.
  344        *
  345        * @exception InvalidKeyException if <code>key</code> is invalid.
  346        */
  347       protected int engineGetKeySize(Key key) throws InvalidKeyException {
  348           byte[] encoded = key.getEncoded();
  349           if (!AESCrypt.isKeySizeValid(encoded.length)) {
  350               throw new InvalidKeyException("Invalid key length: " +
  351                                             encoded.length + " bytes");
  352           }
  353           return encoded.length * 8;
  354       }
  355   
  356       /**
  357        * Wrap a key.
  358        *
  359        * @param key the key to be wrapped.
  360        *
  361        * @return the wrapped key.
  362        *
  363        * @exception IllegalBlockSizeException if this cipher is a block
  364        * cipher, no padding has been requested, and the length of the
  365        * encoding of the key to be wrapped is not a
  366        * multiple of the block size.
  367        *
  368        * @exception InvalidKeyException if it is impossible or unsafe to
  369        * wrap the key with this cipher (e.g., a hardware protected key is
  370        * being passed to a software only cipher).
  371        */
  372       protected byte[] engineWrap(Key key)
  373           throws IllegalBlockSizeException, InvalidKeyException {
  374           byte[] keyVal = key.getEncoded();
  375           if ((keyVal == null) || (keyVal.length == 0)) {
  376               throw new InvalidKeyException("Cannot get an encoding of " +
  377                                             "the key to be wrapped");
  378           }
  379           byte[] out = new byte[keyVal.length + 8];
  380   
  381           if (keyVal.length == 8) {
  382               System.arraycopy(IV, 0, out, 0, IV.length);
  383               System.arraycopy(keyVal, 0, out, IV.length, 8);
  384               cipher.encryptBlock(out, 0, out, 0);
  385           } else {
  386               if (keyVal.length % 8 != 0) {
  387                   throw new IllegalBlockSizeException("length of the " +
  388                       "to be wrapped key should be multiples of 8 bytes");
  389               }
  390               System.arraycopy(IV, 0, out, 0, IV.length);
  391               System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
  392               int N = keyVal.length/8;
  393               byte[] buffer = new byte[blksize];
  394               for (int j = 0; j < 6; j++) {
  395                   for (int i = 1; i <= N; i++) {
  396                       int T = i + j*N;
  397                       System.arraycopy(out, 0, buffer, 0, IV.length);
  398                       System.arraycopy(out, i*8, buffer, IV.length, 8);
  399                       cipher.encryptBlock(buffer, 0, buffer, 0);
  400                       for (int k = 1; T != 0; k++) {
  401                           byte v = (byte) T;
  402                           buffer[IV.length - k] ^= v;
  403                           T >>>= 8;
  404                       }
  405                       System.arraycopy(buffer, 0, out, 0, IV.length);
  406                       System.arraycopy(buffer, 8, out, 8*i, 8);
  407                   }
  408               }
  409           }
  410           return out;
  411       }
  412   
  413       /**
  414        * Unwrap a previously wrapped key.
  415        *
  416        * @param wrappedKey the key to be unwrapped.
  417        *
  418        * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
  419        *
  420        * @param wrappedKeyType the type of the wrapped key.
  421        * This is one of <code>Cipher.SECRET_KEY</code>,
  422        * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
  423        *
  424        * @return the unwrapped key.
  425        *
  426        * @exception NoSuchAlgorithmException if no installed providers
  427        * can create keys of type <code>wrappedKeyType</code> for the
  428        * <code>wrappedKeyAlgorithm</code>.
  429        *
  430        * @exception InvalidKeyException if <code>wrappedKey</code> does not
  431        * represent a wrapped key of type <code>wrappedKeyType</code> for
  432        * the <code>wrappedKeyAlgorithm</code>.
  433        */
  434       protected Key engineUnwrap(byte[] wrappedKey,
  435                                  String wrappedKeyAlgorithm,
  436                                  int wrappedKeyType)
  437           throws InvalidKeyException, NoSuchAlgorithmException {
  438           int wrappedKeyLen = wrappedKey.length;
  439           // ensure the wrappedKey length is multiples of 8 bytes and non-zero
  440           if (wrappedKeyLen == 0) {
  441               throw new InvalidKeyException("The wrapped key is empty");
  442           }
  443           if (wrappedKeyLen % 8 != 0) {
  444               throw new InvalidKeyException
  445                   ("The wrapped key has invalid key length");
  446           }
  447           byte[] out = new byte[wrappedKeyLen - 8];
  448           byte[] buffer = new byte[blksize];
  449           if (wrappedKeyLen == 16) {
  450               cipher.decryptBlock(wrappedKey, 0, buffer, 0);
  451               for (int i = 0; i < IV.length; i++) {
  452                   if (IV[i] != buffer[i]) {
  453                       throw new InvalidKeyException("Integrity check failed");
  454                   }
  455               }
  456               System.arraycopy(buffer, IV.length, out, 0, out.length);
  457           } else {
  458               System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
  459               System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
  460               int N = out.length/8;
  461               for (int j = 5; j >= 0; j--) {
  462                   for (int i = N; i > 0; i--) {
  463                       int T = i + j*N;
  464                       System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);
  465                       for (int k = 1; T != 0; k++) {
  466                           byte v = (byte) T;
  467                           buffer[IV.length - k] ^= v;
  468                           T >>>= 8;
  469                       }
  470                       cipher.decryptBlock(buffer, 0, buffer, 0);
  471                       System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);
  472                   }
  473               }
  474               for (int i = 0; i < IV.length; i++) {
  475                   if (IV[i] != buffer[i]) {
  476                       throw new InvalidKeyException("Integrity check failed");
  477                   }
  478               }
  479           }
  480           return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
  481                                             wrappedKeyType);
  482       }
  483   }

Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]