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 CMS DESede 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.2
   38    * "CMS Triple DES Key Wrap".
   39    * Note: only <code>CBC</code> mode and <code>NoPadding</code> padding
   40    * scheme can be used for this algorithm.
   41    *
   42    * @author Valerie Peng
   43    *
   44    *
   45    * @see DESedeCipher
   46    */
   47   public final class DESedeWrapCipher extends CipherSpi {
   48   
   49       private static final byte[] IV2 = {
   50           (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, (byte) 0x2c,
   51           (byte) 0x79, (byte) 0xe8, (byte) 0x21, (byte) 0x05
   52       };
   53   
   54       /*
   55        * internal cipher object which does the real work.
   56        */
   57       private FeedbackCipher cipher;
   58   
   59       /*
   60        * iv for (re-)initializing the internal cipher object.
   61        */
   62       private byte[] iv = null;
   63   
   64       /*
   65        * key for re-initializing the internal cipher object.
   66        */
   67       private Key cipherKey = null;
   68   
   69       /*
   70        * are we encrypting or decrypting?
   71        */
   72       private boolean decrypting = false;
   73   
   74       /**
   75        * Creates an instance of CMS DESede KeyWrap cipher with default
   76        * mode, i.e. "CBC" and padding scheme, i.e. "NoPadding".
   77        */
   78       public DESedeWrapCipher() {
   79           cipher = new CipherBlockChaining(new DESedeCrypt());
   80       }
   81   
   82       /**
   83        * Sets the mode of this cipher. Only "CBC" mode is accepted for this
   84        * cipher.
   85        *
   86        * @param mode the cipher mode.
   87        *
   88        * @exception NoSuchAlgorithmException if the requested cipher mode
   89        * is not "CBC".
   90        */
   91       protected void engineSetMode(String mode)
   92           throws NoSuchAlgorithmException {
   93           if (!mode.equalsIgnoreCase("CBC")) {
   94               throw new NoSuchAlgorithmException(mode + " cannot be used");
   95           }
   96       }
   97   
   98       /**
   99        * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
  100        * is accepted for this cipher.
  101        *
  102        * @param padding the padding mechanism.
  103        *
  104        * @exception NoSuchPaddingException if the requested padding mechanism
  105        * is not "NoPadding".
  106        */
  107       protected void engineSetPadding(String padding)
  108           throws NoSuchPaddingException {
  109           if (!padding.equalsIgnoreCase("NoPadding")) {
  110               throw new NoSuchPaddingException(padding + " cannot be used");
  111           }
  112       }
  113   
  114       /**
  115        * Returns the block size (in bytes), i.e. 8 bytes.
  116        *
  117        * @return the block size (in bytes), i.e. 8 bytes.
  118        */
  119       protected int engineGetBlockSize() {
  120           return DESConstants.DES_BLOCK_SIZE;
  121       }
  122   
  123       /**
  124        * Returns the length in bytes that an output buffer would need to be
  125        * given the input length <code>inputLen</code> (in bytes).
  126        *
  127        * <p>The actual output length of the next <code>update</code> or
  128        * <code>doFinal</code> call may be smaller than the length returned
  129        * by this method.
  130        *
  131        * @param inputLen the input length (in bytes).
  132        *
  133        * @return the required output buffer size (in bytes).
  134        */
  135       protected int engineGetOutputSize(int inputLen) {
  136           // can only return an upper-limit if not initialized yet.
  137           int result = 0;
  138           if (decrypting) {
  139               result = inputLen - 16;
  140           } else {
  141               result = inputLen + 16;
  142           }
  143           return (result < 0? 0:result);
  144       }
  145   
  146       /**
  147        * Returns the initialization vector (IV) in a new buffer.
  148        *
  149        * @return the initialization vector, or null if the underlying
  150        * algorithm does not use an IV, or if the IV has not yet
  151        * been set.
  152        */
  153       protected byte[] engineGetIV() {
  154           return (iv == null? null:(byte[]) iv.clone());
  155       }
  156   
  157       /**
  158        * Initializes this cipher with a key and a source of randomness.
  159        *
  160        * <p>The cipher only supports the following two operation modes:<b>
  161        * Cipher.WRAP_MODE, and <b>
  162        * Cipher.UNWRAP_MODE.
  163        * <p>For modes other than the above two, UnsupportedOperationException
  164        * will be thrown.
  165        * <p>If this cipher requires an initialization vector (IV), it will get
  166        * it from <code>random</code>.
  167        *
  168        * @param opmode the operation mode of this cipher. Only
  169        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
  170        * @param key the secret key.
  171        * @param random the source of randomness.
  172        *
  173        * @exception InvalidKeyException if the given key is inappropriate
  174        * or if parameters are required but not supplied.
  175        */
  176       protected void engineInit(int opmode, Key key, SecureRandom random)
  177           throws InvalidKeyException {
  178           try {
  179               engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
  180           } catch (InvalidAlgorithmParameterException iape) {
  181               // should never happen
  182               InvalidKeyException ike =
  183                   new InvalidKeyException("Parameters required");
  184               ike.initCause(iape);
  185               throw ike;
  186           }
  187       }
  188   
  189       /**
  190        * Initializes this cipher with a key, a set of algorithm parameters,
  191        * and a source of randomness.
  192        *
  193        * <p>The cipher only supports the following two operation modes:<b>
  194        * Cipher.WRAP_MODE, and <b>
  195        * Cipher.UNWRAP_MODE.
  196        * <p>For modes other than the above two, UnsupportedOperationException
  197        * will be thrown.
  198        * <p>If this cipher requires an initialization vector (IV), it will get
  199        * it from <code>random</code>.
  200        *
  201        * @param opmode the operation mode of this cipher. Only
  202        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
  203        * @param key the secret key.
  204        * @param params the algorithm parameters.
  205        * @param random the source of randomness.
  206        *
  207        * @exception InvalidKeyException if the given key is inappropriate.
  208        * @exception InvalidAlgorithmParameterException if the given algorithm
  209        * parameters are inappropriate for this cipher.
  210        */
  211       protected void engineInit(int opmode, Key key,
  212                                 AlgorithmParameterSpec params,
  213                                 SecureRandom random)
  214           throws InvalidKeyException, InvalidAlgorithmParameterException {
  215           byte[] currIv = null;
  216           if (opmode == Cipher.WRAP_MODE) {
  217               decrypting = false;
  218               if (params == null) {
  219                   iv = new byte[8];
  220                   if (random == null) {
  221                       random = SunJCE.RANDOM;
  222                   }
  223                   random.nextBytes(iv);
  224               }
  225               else if (params instanceof IvParameterSpec) {
  226                   iv = ((IvParameterSpec) params).getIV();
  227               } else {
  228                   throw new InvalidAlgorithmParameterException
  229                       ("Wrong parameter type: IV expected");
  230               }
  231               currIv = iv;
  232           } else if (opmode == Cipher.UNWRAP_MODE) {
  233               if (params != null) {
  234                   throw new InvalidAlgorithmParameterException
  235                       ("No parameter accepted for unwrapping keys");
  236               }
  237               iv = null;
  238               decrypting = true;
  239               currIv = IV2;
  240           } else {
  241               throw new UnsupportedOperationException("This cipher can " +
  242                   "only be used for key wrapping and unwrapping");
  243           }
  244           cipher.init(decrypting, key.getAlgorithm(), key.getEncoded(),
  245                       currIv);
  246           cipherKey = key;
  247       }
  248   
  249       /**
  250        * Initializes this cipher with a key, a set of algorithm parameters,
  251        * and a source of randomness.
  252        *
  253        * <p>The cipher only supports the following two operation modes:<b>
  254        * Cipher.WRAP_MODE, and <b>
  255        * Cipher.UNWRAP_MODE.
  256        * <p>For modes other than the above two, UnsupportedOperationException
  257        * will be thrown.
  258        * <p>If this cipher requires an initialization vector (IV), it will get
  259        * it from <code>random</code>.
  260        *
  261        * @param opmode the operation mode of this cipher. Only
  262        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
  263        * @param key the secret key.
  264        * @param params the algorithm parameters.
  265        * @param random the source of randomness.
  266        *
  267        * @exception InvalidKeyException if the given key is inappropriate.
  268        * @exception InvalidAlgorithmParameterException if the given algorithm
  269        * parameters are inappropriate for this cipher.
  270        */
  271       protected void engineInit(int opmode, Key key,
  272                                 AlgorithmParameters params,
  273                                 SecureRandom random)
  274           throws InvalidKeyException, InvalidAlgorithmParameterException {
  275           IvParameterSpec ivSpec = null;
  276           if (params != null) {
  277               try {
  278                   DESedeParameters paramsEng = new DESedeParameters();
  279                   paramsEng.engineInit(params.getEncoded());
  280                   ivSpec = (IvParameterSpec)
  281                       paramsEng.engineGetParameterSpec(IvParameterSpec.class);
  282               } catch (Exception ex) {
  283                   InvalidAlgorithmParameterException iape =
  284                       new InvalidAlgorithmParameterException
  285                           ("Wrong parameter type: IV expected");
  286                   iape.initCause(ex);
  287                   throw iape;
  288               }
  289           }
  290           engineInit(opmode, key, ivSpec, random);
  291       }
  292   
  293       /**
  294        * This operation is not supported by this cipher.
  295        * Since it's impossible to initialize this cipher given the
  296        * current Cipher.engineInit(...) implementation,
  297        * IllegalStateException will always be thrown upon invocation.
  298        *
  299        * @param in the input buffer.
  300        * @param inOffset the offset in <code>in</code> where the input
  301        * starts.
  302        * @param inLen the input length.
  303        *
  304        * @return n/a.
  305        *
  306        * @exception IllegalStateException upon invocation of this method.
  307        */
  308       protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
  309           throw new IllegalStateException("Cipher has not been initialized");
  310       }
  311   
  312       /**
  313        * This operation is not supported by this cipher.
  314        * Since it's impossible to initialize this cipher given the
  315        * current Cipher.engineInit(...) implementation,
  316        * IllegalStateException will always be thrown upon invocation.
  317        *
  318        * @param in the input buffer.
  319        * @param inOffset the offset in <code>in</code> where the input
  320        * starts.
  321        * @param inLen the input length.
  322        * @param out the buffer for the result.
  323        * @param outOffset the offset in <code>out</code> where the result
  324        * is stored.
  325        *
  326        * @return n/a.
  327        *
  328        * @exception IllegalStateException upon invocation of this method.
  329        */
  330       protected int engineUpdate(byte[] in, int inOffset, int inLen,
  331                                  byte[] out, int outOffset)
  332           throws ShortBufferException {
  333           throw new IllegalStateException("Cipher has not been initialized");
  334       }
  335   
  336       /**
  337        * This operation is not supported by this cipher.
  338        * Since it's impossible to initialize this cipher given the
  339        * current Cipher.engineInit(...) implementation,
  340        * IllegalStateException will always be thrown upon invocation.
  341        *
  342        * @param in the input buffer.
  343        * @param inOffset the offset in <code>in</code> where the input
  344        * starts.
  345        * @param inLen the input length.
  346        *
  347        * @return the new buffer with the result.
  348        *
  349        * @exception IllegalStateException upon invocation of this method.
  350        */
  351       protected byte[] engineDoFinal(byte[] in, int inOffset, int inLen)
  352           throws IllegalBlockSizeException, BadPaddingException {
  353           throw new IllegalStateException("Cipher has not been initialized");
  354       }
  355   
  356       /**
  357        * This operation is not supported by this cipher.
  358        * Since it's impossible to initialize this cipher given the
  359        * current Cipher.engineInit(...) implementation,
  360        * IllegalStateException will always be thrown upon invocation.
  361        *
  362        * @param in the input buffer.
  363        * @param inOffset the offset in <code>in</code> where the input
  364        * starts.
  365        * @param inLen the input length.
  366        * @param out the buffer for the result.
  367        * @param outOffset the ofset in <code>out</code> where the result
  368        * is stored.
  369        *
  370        * @return the number of bytes stored in <code>out</code>.
  371        *
  372        * @exception IllegalStateException upon invocation of this method.
  373        */
  374       protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
  375                                   byte[] output, int outputOffset)
  376           throws IllegalBlockSizeException, ShortBufferException,
  377                  BadPaddingException {
  378           throw new IllegalStateException("Cipher has not been initialized");
  379       }
  380   
  381       /**
  382        * Returns the parameters used with this cipher.
  383        * Note that null maybe returned if this cipher does not use any
  384        * parameters or when it has not be set, e.g. initialized with
  385        * UNWRAP_MODE but wrapped key data has not been given.
  386        *
  387        * @return the parameters used with this cipher; can be null.
  388        */
  389       protected AlgorithmParameters engineGetParameters() {
  390           AlgorithmParameters params = null;
  391           if (iv != null) {
  392               String algo = cipherKey.getAlgorithm();
  393               try {
  394                   params = AlgorithmParameters.getInstance(algo, "SunJCE");
  395               } catch (NoSuchAlgorithmException nsae) {
  396                   // should never happen
  397                   throw new RuntimeException("Cannot find " + algo +
  398                       " AlgorithmParameters implementation in SunJCE provider");
  399               } catch (NoSuchProviderException nspe) {
  400                   // should never happen
  401                   throw new RuntimeException("Cannot find SunJCE provider");
  402               }
  403               try {
  404                   params.init(new IvParameterSpec(iv));
  405               } catch (InvalidParameterSpecException ipse) {
  406                   // should never happen
  407                   throw new RuntimeException("IvParameterSpec not supported");
  408               }
  409           }
  410           return params;
  411       }
  412   
  413       /**
  414        * Returns the key size of the given key object in number of bits.
  415        * This cipher always return the same key size as the DESede ciphers.
  416        *
  417        * @param key the key object.
  418        *
  419        * @return the "effective" key size of the given key object.
  420        *
  421        * @exception InvalidKeyException if <code>key</code> is invalid.
  422        */
  423       protected int engineGetKeySize(Key key) throws InvalidKeyException {
  424           byte[] encoded = key.getEncoded();
  425           if (encoded.length != 24) {
  426               throw new InvalidKeyException("Invalid key length: " +
  427                   encoded.length + " bytes");
  428           }
  429           // Return the effective key length
  430           return 112;
  431       }
  432   
  433       /**
  434        * Wrap a key.
  435        *
  436        * @param key the key to be wrapped.
  437        *
  438        * @return the wrapped key.
  439        *
  440        * @exception IllegalBlockSizeException if this cipher is a block
  441        * cipher, no padding has been requested, and the length of the
  442        * encoding of the key to be wrapped is not a
  443        * multiple of the block size.
  444        *
  445        * @exception InvalidKeyException if it is impossible or unsafe to
  446        * wrap the key with this cipher (e.g., a hardware protected key is
  447        * being passed to a software only cipher).
  448        */
  449       protected byte[] engineWrap(Key key)
  450           throws IllegalBlockSizeException, InvalidKeyException {
  451           byte[] keyVal = key.getEncoded();
  452           if ((keyVal == null) || (keyVal.length == 0)) {
  453               throw new InvalidKeyException("Cannot get an encoding of " +
  454                                             "the key to be wrapped");
  455           }
  456   
  457           byte[] cks = getChecksum(keyVal);
  458           byte[] out = new byte[iv.length + keyVal.length + cks.length];
  459   
  460           System.arraycopy(keyVal, 0, out, iv.length, keyVal.length);
  461           System.arraycopy(cks, 0, out, iv.length+keyVal.length, cks.length);
  462           cipher.encrypt(out, iv.length, keyVal.length+cks.length,
  463                          out, iv.length);
  464   
  465           System.arraycopy(iv, 0, out, 0, iv.length);
  466           // reverse the array content
  467           for (int i = 0; i < out.length/2; i++) {
  468               byte temp = out[i];
  469               out[i] = out[out.length-1-i];
  470               out[out.length-1-i] = temp;
  471           }
  472           try {
  473               cipher.init(false, cipherKey.getAlgorithm(),
  474                           cipherKey.getEncoded(), IV2);
  475           } catch (InvalidKeyException ike) {
  476               // should never happen
  477               throw new RuntimeException("Internal cipher key is corrupted");
  478           }
  479           cipher.encrypt(out, 0, out.length, out, 0);
  480   
  481           // restore cipher state to prior to this call
  482           try {
  483               cipher.init(decrypting, cipherKey.getAlgorithm(),
  484                           cipherKey.getEncoded(), iv);
  485           } catch (InvalidKeyException ike) {
  486               // should never happen
  487               throw new RuntimeException("Internal cipher key is corrupted");
  488           }
  489           return out;
  490       }
  491   
  492       /**
  493        * Unwrap a previously wrapped key.
  494        *
  495        * @param wrappedKey the key to be unwrapped.
  496        *
  497        * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
  498        *
  499        * @param wrappedKeyType the type of the wrapped key.
  500        * This is one of <code>Cipher.SECRET_KEY</code>,
  501        * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
  502        *
  503        * @return the unwrapped key.
  504        *
  505        * @exception NoSuchAlgorithmException if no installed providers
  506        * can create keys of type <code>wrappedKeyType</code> for the
  507        * <code>wrappedKeyAlgorithm</code>.
  508        *
  509        * @exception InvalidKeyException if <code>wrappedKey</code> does not
  510        * represent a wrapped key of type <code>wrappedKeyType</code> for
  511        * the <code>wrappedKeyAlgorithm</code>.
  512        */
  513       protected Key engineUnwrap(byte[] wrappedKey,
  514                                  String wrappedKeyAlgorithm,
  515                                  int wrappedKeyType)
  516           throws InvalidKeyException, NoSuchAlgorithmException {
  517           if (wrappedKey.length == 0) {
  518               throw new InvalidKeyException("The wrapped key is empty");
  519           }
  520           byte[] buffer = new byte[wrappedKey.length];
  521           cipher.decrypt(wrappedKey, 0, wrappedKey.length, buffer, 0);
  522   
  523           // reverse array content
  524           for (int i = 0; i < buffer.length/2; i++) {
  525               byte temp = buffer[i];
  526               buffer[i] = buffer[buffer.length-1-i];
  527               buffer[buffer.length-1-i] = temp;
  528           }
  529           iv = new byte[IV2.length];
  530           System.arraycopy(buffer, 0, iv, 0, iv.length);
  531           cipher.init(true, cipherKey.getAlgorithm(), cipherKey.getEncoded(),
  532                       iv);
  533           cipher.decrypt(buffer, iv.length, buffer.length-iv.length,
  534                          buffer, iv.length);
  535           int origLen = buffer.length - iv.length - 8;
  536           byte[] cks = getChecksum(buffer, iv.length, origLen);
  537           int offset = iv.length + origLen;
  538           for (int i = 0; i < cks.length; i++) {
  539               if (buffer[offset + i] != cks[i]) {
  540                   throw new InvalidKeyException("Checksum comparison failed");
  541               }
  542           }
  543           // restore cipher state to prior to this call
  544           cipher.init(decrypting, cipherKey.getAlgorithm(),
  545                       cipherKey.getEncoded(), IV2);
  546           byte[] out = new byte[origLen];
  547           System.arraycopy(buffer, iv.length, out, 0, out.length);
  548           return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
  549                                             wrappedKeyType);
  550       }
  551   
  552       private static final byte[] getChecksum(byte[] in) {
  553           return getChecksum(in, 0, in.length);
  554       }
  555       private static final byte[] getChecksum(byte[] in, int offset, int len) {
  556           MessageDigest md = null;
  557           try {
  558               md = MessageDigest.getInstance("SHA1");
  559           } catch (NoSuchAlgorithmException nsae) {
  560               throw new RuntimeException("SHA1 message digest not available");
  561           }
  562           md.update(in, offset, len);
  563           byte[] cks = new byte[8];
  564           System.arraycopy(md.digest(), 0, cks, 0, cks.length);
  565           return cks;
  566       }
  567   }

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