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 26package com.sun.crypto.provider; 27 28import java.util.Arrays; 29import java.nio.ByteBuffer; 30 31import javax.crypto.MacSpi; 32import javax.crypto.SecretKey; 33import javax.crypto.spec.SecretKeySpec; 34import javax.crypto.spec.PBEKeySpec; 35import javax.crypto.spec.PBEParameterSpec; 36import java.security.*; 37import java.security.spec.*; 38 39/** 40 * This is an implementation of the PBMAC1 algorithms as defined 41 * in PKCS#5 v2.1 standard. 42 */ 43abstract class PBMAC1Core extends HmacCore { 44 45 // NOTE: this class inherits the Cloneable interface from HmacCore 46 // Need to override clone() if mutable fields are added. 47 private final String kdfAlgo; 48 private final String hashAlgo; 49 private final int blockLength; // in octets 50 51 /** 52 * Creates an instance of PBMAC1 according to the selected 53 * password-based key derivation function. 54 */ 55 PBMAC1Core(String kdfAlgo, String hashAlgo, int blockLength) 56 throws NoSuchAlgorithmException { 57 super(hashAlgo, blockLength); 58 this.kdfAlgo = kdfAlgo; 59 this.hashAlgo = hashAlgo; 60 this.blockLength = blockLength; 61 } 62 63 private static PBKDF2Core getKDFImpl(String algo) { 64 PBKDF2Core kdf = null; 65 switch(algo) { 66 case "HmacSHA1": 67 kdf = new PBKDF2Core.HmacSHA1(); 68 break; 69 case "HmacSHA224": 70 kdf = new PBKDF2Core.HmacSHA224(); 71 break; 72 case "HmacSHA256": 73 kdf = new PBKDF2Core.HmacSHA256(); 74 break; 75 case "HmacSHA384": 76 kdf = new PBKDF2Core.HmacSHA384(); 77 break; 78 case "HmacSHA512": 79 kdf = new PBKDF2Core.HmacSHA512(); 80 break; 81 default: 82 throw new ProviderException( 83 "No MAC implementation for " + algo); 84 } 85 return kdf; 86 } 87 88 /** 89 * Initializes the HMAC with the given secret key and algorithm parameters. 90 * 91 * @param key the secret key. 92 * @param params the algorithm parameters. 93 * 94 * @exception InvalidKeyException if the given key is inappropriate for 95 * initializing this MAC. 96 * @exception InvalidAlgorithmParameterException if the given algorithm 97 * parameters are inappropriate for this MAC. 98 */ 99 protected void engineInit(Key key, AlgorithmParameterSpec params) 100 throws InvalidKeyException, InvalidAlgorithmParameterException { 101 char[] passwdChars; 102 byte[] salt = null; 103 int iCount = 0; 104 if (key instanceof javax.crypto.interfaces.PBEKey) { 105 javax.crypto.interfaces.PBEKey pbeKey = 106 (javax.crypto.interfaces.PBEKey) key; 107 passwdChars = pbeKey.getPassword(); 108 salt = pbeKey.getSalt(); // maybe null if unspecified 109 iCount = pbeKey.getIterationCount(); // maybe 0 if unspecified 110 } else if (key instanceof SecretKey) { 111 byte[] passwdBytes = key.getEncoded(); 112 if ((passwdBytes == null) || 113 !(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) { 114 throw new InvalidKeyException("Missing password"); 115 } 116 passwdChars = new char[passwdBytes.length]; 117 for (int i=0; i<passwdChars.length; i++) { 118 passwdChars[i] = (char) (passwdBytes[i] & 0x7f); 119 } 120 } else { 121 throw new InvalidKeyException("SecretKey of PBE type required"); 122 } 123 if (params == null) { 124 // should not auto-generate default values since current 125 // javax.crypto.Mac api does not have any method for caller to 126 // retrieve the generated defaults. 127 if ((salt == null) || (iCount == 0)) { 128 throw new InvalidAlgorithmParameterException 129 ("PBEParameterSpec required for salt and iteration count"); 130 } 131 } else if (!(params instanceof PBEParameterSpec)) { 132 throw new InvalidAlgorithmParameterException 133 ("PBEParameterSpec type required"); 134 } else { 135 PBEParameterSpec pbeParams = (PBEParameterSpec) params; 136 // make sure the parameter values are consistent 137 if (salt != null) { 138 if (!Arrays.equals(salt, pbeParams.getSalt())) { 139 throw new InvalidAlgorithmParameterException 140 ("Inconsistent value of salt between key and params"); 141 } 142 } else { 143 salt = pbeParams.getSalt(); 144 } 145 if (iCount != 0) { 146 if (iCount != pbeParams.getIterationCount()) { 147 throw new InvalidAlgorithmParameterException 148 ("Different iteration count between key and params"); 149 } 150 } else { 151 iCount = pbeParams.getIterationCount(); 152 } 153 } 154 // For security purpose, we need to enforce a minimum length 155 // for salt; just require the minimum salt length to be 8-byte 156 // which is what PKCS#5 recommends and openssl does. 157 if (salt.length < 8) { 158 throw new InvalidAlgorithmParameterException 159 ("Salt must be at least 8 bytes long"); 160 } 161 if (iCount <= 0) { 162 throw new InvalidAlgorithmParameterException 163 ("IterationCount must be a positive number"); 164 } 165 166 PBEKeySpec pbeSpec = 167 new PBEKeySpec(passwdChars, salt, iCount, blockLength); 168 // password char[] was cloned in PBEKeySpec constructor, 169 // so we can zero it out here 170 java.util.Arrays.fill(passwdChars, ' '); 171 172 SecretKey s = null; 173 PBKDF2Core kdf = getKDFImpl(kdfAlgo); 174 try { 175 s = kdf.engineGenerateSecret(pbeSpec); 176 177 } catch (InvalidKeySpecException ikse) { 178 InvalidKeyException ike = 179 new InvalidKeyException("Cannot construct PBE key"); 180 ike.initCause(ikse); 181 throw ike; 182 } 183 byte[] derivedKey = s.getEncoded(); 184 SecretKey cipherKey = new SecretKeySpec(derivedKey, kdfAlgo); 185 186 super.engineInit(cipherKey, null); 187 } 188 189 public static final class HmacSHA1 extends PBMAC1Core { 190 public HmacSHA1() throws NoSuchAlgorithmException { 191 super("HmacSHA1", "SHA1", 64); 192 } 193 } 194 195 public static final class HmacSHA224 extends PBMAC1Core { 196 public HmacSHA224() throws NoSuchAlgorithmException { 197 super("HmacSHA224", "SHA-224", 64); 198 } 199 } 200 201 public static final class HmacSHA256 extends PBMAC1Core { 202 public HmacSHA256() throws NoSuchAlgorithmException { 203 super("HmacSHA256", "SHA-256", 64); 204 } 205 } 206 207 public static final class HmacSHA384 extends PBMAC1Core { 208 public HmacSHA384() throws NoSuchAlgorithmException { 209 super("HmacSHA384", "SHA-384", 128); 210 } 211 } 212 213 public static final class HmacSHA512 extends PBMAC1Core { 214 public HmacSHA512() throws NoSuchAlgorithmException { 215 super("HmacSHA512", "SHA-512", 128); 216 } 217 } 218} 219