jdk/src/java.base/share/classes/com/sun/crypto/provider/PBES2Core.java
changeset 25859 3317bb8137f4
parent 16909 78a1749a43e2
child 35285 c8e399b7825b
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 2012, 2013, 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.security.*;
       
    29 import java.security.spec.*;
       
    30 import javax.crypto.*;
       
    31 import javax.crypto.spec.*;
       
    32 
       
    33 /**
       
    34  * This class represents password-based encryption as defined by the PKCS #5
       
    35  * standard.
       
    36  * These algorithms implement PBE with HmacSHA1/HmacSHA2-family and AES-CBC.
       
    37  * Padding is done as described in PKCS #5.
       
    38  *
       
    39  * @author Jan Luehe
       
    40  *
       
    41  *
       
    42  * @see javax.crypto.Cipher
       
    43  */
       
    44 abstract class PBES2Core extends CipherSpi {
       
    45 
       
    46     private static final int DEFAULT_SALT_LENGTH = 20;
       
    47     private static final int DEFAULT_COUNT = 4096;
       
    48 
       
    49     // the encapsulated cipher
       
    50     private final CipherCore cipher;
       
    51     private final int keyLength; // in bits
       
    52     private final int blkSize; // in bits
       
    53     private final PBKDF2Core kdf;
       
    54     private final String pbeAlgo;
       
    55     private final String cipherAlgo;
       
    56     private int iCount = DEFAULT_COUNT;
       
    57     private byte[] salt = null;
       
    58     private IvParameterSpec ivSpec = null;
       
    59 
       
    60     /**
       
    61      * Creates an instance of PBE Scheme 2 according to the selected
       
    62      * password-based key derivation function and encryption scheme.
       
    63      */
       
    64     PBES2Core(String kdfAlgo, String cipherAlgo, int keySize)
       
    65         throws NoSuchAlgorithmException, NoSuchPaddingException {
       
    66 
       
    67         this.cipherAlgo = cipherAlgo;
       
    68         keyLength = keySize * 8;
       
    69         pbeAlgo = "PBEWith" + kdfAlgo + "And" + cipherAlgo + "_" + keyLength;
       
    70 
       
    71         if (cipherAlgo.equals("AES")) {
       
    72             blkSize = AESConstants.AES_BLOCK_SIZE;
       
    73             cipher = new CipherCore(new AESCrypt(), blkSize);
       
    74 
       
    75             switch(kdfAlgo) {
       
    76             case "HmacSHA1":
       
    77                 kdf = new PBKDF2Core.HmacSHA1();
       
    78                 break;
       
    79             case "HmacSHA224":
       
    80                 kdf = new PBKDF2Core.HmacSHA224();
       
    81                 break;
       
    82             case "HmacSHA256":
       
    83                 kdf = new PBKDF2Core.HmacSHA256();
       
    84                 break;
       
    85             case "HmacSHA384":
       
    86                 kdf = new PBKDF2Core.HmacSHA384();
       
    87                 break;
       
    88             case "HmacSHA512":
       
    89                 kdf = new PBKDF2Core.HmacSHA512();
       
    90                 break;
       
    91             default:
       
    92                 throw new NoSuchAlgorithmException(
       
    93                     "No Cipher implementation for " + kdfAlgo);
       
    94             }
       
    95         } else {
       
    96             throw new NoSuchAlgorithmException("No Cipher implementation for " +
       
    97                                                pbeAlgo);
       
    98         }
       
    99         cipher.setMode("CBC");
       
   100         cipher.setPadding("PKCS5Padding");
       
   101     }
       
   102 
       
   103     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
       
   104         if ((mode != null) && (!mode.equalsIgnoreCase("CBC"))) {
       
   105             throw new NoSuchAlgorithmException("Invalid cipher mode: " + mode);
       
   106         }
       
   107     }
       
   108 
       
   109     protected void engineSetPadding(String paddingScheme)
       
   110         throws NoSuchPaddingException {
       
   111         if ((paddingScheme != null) &&
       
   112             (!paddingScheme.equalsIgnoreCase("PKCS5Padding"))) {
       
   113             throw new NoSuchPaddingException("Invalid padding scheme: " +
       
   114                                              paddingScheme);
       
   115         }
       
   116     }
       
   117 
       
   118     protected int engineGetBlockSize() {
       
   119         return blkSize;
       
   120     }
       
   121 
       
   122     protected int engineGetOutputSize(int inputLen) {
       
   123         return cipher.getOutputSize(inputLen);
       
   124     }
       
   125 
       
   126     protected byte[] engineGetIV() {
       
   127         return cipher.getIV();
       
   128     }
       
   129 
       
   130     protected AlgorithmParameters engineGetParameters() {
       
   131         AlgorithmParameters params = null;
       
   132         if (salt == null) {
       
   133             // generate random salt and use default iteration count
       
   134             salt = new byte[DEFAULT_SALT_LENGTH];
       
   135             SunJCE.getRandom().nextBytes(salt);
       
   136             iCount = DEFAULT_COUNT;
       
   137         }
       
   138         if (ivSpec == null) {
       
   139             // generate random IV
       
   140             byte[] ivBytes = new byte[blkSize];
       
   141             SunJCE.getRandom().nextBytes(ivBytes);
       
   142             ivSpec = new IvParameterSpec(ivBytes);
       
   143         }
       
   144         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iCount, ivSpec);
       
   145         try {
       
   146             params = AlgorithmParameters.getInstance(pbeAlgo,
       
   147                 SunJCE.getInstance());
       
   148             params.init(pbeSpec);
       
   149         } catch (NoSuchAlgorithmException nsae) {
       
   150             // should never happen
       
   151             throw new RuntimeException("SunJCE called, but not configured");
       
   152         } catch (InvalidParameterSpecException ipse) {
       
   153             // should never happen
       
   154             throw new RuntimeException("PBEParameterSpec not supported");
       
   155         }
       
   156         return params;
       
   157     }
       
   158 
       
   159     protected void engineInit(int opmode, Key key, SecureRandom random)
       
   160         throws InvalidKeyException {
       
   161         try {
       
   162             engineInit(opmode, key, (AlgorithmParameterSpec) null, random);
       
   163         } catch (InvalidAlgorithmParameterException ie) {
       
   164             InvalidKeyException ike =
       
   165                 new InvalidKeyException("requires PBE parameters");
       
   166             ike.initCause(ie);
       
   167             throw ike;
       
   168         }
       
   169     }
       
   170 
       
   171     protected void engineInit(int opmode, Key key,
       
   172                               AlgorithmParameterSpec params,
       
   173                               SecureRandom random)
       
   174         throws InvalidKeyException, InvalidAlgorithmParameterException {
       
   175 
       
   176         if ((key == null) ||
       
   177             (key.getEncoded() == null) ||
       
   178             !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {
       
   179             throw new InvalidKeyException("Missing password");
       
   180         }
       
   181 
       
   182         // TBD: consolidate the salt, ic and IV parameter checks below
       
   183 
       
   184         // Extract salt and iteration count from the key, if present
       
   185         if (key instanceof javax.crypto.interfaces.PBEKey) {
       
   186             salt = ((javax.crypto.interfaces.PBEKey)key).getSalt();
       
   187             if (salt != null && salt.length < 8) {
       
   188                 throw new InvalidAlgorithmParameterException(
       
   189                     "Salt must be at least 8 bytes long");
       
   190             }
       
   191             iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount();
       
   192             if (iCount == 0) {
       
   193                 iCount = DEFAULT_COUNT;
       
   194             } else if (iCount < 0) {
       
   195                 throw new InvalidAlgorithmParameterException(
       
   196                     "Iteration count must be a positive number");
       
   197             }
       
   198         }
       
   199 
       
   200         // Extract salt, iteration count and IV from the params, if present
       
   201         if (params == null) {
       
   202             if (salt == null) {
       
   203                 // generate random salt and use default iteration count
       
   204                 salt = new byte[DEFAULT_SALT_LENGTH];
       
   205                 random.nextBytes(salt);
       
   206                 iCount = DEFAULT_COUNT;
       
   207             }
       
   208             if ((opmode == Cipher.ENCRYPT_MODE) ||
       
   209                         (opmode == Cipher.WRAP_MODE)) {
       
   210                 // generate random IV
       
   211                 byte[] ivBytes = new byte[blkSize];
       
   212                 random.nextBytes(ivBytes);
       
   213                 ivSpec = new IvParameterSpec(ivBytes);
       
   214             }
       
   215         } else {
       
   216             if (!(params instanceof PBEParameterSpec)) {
       
   217                 throw new InvalidAlgorithmParameterException
       
   218                     ("Wrong parameter type: PBE expected");
       
   219             }
       
   220             // salt and iteration count from the params take precedence
       
   221             byte[] specSalt = ((PBEParameterSpec) params).getSalt();
       
   222             if (specSalt != null && specSalt.length < 8) {
       
   223                 throw new InvalidAlgorithmParameterException(
       
   224                     "Salt must be at least 8 bytes long");
       
   225             }
       
   226             salt = specSalt;
       
   227             int specICount = ((PBEParameterSpec) params).getIterationCount();
       
   228             if (specICount == 0) {
       
   229                 specICount = DEFAULT_COUNT;
       
   230             } else if (specICount < 0) {
       
   231                 throw new InvalidAlgorithmParameterException(
       
   232                     "Iteration count must be a positive number");
       
   233             }
       
   234             iCount = specICount;
       
   235 
       
   236             AlgorithmParameterSpec specParams =
       
   237                 ((PBEParameterSpec) params).getParameterSpec();
       
   238             if (specParams != null) {
       
   239                 if (specParams instanceof IvParameterSpec) {
       
   240                     ivSpec = (IvParameterSpec)specParams;
       
   241                 } else {
       
   242                     throw new InvalidAlgorithmParameterException(
       
   243                         "Wrong parameter type: IV expected");
       
   244                 }
       
   245             } else if ((opmode == Cipher.ENCRYPT_MODE) ||
       
   246                         (opmode == Cipher.WRAP_MODE)) {
       
   247                 // generate random IV
       
   248                 byte[] ivBytes = new byte[blkSize];
       
   249                 random.nextBytes(ivBytes);
       
   250                 ivSpec = new IvParameterSpec(ivBytes);
       
   251             } else {
       
   252                 throw new InvalidAlgorithmParameterException(
       
   253                     "Missing parameter type: IV expected");
       
   254             }
       
   255         }
       
   256 
       
   257         SecretKeySpec cipherKey = null;
       
   258         byte[] derivedKey = null;
       
   259         byte[] passwdBytes = key.getEncoded();
       
   260         char[] passwdChars = new char[passwdBytes.length];
       
   261 
       
   262         for (int i=0; i<passwdChars.length; i++)
       
   263             passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
       
   264 
       
   265         PBEKeySpec pbeSpec =
       
   266             new PBEKeySpec(passwdChars, salt, iCount, blkSize * 8);
       
   267             // password char[] was cloned in PBEKeySpec constructor,
       
   268             // so we can zero it out here
       
   269         java.util.Arrays.fill(passwdChars, ' ');
       
   270         java.util.Arrays.fill(passwdBytes, (byte)0x00);
       
   271 
       
   272         SecretKey s = null;
       
   273 
       
   274         try {
       
   275             s = kdf.engineGenerateSecret(pbeSpec);
       
   276 
       
   277         } catch (InvalidKeySpecException ikse) {
       
   278             InvalidKeyException ike =
       
   279                 new InvalidKeyException("Cannot construct PBE key");
       
   280             ike.initCause(ikse);
       
   281             throw ike;
       
   282         }
       
   283         derivedKey = s.getEncoded();
       
   284         cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
       
   285 
       
   286         // initialize the underlying cipher
       
   287         cipher.init(opmode, cipherKey, ivSpec, random);
       
   288     }
       
   289 
       
   290     protected void engineInit(int opmode, Key key, AlgorithmParameters params,
       
   291                               SecureRandom random)
       
   292         throws InvalidKeyException, InvalidAlgorithmParameterException {
       
   293         AlgorithmParameterSpec pbeSpec = null;
       
   294         if (params != null) {
       
   295             try {
       
   296                 pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
       
   297             } catch (InvalidParameterSpecException ipse) {
       
   298                 throw new InvalidAlgorithmParameterException(
       
   299                     "Wrong parameter type: PBE expected");
       
   300             }
       
   301         }
       
   302         engineInit(opmode, key, pbeSpec, random);
       
   303     }
       
   304 
       
   305     protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
       
   306         return cipher.update(input, inputOffset, inputLen);
       
   307     }
       
   308 
       
   309     protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
       
   310                                byte[] output, int outputOffset)
       
   311         throws ShortBufferException {
       
   312         return cipher.update(input, inputOffset, inputLen,
       
   313                              output, outputOffset);
       
   314     }
       
   315 
       
   316     protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
       
   317         throws IllegalBlockSizeException, BadPaddingException {
       
   318         return cipher.doFinal(input, inputOffset, inputLen);
       
   319     }
       
   320 
       
   321     protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
       
   322                                 byte[] output, int outputOffset)
       
   323         throws ShortBufferException, IllegalBlockSizeException,
       
   324                BadPaddingException {
       
   325         return cipher.doFinal(input, inputOffset, inputLen,
       
   326                               output, outputOffset);
       
   327     }
       
   328 
       
   329     protected int engineGetKeySize(Key key) throws InvalidKeyException {
       
   330         return keyLength;
       
   331     }
       
   332 
       
   333     protected byte[] engineWrap(Key key)
       
   334         throws IllegalBlockSizeException, InvalidKeyException {
       
   335         return cipher.wrap(key);
       
   336     }
       
   337 
       
   338     protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
       
   339                                int wrappedKeyType)
       
   340         throws InvalidKeyException, NoSuchAlgorithmException {
       
   341         byte[] encodedKey;
       
   342         return cipher.unwrap(wrappedKey, wrappedKeyAlgorithm,
       
   343                              wrappedKeyType);
       
   344     }
       
   345 
       
   346     public static final class HmacSHA1AndAES_128 extends PBES2Core {
       
   347         public HmacSHA1AndAES_128()
       
   348             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   349             super("HmacSHA1", "AES", 16);
       
   350         }
       
   351     }
       
   352 
       
   353     public static final class HmacSHA224AndAES_128 extends PBES2Core {
       
   354         public HmacSHA224AndAES_128()
       
   355             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   356             super("HmacSHA224", "AES", 16);
       
   357         }
       
   358     }
       
   359 
       
   360     public static final class HmacSHA256AndAES_128 extends PBES2Core {
       
   361         public HmacSHA256AndAES_128()
       
   362             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   363             super("HmacSHA256", "AES", 16);
       
   364         }
       
   365     }
       
   366 
       
   367     public static final class HmacSHA384AndAES_128 extends PBES2Core {
       
   368         public HmacSHA384AndAES_128()
       
   369             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   370             super("HmacSHA384", "AES", 16);
       
   371         }
       
   372     }
       
   373 
       
   374     public static final class HmacSHA512AndAES_128 extends PBES2Core {
       
   375         public HmacSHA512AndAES_128()
       
   376             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   377             super("HmacSHA512", "AES", 16);
       
   378         }
       
   379     }
       
   380 
       
   381     public static final class HmacSHA1AndAES_256 extends PBES2Core {
       
   382         public HmacSHA1AndAES_256()
       
   383             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   384             super("HmacSHA1", "AES", 32);
       
   385         }
       
   386     }
       
   387 
       
   388     public static final class HmacSHA224AndAES_256 extends PBES2Core {
       
   389         public HmacSHA224AndAES_256()
       
   390             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   391             super("HmacSHA224", "AES", 32);
       
   392         }
       
   393     }
       
   394 
       
   395     public static final class HmacSHA256AndAES_256 extends PBES2Core {
       
   396         public HmacSHA256AndAES_256()
       
   397             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   398             super("HmacSHA256", "AES", 32);
       
   399         }
       
   400     }
       
   401 
       
   402     public static final class HmacSHA384AndAES_256 extends PBES2Core {
       
   403         public HmacSHA384AndAES_256()
       
   404             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   405             super("HmacSHA384", "AES", 32);
       
   406         }
       
   407     }
       
   408 
       
   409     public static final class HmacSHA512AndAES_256 extends PBES2Core {
       
   410         public HmacSHA512AndAES_256()
       
   411             throws NoSuchAlgorithmException, NoSuchPaddingException {
       
   412             super("HmacSHA512", "AES", 32);
       
   413         }
       
   414     }
       
   415 }