1 /*
   2  * Copyright (c) 2003, 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.util.Arrays;
  29 import java.nio.ByteBuffer;
  30 
  31 import javax.crypto.MacSpi;
  32 import javax.crypto.SecretKey;
  33 import javax.crypto.spec.SecretKeySpec;
  34 import javax.crypto.spec.PBEParameterSpec;
  35 import java.security.*;
  36 import java.security.spec.*;
  37 
  38 /**
  39  * This is an implementation of the HMAC-PBESHA1 algorithm as defined
  40  * in PKCS#12 v1.0 standard.
  41  *
  42  * @author Valerie Peng
  43  */
  44 public final class HmacPKCS12PBESHA1 extends HmacCore {
  45 
  46     /**
  47      * Standard constructor, creates a new HmacSHA1 instance.
  48      */
  49     public HmacPKCS12PBESHA1() throws NoSuchAlgorithmException {
  50         super("SHA1", 64);
  51     }
  52 
  53     /**
  54      * Initializes the HMAC with the given secret key and algorithm parameters.
  55      *
  56      * @param key the secret key.
  57      * @param params the algorithm parameters.
  58      *
  59      * @exception InvalidKeyException if the given key is inappropriate for
  60      * initializing this MAC.
  61      * @exception InvalidAlgorithmParameterException if the given algorithm
  62      * parameters are inappropriate for this MAC.
  63      */
  64     protected void engineInit(Key key, AlgorithmParameterSpec params)
  65         throws InvalidKeyException, InvalidAlgorithmParameterException {
  66         char[] passwdChars;
  67         byte[] salt = null;
  68         int iCount = 0;
  69         if (key instanceof javax.crypto.interfaces.PBEKey) {
  70             javax.crypto.interfaces.PBEKey pbeKey =
  71                 (javax.crypto.interfaces.PBEKey) key;
  72             passwdChars = pbeKey.getPassword();
  73             salt = pbeKey.getSalt(); // maybe null if unspecified
  74             iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified
  75         } else if (key instanceof SecretKey) {
  76             byte[] passwdBytes;
  77             if (!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3)) ||
  78                     (passwdBytes = key.getEncoded()) == null) {
  79                 throw new InvalidKeyException("Missing password");
  80             }
  81             passwdChars = new char[passwdBytes.length];
  82             for (int i=0; i<passwdChars.length; i++) {
  83                 passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
  84             }
  85             Arrays.fill(passwdBytes, (byte)0x00);
  86         } else {
  87             throw new InvalidKeyException("SecretKey of PBE type required");
  88         }
  89 
  90         byte[] derivedKey;
  91         try {
  92             if (params == null) {
  93                 // should not auto-generate default values since current
  94                 // javax.crypto.Mac api does not have any method for caller to
  95                 // retrieve the generated defaults.
  96                 if ((salt == null) || (iCount == 0)) {
  97                     throw new InvalidAlgorithmParameterException
  98                             ("PBEParameterSpec required for salt and iteration count");
  99                 }
 100             } else if (!(params instanceof PBEParameterSpec)) {
 101                 throw new InvalidAlgorithmParameterException
 102                         ("PBEParameterSpec type required");
 103             } else {
 104                 PBEParameterSpec pbeParams = (PBEParameterSpec) params;
 105                 // make sure the parameter values are consistent
 106                 if (salt != null) {
 107                     if (!Arrays.equals(salt, pbeParams.getSalt())) {
 108                         throw new InvalidAlgorithmParameterException
 109                                 ("Inconsistent value of salt between key and params");
 110                     }
 111                 } else {
 112                     salt = pbeParams.getSalt();
 113                 }
 114                 if (iCount != 0) {
 115                     if (iCount != pbeParams.getIterationCount()) {
 116                         throw new InvalidAlgorithmParameterException
 117                                 ("Different iteration count between key and params");
 118                     }
 119                 } else {
 120                     iCount = pbeParams.getIterationCount();
 121                 }
 122             }
 123             // For security purpose, we need to enforce a minimum length
 124             // for salt; just require the minimum salt length to be 8-byte
 125             // which is what PKCS#5 recommends and openssl does.
 126             if (salt.length < 8) {
 127                 throw new InvalidAlgorithmParameterException
 128                         ("Salt must be at least 8 bytes long");
 129             }
 130             if (iCount <= 0) {
 131                 throw new InvalidAlgorithmParameterException
 132                         ("IterationCount must be a positive number");
 133             }
 134             derivedKey = PKCS12PBECipherCore.derive(passwdChars, salt,
 135                     iCount, engineGetMacLength(), PKCS12PBECipherCore.MAC_KEY);
 136         } finally {
 137             Arrays.fill(passwdChars, '\0');
 138         }
 139         SecretKey cipherKey = new SecretKeySpec(derivedKey, "HmacSHA1");
 140         super.engineInit(cipherKey, null);
 141     }
 142 }