1/* 2 * Copyright (c) 2003, 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24import java.security.InvalidKeyException; 25import java.security.NoSuchAlgorithmException; 26import java.security.spec.InvalidKeySpecException; 27import java.util.Arrays; 28import java.util.Random; 29import javax.crypto.SecretKey; 30import javax.crypto.SecretKeyFactory; 31import javax.crypto.interfaces.PBEKey; 32import javax.crypto.spec.PBEKeySpec; 33import javax.security.auth.DestroyFailedException; 34 35import static java.lang.System.out; 36 37/* 38 * @test 39 * @bug 8048820 40 * @summary The test verifies if the SecretKeyFactory.translateKey() method 41 * works as expected for the PBKDF2 algorithms. 42 */ 43 44public class PBKDF2TranslateTest { 45 46 private static final String PASS_PHRASE = "some hidden string"; 47 private static final int ITERATION_COUNT = 1000; 48 private static final int KEY_SIZE = 128; 49 private static final String[] TEST_ALGOS = { "PBKDF2WithHmacSHA1", 50 "PBKDF2WithHmacSHA224", "PBKDF2WithHmacSHA256", 51 "PBKDF2WithHmacSHA384", "PBKDF2WithHmacSHA512" }; 52 private final String algoForTest; 53 54 public static void main(String[] args) throws Exception { 55 for (String algo : TEST_ALGOS) { 56 PBKDF2TranslateTest theTest = new PBKDF2TranslateTest(algo); 57 byte[] salt = new byte[8]; 58 new Random().nextBytes(salt); 59 theTest.testMyOwnSecretKey(salt); 60 theTest.generateAndTranslateKey(salt); 61 theTest.translateSpoiledKey(salt); 62 } 63 } 64 65 public PBKDF2TranslateTest(String algo) { 66 algoForTest = algo; 67 } 68 69 /** 70 * The test case scenario implemented in the method: - derive PBKDF2 key 71 * using the given algorithm; - translate the key - check if the translated 72 * and original keys have the same key value. 73 * 74 */ 75 public void generateAndTranslateKey(byte[] salt) 76 throws NoSuchAlgorithmException, InvalidKeySpecException, 77 InvalidKeyException { 78 // derive PBKDF2 key 79 SecretKey key1 = getSecretKeyForPBKDF2(algoForTest, salt); 80 81 // translate key 82 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest); 83 SecretKey key2 = skf.translateKey(key1); 84 85 // Check if it still the same after translation 86 if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) { 87 System.out.println("Key1=" + new String(key1.getEncoded()) 88 + " key2=" + new String(key2.getEncoded()) + " salt=" 89 + new String(salt)); 90 throw new RuntimeException( 91 "generateAndTranslateKey test case failed: the key1 and" 92 + " key2 values in its primary encoding format are" 93 + " not the same for " + algoForTest 94 + " algorithm."); 95 } 96 } 97 98 /** 99 * The test case scenario implemented in the method: - derive Key1 for the 100 * given PBKDF2 algorithm - create my own secret Key2 as an instance of a 101 * class implements PBEKey - translate Key2 - check if the key value of the 102 * translated key and Key1 are the same. 103 */ 104 private void testMyOwnSecretKey(byte[] salt) 105 throws NoSuchAlgorithmException, InvalidKeySpecException, 106 InvalidKeyException { 107 SecretKey key1 = getSecretKeyForPBKDF2(algoForTest, salt); 108 SecretKey key2 = getMyOwnSecretKey(salt); 109 110 // Is it actually the same? 111 if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) { 112 throw new RuntimeException( 113 "We shouldn't be here. The key1 and key2 values in its" 114 + " primary encoding format have to be the same!"); 115 } 116 117 // translate key 118 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest); 119 SecretKey key3 = skf.translateKey(key2); 120 121 // Check if it still the same after translation 122 if (!Arrays.equals(key1.getEncoded(), key3.getEncoded())) { 123 System.out.println("Key1=" + new String(key1.getEncoded()) 124 + " key3=" + new String(key3.getEncoded()) + " salt=" 125 + new String(salt)); 126 throw new RuntimeException( 127 "testMyOwnSecretKey test case failed: the key1 and key3" 128 + " values in its primary encoding format are not" 129 + " the same for " + algoForTest + " algorithm."); 130 } 131 132 } 133 134 /** 135 * The test case scenario implemented in the method: - create my own secret 136 * Key2 as an instance of a class implements PBEKey - spoil the key (set 137 * iteration count to 0, for example) - try to translate key - 138 * InvalidKeyException is expected. 139 */ 140 public void translateSpoiledKey(byte[] salt) 141 throws NoSuchAlgorithmException, InvalidKeySpecException { 142 // derive the key 143 SecretKey key1 = getMyOwnSecretKey(salt); 144 145 // spoil the key 146 ((MyPBKDF2SecretKey) key1).spoil(); 147 148 // translate key 149 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest); 150 try { 151 skf.translateKey(key1); 152 throw new RuntimeException( 153 "translateSpoiledKey test case failed, should throw" 154 + " InvalidKeyException when spoil the key"); 155 } catch (InvalidKeyException ike) { 156 out.println("Expected exception when spoil the key"); 157 } 158 159 } 160 161 /** 162 * Generate a PBKDF2 secret key using given algorithm. 163 */ 164 private SecretKey getSecretKeyForPBKDF2(String algoDeriveKey, byte[] salt) 165 throws NoSuchAlgorithmException, InvalidKeySpecException { 166 167 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoDeriveKey); 168 PBEKeySpec spec = new PBEKeySpec(PASS_PHRASE.toCharArray(), salt, 169 ITERATION_COUNT, KEY_SIZE); 170 171 return skf.generateSecret(spec); 172 } 173 174 /** 175 * Generate a secrete key as an instance of a class implements PBEKey. 176 */ 177 private SecretKey getMyOwnSecretKey(byte[] salt) 178 throws InvalidKeySpecException, NoSuchAlgorithmException { 179 return new MyPBKDF2SecretKey(PASS_PHRASE, algoForTest, salt, 180 ITERATION_COUNT, KEY_SIZE); 181 } 182 183 /** 184 * An utility class to check the SecretKeyFactory.translateKey() method. 185 */ 186 class MyPBKDF2SecretKey implements PBEKey { 187 private final byte[] key; 188 private final byte[] salt; 189 private final String algorithm; 190 private final int keyLength; 191 private final String pass; 192 private int itereationCount; 193 194 /** 195 * The key is generating by SecretKeyFactory and its value just copying 196 * in the key field of MySecretKey class. So, this is real key derived 197 * using the given algo. 198 */ 199 public MyPBKDF2SecretKey(String passPhrase, String algo, byte[] salt1, 200 int iterationCount, int keySize) 201 throws InvalidKeySpecException, NoSuchAlgorithmException { 202 algorithm = algo; 203 salt = salt1; 204 itereationCount = iterationCount; 205 pass = passPhrase; 206 207 PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, 208 iterationCount, keySize); 209 210 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algo); 211 212 SecretKey realKey = keyFactory.generateSecret(spec); 213 214 keyLength = realKey.getEncoded().length; 215 216 key = new byte[keyLength]; 217 System.arraycopy(realKey.getEncoded(), 0, key, 0, keyLength); 218 } 219 220 @Override 221 public String getAlgorithm() { 222 return algorithm; 223 } 224 225 @Override 226 public String getFormat() { 227 return "RAW"; 228 } 229 230 @Override 231 public byte[] getEncoded() { 232 byte[] copy = new byte[keyLength]; 233 System.arraycopy(key, 0, copy, 0, keyLength); 234 return copy; 235 } 236 237 @Override 238 public int getIterationCount() { 239 return itereationCount; 240 } 241 242 @Override 243 public byte[] getSalt() { 244 return salt; 245 } 246 247 @Override 248 public char[] getPassword() { 249 return pass.toCharArray(); 250 } 251 252 /** 253 * Spoil the generated key (before translation) to cause an 254 * InvalidKeyException 255 */ 256 public void spoil() { 257 itereationCount = -1; 258 } 259 260 @Override 261 public void destroy() throws DestroyFailedException { 262 } 263 264 @Override 265 public boolean isDestroyed() { 266 return false; 267 } 268 269 } 270} 271