Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]
    1   /*
    2    * Copyright (c) 2002, 2007, 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.io.UnsupportedEncodingException;
   29   import java.security;
   30   import java.security.spec;
   31   import javax.crypto;
   32   import javax.crypto.spec;
   33   
   34   /**
   35    * This class represents password-based encryption as defined by the PKCS #5
   36    * standard.
   37    *
   38    * @author Jan Luehe
   39    *
   40    *
   41    * @see javax.crypto.Cipher
   42    */
   43   final class PBECipherCore {
   44   
   45       // the encapsulated DES cipher
   46       private CipherCore cipher;
   47       private MessageDigest md;
   48       private int blkSize;
   49       private String algo = null;
   50       private byte[] salt = null;
   51       private int iCount = 10;
   52   
   53       /**
   54        * Creates an instance of PBE Cipher using the specified CipherSpi
   55        * instance.
   56        *
   57        */
   58       PBECipherCore(String cipherAlg) throws NoSuchAlgorithmException,
   59           NoSuchPaddingException {
   60           algo = cipherAlg;
   61           if (algo.equals("DES")) {
   62               cipher = new CipherCore(new DESCrypt(),
   63                                       DESConstants.DES_BLOCK_SIZE);
   64           } else if (algo.equals("DESede")) {
   65   
   66               cipher = new CipherCore(new DESedeCrypt(),
   67                                       DESConstants.DES_BLOCK_SIZE);
   68           } else {
   69               throw new NoSuchAlgorithmException("No Cipher implementation " +
   70                                                  "for PBEWithMD5And" + algo);
   71           }
   72           cipher.setMode("CBC");
   73           cipher.setPadding("PKCS5Padding");
   74           // get instance of MD5
   75           md = MessageDigest.getInstance("MD5");
   76       }
   77   
   78       /**
   79        * Sets the mode of this cipher. This algorithm can only be run in CBC
   80        * mode.
   81        *
   82        * @param mode the cipher mode
   83        *
   84        * @exception NoSuchAlgorithmException if the requested cipher mode is
   85        * invalid
   86        */
   87       void setMode(String mode) throws NoSuchAlgorithmException {
   88           cipher.setMode(mode);
   89       }
   90   
   91        /**
   92        * Sets the padding mechanism of this cipher. This algorithm only uses
   93        * PKCS #5 padding.
   94        *
   95        * @param padding the padding mechanism
   96        *
   97        * @exception NoSuchPaddingException if the requested padding mechanism
   98        * is invalid
   99        */
  100       void setPadding(String paddingScheme) throws NoSuchPaddingException {
  101           cipher.setPadding(paddingScheme);
  102       }
  103   
  104       /**
  105        * Returns the block size (in bytes).
  106        *
  107        * @return the block size (in bytes)
  108        */
  109       int getBlockSize() {
  110           return DESConstants.DES_BLOCK_SIZE;
  111       }
  112   
  113       /**
  114        * Returns the length in bytes that an output buffer would need to be in
  115        * order to hold the result of the next <code>update</code> or
  116        * <code>doFinal</code> operation, given the input length
  117        * <code>inputLen</code> (in bytes).
  118        *
  119        * <p>This call takes into account any unprocessed (buffered) data from a
  120        * previous <code>update</code> call, and padding.
  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 by
  124        * this method.
  125        *
  126        * @param inputLen the input length (in bytes)
  127        *
  128        * @return the required output buffer size (in bytes)
  129        *
  130        */
  131       int getOutputSize(int inputLen) {
  132           return cipher.getOutputSize(inputLen);
  133       }
  134   
  135       /**
  136        * Returns the initialization vector (IV) in a new buffer.
  137        *
  138        * <p> This is useful in the case where a random IV has been created
  139        * (see <a href = "#init">init</a>),
  140        * or in the context of password-based encryption or
  141        * decryption, where the IV is derived from a user-supplied password.
  142        *
  143        * @return the initialization vector in a new buffer, or null if the
  144        * underlying algorithm does not use an IV, or if the IV has not yet
  145        * been set.
  146        */
  147       byte[] getIV() {
  148           return cipher.getIV();
  149       }
  150   
  151       /**
  152        * Returns the parameters used with this cipher.
  153        *
  154        * <p>The returned parameters may be the same that were used to initialize
  155        * this cipher, or may contain the default set of parameters or a set of
  156        * randomly generated parameters used by the underlying cipher
  157        * implementation (provided that the underlying cipher implementation
  158        * uses a default set of parameters or creates new parameters if it needs
  159        * parameters but was not initialized with any).
  160        *
  161        * @return the parameters used with this cipher, or null if this cipher
  162        * does not use any parameters.
  163        */
  164       AlgorithmParameters getParameters() {
  165           AlgorithmParameters params = null;
  166           if (salt == null) {
  167               salt = new byte[8];
  168               SunJCE.RANDOM.nextBytes(salt);
  169           }
  170           PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
  171           try {
  172               params = AlgorithmParameters.getInstance("PBEWithMD5And" +
  173                   (algo.equalsIgnoreCase("DES")? "DES":"TripleDES"), "SunJCE");
  174           } catch (NoSuchAlgorithmException nsae) {
  175               // should never happen
  176               throw new RuntimeException("SunJCE called, but not configured");
  177           } catch (NoSuchProviderException nspe) {
  178               // should never happen
  179               throw new RuntimeException("SunJCE called, but not configured");
  180           }
  181           try {
  182               params.init(pbeSpec);
  183           } catch (InvalidParameterSpecException ipse) {
  184               // should never happen
  185               throw new RuntimeException("PBEParameterSpec not supported");
  186           }
  187           return params;
  188       }
  189   
  190       /**
  191        * Initializes this cipher with a key, a set of
  192        * algorithm parameters, and a source of randomness.
  193        * The cipher is initialized for one of the following four operations:
  194        * encryption, decryption, key wrapping or key unwrapping, depending on
  195        * the value of <code>opmode</code>.
  196        *
  197        * <p>If this cipher (including its underlying feedback or padding scheme)
  198        * requires any random bytes, it will get them from <code>random</code>.
  199        *
  200        * @param opmode the operation mode of this cipher (this is one of
  201        * the following:
  202        * <code>ENCRYPT_MODE</code>, <code>DECRYPT_MODE</code>),
  203        * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>)
  204        * @param key the encryption key
  205        * @param params the algorithm parameters
  206        * @param random the source of randomness
  207        *
  208        * @exception InvalidKeyException if the given key is inappropriate for
  209        * initializing this cipher
  210        * @exception InvalidAlgorithmParameterException if the given algorithm
  211        * parameters are inappropriate for this cipher
  212        */
  213       void init(int opmode, Key key, AlgorithmParameterSpec params,
  214                 SecureRandom random)
  215           throws InvalidKeyException, InvalidAlgorithmParameterException {
  216           if (((opmode == Cipher.DECRYPT_MODE) ||
  217                (opmode == Cipher.UNWRAP_MODE)) && (params == null)) {
  218               throw new InvalidAlgorithmParameterException("Parameters "
  219                                                            + "missing");
  220           }
  221           if ((key == null) ||
  222               (key.getEncoded() == null) ||
  223               !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
  224               throw new InvalidKeyException("Missing password");
  225           }
  226   
  227           if (params == null) {
  228               // create random salt and use default iteration count
  229               salt = new byte[8];
  230               random.nextBytes(salt);
  231           } else {
  232               if (!(params instanceof PBEParameterSpec)) {
  233                   throw new InvalidAlgorithmParameterException
  234                       ("Wrong parameter type: PBE expected");
  235               }
  236               salt = ((PBEParameterSpec) params).getSalt();
  237               // salt must be 8 bytes long (by definition)
  238               if (salt.length != 8) {
  239                   throw new InvalidAlgorithmParameterException
  240                       ("Salt must be 8 bytes long");
  241               }
  242               iCount = ((PBEParameterSpec) params).getIterationCount();
  243               if (iCount <= 0) {
  244                   throw new InvalidAlgorithmParameterException
  245                       ("IterationCount must be a positive number");
  246               }
  247           }
  248   
  249           byte[] derivedKey = deriveCipherKey(key);
  250           // use all but the last 8 bytes as the key value
  251           SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, 0,
  252                                                       derivedKey.length-8, algo);
  253           // use the last 8 bytes as the IV
  254           IvParameterSpec ivSpec = new IvParameterSpec(derivedKey,
  255                                                        derivedKey.length-8,
  256                                                        8);
  257           // initialize the underlying cipher
  258           cipher.init(opmode, cipherKey, ivSpec, random);
  259       }
  260   
  261       private byte[] deriveCipherKey(Key key) {
  262   
  263           byte[] result = null;
  264           byte[] passwdBytes = key.getEncoded();
  265   
  266           if (algo.equals("DES")) {
  267               // P || S (password concatenated with salt)
  268               byte[] concat = new byte[passwdBytes.length + salt.length];
  269               System.arraycopy(passwdBytes, 0, concat, 0, passwdBytes.length);
  270               java.util.Arrays.fill(passwdBytes, (byte)0x00);
  271               System.arraycopy(salt, 0, concat, passwdBytes.length, salt.length);
  272   
  273               // digest P || S with c iterations
  274               byte[] toBeHashed = concat;
  275               for (int i = 0; i < iCount; i++) {
  276                   md.update(toBeHashed);
  277                   toBeHashed = md.digest(); // this resets the digest
  278               }
  279               java.util.Arrays.fill(concat, (byte)0x00);
  280               result = toBeHashed;
  281           } else if (algo.equals("DESede")) {
  282               // if the 2 salt halves are the same, invert one of them
  283               int i;
  284               for (i=0; i<4; i++) {
  285                   if (salt[i] != salt[i+4])
  286                       break;
  287               }
  288               if (i==4) { // same, invert 1st half
  289                   for (i=0; i<2; i++) {
  290                       byte tmp = salt[i];
  291                       salt[i] = salt[3-i];
  292                       salt[3-1] = tmp;
  293                   }
  294               }
  295   
  296               // Now digest each half (concatenated with password). For each
  297               // half, go through the loop as many times as specified by the
  298               // iteration count parameter (inner for loop).
  299               // Concatenate the output from each digest round with the
  300               // password, and use the result as the input to the next digest
  301               // operation.
  302               byte[] kBytes = null;
  303               IvParameterSpec iv = null;
  304               byte[] toBeHashed = null;
  305               result = new byte[DESedeKeySpec.DES_EDE_KEY_LEN +
  306                                 DESConstants.DES_BLOCK_SIZE];
  307               for (i = 0; i < 2; i++) {
  308                   toBeHashed = new byte[salt.length/2];
  309                   System.arraycopy(salt, i*(salt.length/2), toBeHashed, 0,
  310                                    toBeHashed.length);
  311                   for (int j=0; j < iCount; j++) {
  312                       md.update(toBeHashed);
  313                       md.update(passwdBytes);
  314                       toBeHashed = md.digest(); // this resets the digest
  315                   }
  316                   System.arraycopy(toBeHashed, 0, result, i*16,
  317                                    toBeHashed.length);
  318               }
  319           }
  320           return result;
  321       }
  322   
  323       void init(int opmode, Key key, AlgorithmParameters params,
  324                 SecureRandom random)
  325           throws InvalidKeyException, InvalidAlgorithmParameterException {
  326           PBEParameterSpec pbeSpec = null;
  327           if (params != null) {
  328               try {
  329                   pbeSpec = (PBEParameterSpec) params.getParameterSpec
  330                       (PBEParameterSpec.class);
  331               } catch (InvalidParameterSpecException ipse) {
  332                   throw new InvalidAlgorithmParameterException("Wrong parameter "
  333                                                                + "type: PBE "
  334                                                                + "expected");
  335               }
  336           }
  337           init(opmode, key, pbeSpec, random);
  338       }
  339   
  340       /**
  341        * Continues a multiple-part encryption or decryption operation
  342        * (depending on how this cipher was initialized), processing another data
  343        * part.
  344        *
  345        * <p>The first <code>inputLen</code> bytes in the <code>input</code>
  346        * buffer, starting at <code>inputOffset</code>, are processed, and the
  347        * result is stored in a new buffer.
  348        *
  349        * @param input the input buffer
  350        * @param inputOffset the offset in <code>input</code> where the input
  351        * starts
  352        * @param inputLen the input length
  353        *
  354        * @return the new buffer with the result
  355        *
  356        */
  357       byte[] update(byte[] input, int inputOffset, int inputLen) {
  358           return cipher.update(input, inputOffset, inputLen);
  359       }
  360   
  361       /**
  362        * Continues a multiple-part encryption or decryption operation
  363        * (depending on how this cipher was initialized), processing another data
  364        * part.
  365        *
  366        * <p>The first <code>inputLen</code> bytes in the <code>input</code>
  367        * buffer, starting at <code>inputOffset</code>, are processed, and the
  368        * result is stored in the <code>output</code> buffer, starting at
  369        * <code>outputOffset</code>.
  370        *
  371        * @param input the input buffer
  372        * @param inputOffset the offset in <code>input</code> where the input
  373        * starts
  374        * @param inputLen the input length
  375        * @param output the buffer for the result
  376        * @param outputOffset the offset in <code>output</code> where the result
  377        * is stored
  378        *
  379        * @return the number of bytes stored in <code>output</code>
  380        *
  381        * @exception ShortBufferException if the given output buffer is too small
  382        * to hold the result
  383        */
  384       int update(byte[] input, int inputOffset, int inputLen,
  385                  byte[] output, int outputOffset)
  386           throws ShortBufferException {
  387           return cipher.update(input, inputOffset, inputLen,
  388                                output, outputOffset);
  389       }
  390   
  391       /**
  392        * Encrypts or decrypts data in a single-part operation,
  393        * or finishes a multiple-part operation.
  394        * The data is encrypted or decrypted, depending on how this cipher was
  395        * initialized.
  396        *
  397        * <p>The first <code>inputLen</code> bytes in the <code>input</code>
  398        * buffer, starting at <code>inputOffset</code>, and any input bytes that
  399        * may have been buffered during a previous <code>update</code> operation,
  400        * are processed, with padding (if requested) being applied.
  401        * The result is stored in a new buffer.
  402        *
  403        * <p>The cipher is reset to its initial state (uninitialized) after this
  404        * call.
  405        *
  406        * @param input the input buffer
  407        * @param inputOffset the offset in <code>input</code> where the input
  408        * starts
  409        * @param inputLen the input length
  410        *
  411        * @return the new buffer with the result
  412        *
  413        * @exception IllegalBlockSizeException if this cipher is a block cipher,
  414        * no padding has been requested (only in encryption mode), and the total
  415        * input length of the data processed by this cipher is not a multiple of
  416        * block size
  417        * @exception BadPaddingException if decrypting and padding is choosen,
  418        * but the last input data does not have proper padding bytes.
  419        */
  420       byte[] doFinal(byte[] input, int inputOffset, int inputLen)
  421           throws IllegalBlockSizeException, BadPaddingException {
  422           return cipher.doFinal(input, inputOffset, inputLen);
  423       }
  424   
  425       /**
  426        * Encrypts or decrypts data in a single-part operation,
  427        * or finishes a multiple-part operation.
  428        * The data is encrypted or decrypted, depending on how this cipher was
  429        * initialized.
  430        *
  431        * <p>The first <code>inputLen</code> bytes in the <code>input</code>
  432        * buffer, starting at <code>inputOffset</code>, and any input bytes that
  433        * may have been buffered during a previous <code>update</code> operation,
  434        * are processed, with padding (if requested) being applied.
  435        * The result is stored in the <code>output</code> buffer, starting at
  436        * <code>outputOffset</code>.
  437        *
  438        * <p>The cipher is reset to its initial state (uninitialized) after this
  439        * call.
  440        *
  441        * @param input the input buffer
  442        * @param inputOffset the offset in <code>input</code> where the input
  443        * starts
  444        * @param inputLen the input length
  445        * @param output the buffer for the result
  446        * @param outputOffset the offset in <code>output</code> where the result
  447        * is stored
  448        *
  449        * @return the number of bytes stored in <code>output</code>
  450        *
  451        * @exception IllegalBlockSizeException if this cipher is a block cipher,
  452        * no padding has been requested (only in encryption mode), and the total
  453        * input length of the data processed by this cipher is not a multiple of
  454        * block size
  455        * @exception ShortBufferException if the given output buffer is too small
  456        * to hold the result
  457        * @exception BadPaddingException if decrypting and padding is choosen,
  458        * but the last input data does not have proper padding bytes.
  459        */
  460       int doFinal(byte[] input, int inputOffset, int inputLen,
  461                   byte[] output, int outputOffset)
  462           throws ShortBufferException, IllegalBlockSizeException,
  463                  BadPaddingException {
  464           return cipher.doFinal(input, inputOffset, inputLen,
  465                                       output, outputOffset);
  466       }
  467   
  468       /**
  469        * Wrap a key.
  470        *
  471        * @param key the key to be wrapped.
  472        *
  473        * @return the wrapped key.
  474        *
  475        * @exception IllegalBlockSizeException if this cipher is a block
  476        * cipher, no padding has been requested, and the length of the
  477        * encoding of the key to be wrapped is not a
  478        * multiple of the block size.
  479        *
  480        * @exception InvalidKeyException if it is impossible or unsafe to
  481        * wrap the key with this cipher (e.g., a hardware protected key is
  482        * being passed to a software only cipher).
  483        */
  484       byte[] wrap(Key key)
  485           throws IllegalBlockSizeException, InvalidKeyException {
  486           byte[] result = null;
  487   
  488           try {
  489               byte[] encodedKey = key.getEncoded();
  490               if ((encodedKey == null) || (encodedKey.length == 0)) {
  491                   throw new InvalidKeyException("Cannot get an encoding of " +
  492                                                 "the key to be wrapped");
  493               }
  494   
  495               result = doFinal(encodedKey, 0, encodedKey.length);
  496           } catch (BadPaddingException e) {
  497               // Should never happen
  498           }
  499   
  500           return result;
  501       }
  502   
  503       /**
  504        * Unwrap a previously wrapped key.
  505        *
  506        * @param wrappedKey the key to be unwrapped.
  507        *
  508        * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
  509        *
  510        * @param wrappedKeyType the type of the wrapped key.
  511        * This is one of <code>Cipher.SECRET_KEY</code>,
  512        * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
  513        *
  514        * @return the unwrapped key.
  515        *
  516        * @exception NoSuchAlgorithmException if no installed providers
  517        * can create keys of type <code>wrappedKeyType</code> for the
  518        * <code>wrappedKeyAlgorithm</code>.
  519        *
  520        * @exception InvalidKeyException if <code>wrappedKey</code> does not
  521        * represent a wrapped key of type <code>wrappedKeyType</code> for
  522        * the <code>wrappedKeyAlgorithm</code>.
  523        */
  524       Key unwrap(byte[] wrappedKey,
  525                  String wrappedKeyAlgorithm,
  526                  int wrappedKeyType)
  527           throws InvalidKeyException, NoSuchAlgorithmException {
  528           byte[] encodedKey;
  529           try {
  530               encodedKey = doFinal(wrappedKey, 0, wrappedKey.length);
  531           } catch (BadPaddingException ePadding) {
  532               throw new InvalidKeyException("The wrapped key is not padded " +
  533                                             "correctly");
  534           } catch (IllegalBlockSizeException eBlockSize) {
  535               throw new InvalidKeyException("The wrapped key does not have " +
  536                                             "the correct length");
  537           }
  538           return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm,
  539                                             wrappedKeyType);
  540       }
  541   }

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