Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]
    1   /*
    2    * Copyright (c) 2003, 2010, 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.math.BigInteger;
   30   import java.security;
   31   import java.security.spec;
   32   import java.util.Arrays;
   33   import javax.crypto;
   34   import javax.crypto.spec;
   35   
   36   /**
   37    * This class implements password-base encryption algorithm with
   38    * SHA1 digest and the following Ciphers in CBC mode
   39    * - DESede cipher and
   40    * - RC2 Cipher with 40-bit effective key length
   41    * as defined by PKCS #12 version 1.0 standard.
   42    *
   43    * @author Valerie Peng
   44    * @see javax.crypto.CipherSpi
   45    */
   46   final class PKCS12PBECipherCore {
   47       private CipherCore cipher;
   48       private int blockSize;
   49       private int keySize;
   50       private String algo = null;
   51       private byte[] salt = null;
   52       private int iCount = 0;
   53   
   54       private static final int DEFAULT_SALT_LENGTH = 20;
   55       private static final int DEFAULT_COUNT = 1024;
   56   
   57       static final int CIPHER_KEY = 1;
   58       static final int CIPHER_IV = 2;
   59       static final int MAC_KEY = 3;
   60   
   61       static byte[] derive(char[] chars, byte[] salt,
   62                            int ic, int n, int type) {
   63           // Add in trailing NULL terminator.
   64           int length = chars.length*2;
   65           if (length != 0) {
   66               length += 2;
   67           }
   68           byte[] passwd = new byte[length];
   69           for (int i = 0, j = 0; i < chars.length; i++, j+=2) {
   70               passwd[j] = (byte) ((chars[i] >>> 8) & 0xFF);
   71               passwd[j+1] = (byte) (chars[i] & 0xFF);
   72           }
   73           int v = 512 / 8;
   74           int u = 160 / 8;
   75           int c = roundup(n, u) / u;
   76           byte[] D = new byte[v];
   77           int s = roundup(salt.length, v);
   78           int p = roundup(passwd.length, v);
   79           byte[] I = new byte[s + p];
   80           byte[] key = new byte[n];
   81   
   82           Arrays.fill(D, (byte)type);
   83           concat(salt, I, 0, s);
   84           concat(passwd, I, s, p);
   85   
   86           try {
   87               MessageDigest sha = MessageDigest.getInstance("SHA1");
   88               byte[] Ai;
   89               byte[] B = new byte[v];
   90               byte[] tmp = new byte[v];
   91   
   92               int i = 0;
   93               for (; ; i++, n -= u) {
   94                   sha.update(D);
   95                   sha.update(I);
   96                   Ai = sha.digest();
   97                   for (int r = 1; r < ic; r++)
   98                       Ai = sha.digest(Ai);
   99                   System.arraycopy(Ai, 0, key, u * i, Math.min(n, u));
  100                   if (i + 1 == c)
  101                       break;
  102                   concat(Ai, B, 0, B.length);
  103                   BigInteger B1;
  104                   B1 = new BigInteger(1, B).add(BigInteger.ONE);
  105   
  106                   for (int j = 0; j < I.length; j += v) {
  107                       BigInteger Ij;
  108                       int trunc;
  109   
  110                       if (tmp.length != v)
  111                           tmp = new byte[v];
  112                       System.arraycopy(I, j, tmp, 0, v);
  113                       Ij = new BigInteger(1, tmp);
  114                       Ij = Ij.add(B1);
  115                       tmp = Ij.toByteArray();
  116                       trunc = tmp.length - v;
  117                       if (trunc >= 0) {
  118                           System.arraycopy(tmp, trunc, I, j, v);
  119                       } else if (trunc < 0) {
  120                           Arrays.fill(I, j, j + (-trunc), (byte)0);
  121                           System.arraycopy(tmp, 0, I, j + (-trunc), tmp.length);
  122                       }
  123                   }
  124               }
  125           } catch (Exception e) {
  126               throw new RuntimeException("internal error: " + e);
  127           }
  128           return key;
  129       }
  130   
  131       private static int roundup(int x, int y) {
  132           return ((x + (y - 1)) / y) * y;
  133       }
  134   
  135       private static void concat(byte[] src, byte[] dst, int start, int len) {
  136           int loop = len / src.length;
  137           int off, i;
  138           for (i = 0, off = 0; i < loop; i++, off += src.length)
  139               System.arraycopy(src, 0, dst, off + start, src.length);
  140           System.arraycopy(src, 0, dst, off + start, len - off);
  141       }
  142   
  143       PKCS12PBECipherCore(String symmCipherAlg, int defKeySize)
  144           throws NoSuchAlgorithmException {
  145           algo = symmCipherAlg;
  146           SymmetricCipher symmCipher = null;
  147           if (algo.equals("DESede")) {
  148               symmCipher = new DESedeCrypt();
  149           } else if (algo.equals("RC2")) {
  150               symmCipher = new RC2Crypt();
  151           } else {
  152               throw new NoSuchAlgorithmException("No Cipher implementation " +
  153                          "for PBEWithSHA1And" + algo);
  154           }
  155           blockSize = symmCipher.getBlockSize();
  156           cipher = new CipherCore(symmCipher, blockSize);
  157           cipher.setMode("CBC");
  158           try {
  159               cipher.setPadding("PKCS5Padding");
  160           } catch (NoSuchPaddingException nspe) {
  161               // should not happen
  162           }
  163           keySize = defKeySize;
  164       }
  165   
  166       void implSetMode(String mode) throws NoSuchAlgorithmException {
  167           if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
  168               throw new NoSuchAlgorithmException("Invalid cipher mode: "
  169                                                  + mode);
  170           }
  171       }
  172   
  173       void implSetPadding(String padding) throws NoSuchPaddingException {
  174           if ((padding != null) &&
  175               (!padding.equalsIgnoreCase("PKCS5Padding"))) {
  176               throw new NoSuchPaddingException("Invalid padding scheme: " +
  177                                                padding);
  178           }
  179       }
  180   
  181       int implGetBlockSize() {
  182           return blockSize;
  183       }
  184   
  185       int implGetOutputSize(int inLen) {
  186           return cipher.getOutputSize(inLen);
  187       }
  188   
  189       byte[] implGetIV() {
  190           return cipher.getIV();
  191       }
  192   
  193       AlgorithmParameters implGetParameters() {
  194           AlgorithmParameters params = null;
  195           if (salt == null) {
  196               // Cipher is not initialized with parameters;
  197               // follow the recommendation in PKCS12 v1.0
  198               // section B.4 to generate salt and iCount.
  199               salt = new byte[DEFAULT_SALT_LENGTH];
  200               SunJCE.RANDOM.nextBytes(salt);
  201               iCount = DEFAULT_COUNT;
  202           }
  203           PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount);
  204           try {
  205               params = AlgorithmParameters.getInstance("PBEWithSHA1And" +
  206                   (algo.equalsIgnoreCase("RC2")?"RC2_40":algo), "SunJCE");
  207           } catch (GeneralSecurityException gse) {
  208               // should never happen
  209               throw new RuntimeException(
  210                   "SunJCE provider is not configured properly");
  211           }
  212           try {
  213               params.init(pbeSpec);
  214           } catch (InvalidParameterSpecException ipse) {
  215               // should never happen
  216               throw new RuntimeException("PBEParameterSpec not supported");
  217           }
  218           return params;
  219       }
  220   
  221       void implInit(int opmode, Key key, AlgorithmParameterSpec params,
  222                     SecureRandom random) throws InvalidKeyException,
  223           InvalidAlgorithmParameterException {
  224           char[] passwdChars = null;
  225           salt = null;
  226           iCount = 0;
  227           if (key instanceof javax.crypto.interfaces.PBEKey) {
  228               javax.crypto.interfaces.PBEKey pbeKey =
  229                   (javax.crypto.interfaces.PBEKey) key;
  230               passwdChars = pbeKey.getPassword();
  231               salt = pbeKey.getSalt(); // maybe null if unspecified
  232               iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
  233           } else if (key instanceof SecretKey) {
  234               byte[] passwdBytes = key.getEncoded();
  235               if ((passwdBytes == null) ||
  236                   !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
  237                   throw new InvalidKeyException("Missing password");
  238               }
  239               passwdChars = new char[passwdBytes.length];
  240               for (int i=0; i<passwdChars.length; i++) {
  241                   passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
  242               }
  243           } else {
  244               throw new InvalidKeyException("SecretKey of PBE type required");
  245           }
  246   
  247           if (((opmode == Cipher.DECRYPT_MODE) ||
  248                (opmode == Cipher.UNWRAP_MODE)) &&
  249               ((params == null) && ((salt == null) || (iCount == 0)))) {
  250               throw new InvalidAlgorithmParameterException
  251                   ("Parameters missing");
  252           }
  253   
  254           if (params == null) {
  255               // generate default for salt and iteration count if necessary
  256               if (salt == null) {
  257                   salt = new byte[DEFAULT_SALT_LENGTH];
  258                   if (random != null) {
  259                       random.nextBytes(salt);
  260                   } else {
  261                       SunJCE.RANDOM.nextBytes(salt);
  262                   }
  263               }
  264               if (iCount == 0) iCount = DEFAULT_COUNT;
  265           } else if (!(params instanceof PBEParameterSpec)) {
  266               throw new InvalidAlgorithmParameterException
  267                   ("PBEParameterSpec type required");
  268           } else {
  269               PBEParameterSpec pbeParams = (PBEParameterSpec) params;
  270               // make sure the parameter values are consistent
  271               if (salt != null) {
  272                   if (!Arrays.equals(salt, pbeParams.getSalt())) {
  273                       throw new InvalidAlgorithmParameterException
  274                           ("Inconsistent value of salt between key and params");
  275                   }
  276               } else {
  277                   salt = pbeParams.getSalt();
  278               }
  279               if (iCount != 0) {
  280                   if (iCount != pbeParams.getIterationCount()) {
  281                       throw new InvalidAlgorithmParameterException
  282                           ("Different iteration count between key and params");
  283                   }
  284               } else {
  285                   iCount = pbeParams.getIterationCount();
  286               }
  287           }
  288           // salt is recommended to be ideally as long as the output
  289           // of the hash function. However, it may be too strict to
  290           // force this; so instead, we'll just require the minimum
  291           // salt length to be 8-byte which is what PKCS#5 recommends
  292           // and openssl does.
  293           if (salt.length < 8) {
  294               throw new InvalidAlgorithmParameterException
  295                   ("Salt must be at least 8 bytes long");
  296           }
  297           if (iCount <= 0) {
  298               throw new InvalidAlgorithmParameterException
  299                   ("IterationCount must be a positive number");
  300           }
  301           byte[] derivedKey = derive(passwdChars, salt, iCount,
  302                                      keySize, CIPHER_KEY);
  303           SecretKey cipherKey = new SecretKeySpec(derivedKey, algo);
  304           byte[] derivedIv = derive(passwdChars, salt, iCount, 8,
  305                                     CIPHER_IV);
  306           IvParameterSpec ivSpec = new IvParameterSpec(derivedIv, 0, 8);
  307   
  308           // initialize the underlying cipher
  309           cipher.init(opmode, cipherKey, ivSpec, random);
  310       }
  311   
  312       void implInit(int opmode, Key key, AlgorithmParameters params,
  313                     SecureRandom random)
  314           throws InvalidKeyException, InvalidAlgorithmParameterException {
  315           AlgorithmParameterSpec paramSpec = null;
  316           if (params != null) {
  317               try {
  318                   paramSpec = params.getParameterSpec(PBEParameterSpec.class);
  319               } catch (InvalidParameterSpecException ipse) {
  320                   throw new InvalidAlgorithmParameterException(
  321                       "requires PBE parameters");
  322               }
  323           }
  324           implInit(opmode, key, paramSpec, random);
  325       }
  326   
  327       void implInit(int opmode, Key key, SecureRandom random)
  328           throws InvalidKeyException {
  329           try {
  330               implInit(opmode, key, (AlgorithmParameterSpec) null, random);
  331           } catch (InvalidAlgorithmParameterException iape) {
  332               throw new InvalidKeyException("requires PBE parameters");
  333           }
  334       }
  335   
  336       byte[] implUpdate(byte[] in, int inOff, int inLen) {
  337           return cipher.update(in, inOff, inLen);
  338       }
  339   
  340       int implUpdate(byte[] in, int inOff, int inLen, byte[] out, int outOff)
  341           throws ShortBufferException {
  342           return cipher.update(in, inOff, inLen, out, outOff);
  343       }
  344   
  345       byte[] implDoFinal(byte[] in, int inOff, int inLen)
  346           throws IllegalBlockSizeException, BadPaddingException {
  347           return cipher.doFinal(in, inOff, inLen);
  348       }
  349   
  350       int implDoFinal(byte[] in, int inOff, int inLen, byte[] out, int outOff)
  351           throws ShortBufferException, IllegalBlockSizeException,
  352                  BadPaddingException {
  353           return cipher.doFinal(in, inOff, inLen, out, outOff);
  354       }
  355   
  356       int implGetKeySize(Key key) throws InvalidKeyException {
  357           return keySize;
  358       }
  359   
  360       byte[] implWrap(Key key) throws IllegalBlockSizeException,
  361           InvalidKeyException {
  362           return cipher.wrap(key);
  363       }
  364   
  365       Key implUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
  366                      int wrappedKeyType)
  367           throws InvalidKeyException, NoSuchAlgorithmException {
  368           return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
  369                                wrappedKeyType);
  370       }
  371   
  372       public static final class PBEWithSHA1AndDESede extends CipherSpi {
  373           private final PKCS12PBECipherCore core;
  374           public PBEWithSHA1AndDESede() throws NoSuchAlgorithmException {
  375               core = new PKCS12PBECipherCore("DESede", 24);
  376           }
  377           protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
  378               throws IllegalBlockSizeException, BadPaddingException {
  379               return core.implDoFinal(in, inOff, inLen);
  380           }
  381           protected int engineDoFinal(byte[] in, int inOff, int inLen,
  382                                       byte[] out, int outOff)
  383               throws ShortBufferException, IllegalBlockSizeException,
  384                      BadPaddingException {
  385               return core.implDoFinal(in, inOff, inLen, out, outOff);
  386           }
  387           protected int engineGetBlockSize() {
  388               return core.implGetBlockSize();
  389           }
  390           protected byte[] engineGetIV() {
  391               return core.implGetIV();
  392           }
  393           protected int engineGetKeySize(Key key) throws InvalidKeyException {
  394               return core.implGetKeySize(key);
  395           }
  396           protected int engineGetOutputSize(int inLen) {
  397               return core.implGetOutputSize(inLen);
  398           }
  399           protected AlgorithmParameters engineGetParameters() {
  400               return core.implGetParameters();
  401           }
  402           protected void engineInit(int opmode, Key key,
  403                                     AlgorithmParameterSpec params,
  404                                     SecureRandom random)
  405               throws InvalidKeyException, InvalidAlgorithmParameterException {
  406               core.implInit(opmode, key, params, random);
  407           }
  408           protected void engineInit(int opmode, Key key,
  409                                     AlgorithmParameters params,
  410                                     SecureRandom random)
  411               throws InvalidKeyException, InvalidAlgorithmParameterException {
  412               core.implInit(opmode, key, params, random);
  413           }
  414           protected void engineInit(int opmode, Key key, SecureRandom random)
  415               throws InvalidKeyException {
  416               core.implInit(opmode, key, random);
  417           }
  418           protected void engineSetMode(String mode)
  419               throws NoSuchAlgorithmException {
  420               core.implSetMode(mode);
  421           }
  422           protected void engineSetPadding(String paddingScheme)
  423               throws NoSuchPaddingException {
  424               core.implSetPadding(paddingScheme);
  425           }
  426           protected Key engineUnwrap(byte[] wrappedKey,
  427                                      String wrappedKeyAlgorithm,
  428                                      int wrappedKeyType)
  429               throws InvalidKeyException, NoSuchAlgorithmException {
  430               return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
  431                                      wrappedKeyType);
  432           }
  433           protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
  434               return core.implUpdate(in, inOff, inLen);
  435           }
  436           protected int engineUpdate(byte[] in, int inOff, int inLen,
  437                                      byte[] out, int outOff)
  438               throws ShortBufferException {
  439               return core.implUpdate(in, inOff, inLen, out, outOff);
  440           }
  441           protected byte[] engineWrap(Key key)
  442               throws IllegalBlockSizeException, InvalidKeyException {
  443               return core.implWrap(key);
  444           }
  445       }
  446   
  447       public static final class PBEWithSHA1AndRC2_40 extends CipherSpi {
  448           private final PKCS12PBECipherCore core;
  449           public PBEWithSHA1AndRC2_40() throws NoSuchAlgorithmException {
  450               core = new PKCS12PBECipherCore("RC2", 5);
  451           }
  452           protected byte[] engineDoFinal(byte[] in, int inOff, int inLen)
  453               throws IllegalBlockSizeException, BadPaddingException {
  454               return core.implDoFinal(in, inOff, inLen);
  455           }
  456           protected int engineDoFinal(byte[] in, int inOff, int inLen,
  457                                       byte[] out, int outOff)
  458               throws ShortBufferException, IllegalBlockSizeException,
  459                      BadPaddingException {
  460               return core.implDoFinal(in, inOff, inLen, out, outOff);
  461           }
  462           protected int engineGetBlockSize() {
  463               return core.implGetBlockSize();
  464           }
  465           protected byte[] engineGetIV() {
  466               return core.implGetIV();
  467           }
  468           protected int engineGetKeySize(Key key) throws InvalidKeyException {
  469               return core.implGetKeySize(key);
  470           }
  471           protected int engineGetOutputSize(int inLen) {
  472               return core.implGetOutputSize(inLen);
  473           }
  474           protected AlgorithmParameters engineGetParameters() {
  475               return core.implGetParameters();
  476           }
  477           protected void engineInit(int opmode, Key key,
  478                                     AlgorithmParameterSpec params,
  479                                     SecureRandom random)
  480               throws InvalidKeyException, InvalidAlgorithmParameterException {
  481               core.implInit(opmode, key, params, random);
  482           }
  483           protected void engineInit(int opmode, Key key,
  484                                     AlgorithmParameters params,
  485                                     SecureRandom random)
  486               throws InvalidKeyException, InvalidAlgorithmParameterException {
  487               core.implInit(opmode, key, params, random);
  488           }
  489           protected void engineInit(int opmode, Key key, SecureRandom random)
  490               throws InvalidKeyException {
  491               core.implInit(opmode, key, random);
  492           }
  493           protected void engineSetMode(String mode)
  494               throws NoSuchAlgorithmException {
  495               core.implSetMode(mode);
  496           }
  497           protected void engineSetPadding(String paddingScheme)
  498               throws NoSuchPaddingException {
  499               core.implSetPadding(paddingScheme);
  500           }
  501           protected Key engineUnwrap(byte[] wrappedKey,
  502                                      String wrappedKeyAlgorithm,
  503                                      int wrappedKeyType)
  504               throws InvalidKeyException, NoSuchAlgorithmException {
  505               return core.implUnwrap(wrappedKey, wrappedKeyAlgorithm,
  506                                      wrappedKeyType);
  507           }
  508           protected byte[] engineUpdate(byte[] in, int inOff, int inLen) {
  509               return core.implUpdate(in, inOff, inLen);
  510           }
  511           protected int engineUpdate(byte[] in, int inOff, int inLen,
  512                                      byte[] out, int outOff)
  513               throws ShortBufferException {
  514               return core.implUpdate(in, inOff, inLen, out, outOff);
  515           }
  516           protected byte[] engineWrap(Key key)
  517               throws IllegalBlockSizeException, InvalidKeyException {
  518               return core.implWrap(key);
  519           }
  520       }
  521   }

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