1 /* 2 * Copyright (c) 2012, 2014, 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 24 import java.security.InvalidKeyException; 25 import java.security.NoSuchAlgorithmException; 26 import java.security.spec.InvalidKeySpecException; 27 import java.util.Arrays; 28 import java.util.Random; 29 import javax.crypto.SecretKey; 30 import javax.crypto.SecretKeyFactory; 31 import javax.crypto.interfaces.PBEKey; 32 import javax.crypto.spec.PBEKeySpec; 33 34 /** 35 * @test 36 * @bug 8041781 37 * @summary Verify if the SecretKeyFactory.translateKey() method works 38 * @author Alexander Fomin 39 * @run main PBKDF2Translate 40 * @key randomness 41 */ 42 public class PBKDF2Translate { 43 44 private static final String[] ALGO_TO_TEST = { 45 "PBKDF2WithHmacSHA1", 46 "PBKDF2WithHmacSHA224", 47 "PBKDF2WithHmacSHA256", 48 "PBKDF2WithHmacSHA384", 49 "PBKDF2WithHmacSHA512" 50 }; 51 52 private static final String PASS_PHRASE = "some hidden string"; 53 private static final int ITERATION_COUNT = 1000; 54 private static final int KEY_SIZE = 128; 55 56 private final String algoToTest; 57 private final byte[] salt = new byte[8]; 58 59 public static void main(String[] args) throws Exception { 60 61 boolean failed = false; 62 63 for (String algo : ALGO_TO_TEST) { 64 65 System.out.println("Testing " + algo + ":"); 66 PBKDF2Translate theTest = new PBKDF2Translate(algo); 67 68 try { 69 if (!theTest.testMyOwnSecretKey() 70 || !theTest.generateAndTranslateKey() 71 || !theTest.translateSpoiledKey()) { 72 // we don't want to set failed to false 73 failed = true; 74 } 75 } catch (InvalidKeyException | NoSuchAlgorithmException | 76 InvalidKeySpecException e) { 77 e.printStackTrace(System.err); 78 failed = true; 79 } 80 } 81 82 if (failed) { 83 throw new RuntimeException("One or more tests failed...."); 84 } 85 } 86 87 public PBKDF2Translate(String algoToTest) { 88 this.algoToTest = algoToTest; 89 new Random().nextBytes(this.salt); 90 } 91 92 /** 93 * The test case scenario implemented in the method: - derive PBKDF2 key 94 * using the given algorithm; - translate the key - check if the translated 95 * and original keys have the same key value. 96 * 97 * @return true if the test case passed; false - otherwise. 98 * @throws NoSuchAlgorithmException 99 * @throws InvalidKeySpecException 100 * @throws InvalidKeyException 101 */ 102 public boolean generateAndTranslateKey() throws NoSuchAlgorithmException, 103 InvalidKeySpecException, InvalidKeyException { 104 // derive PBKDF2 key 105 SecretKey key1 = getSecretKeyForPBKDF2(algoToTest); 106 107 // translate key 108 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoToTest); 109 SecretKey key2 = skf.translateKey(key1); 110 111 // check if it still the same after translation 112 if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) { 113 System.err.println("generateAndTranslateKey test case failed: the " 114 + "key1 and key2 values in its primary encoding format are " 115 + "not the same for " + algoToTest + "algorithm."); 116 return false; 117 } 118 119 return true; 120 } 121 122 /** 123 * The test case scenario implemented in the method: - derive Key1 for the 124 * given PBKDF2 algorithm - create my own secret Key2 as an instance of a 125 * class implements PBEKey - translate Key2 - check if the key value of the 126 * translated key and Key1 are the same. 127 * 128 * @return true if the test case passed; false - otherwise. 129 * @throws NoSuchAlgorithmException 130 * @throws InvalidKeySpecException 131 * @throws InvalidKeyException 132 */ 133 public boolean testMyOwnSecretKey() 134 throws NoSuchAlgorithmException, InvalidKeySpecException, 135 InvalidKeyException { 136 SecretKey key1 = getSecretKeyForPBKDF2(algoToTest); 137 SecretKey key2 = getMyOwnSecretKey(); 138 139 // Is it actually the same? 140 if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) { 141 System.err.println("We shouldn't be here. The key1 and key2 values " 142 + "in its primary encoding format have to be the same!"); 143 return false; 144 } 145 146 // Translate key 147 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoToTest); 148 SecretKey key3 = skf.translateKey(key2); 149 150 // Check if it still the same after translation 151 if (!Arrays.equals(key1.getEncoded(), key3.getEncoded())) { 152 System.err.println("testMyOwnSecretKey test case failed: the key1 " 153 + "and key3 values in its primary encoding format are not " 154 + "the same for " + algoToTest + "algorithm."); 155 return false; 156 } 157 158 return true; 159 } 160 161 /** 162 * The test case scenario implemented in the method: - create my own secret 163 * Key2 as an instance of a class implements PBEKey - spoil the key (set 164 * iteration count to 0, for example) - try to translate key - 165 * InvalidKeyException is expected. 166 * 167 * @return true if InvalidKeyException occurred; false - otherwise. 168 * @throws NoSuchAlgorithmException 169 * @throws InvalidKeySpecException 170 */ 171 public boolean translateSpoiledKey() throws NoSuchAlgorithmException, 172 InvalidKeySpecException { 173 // derive the key 174 SecretKey key1 = getMyOwnSecretKey(); 175 176 // spoil the key 177 ((MyPBKDF2SecretKey) key1).spoil(); 178 179 // translate key 180 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoToTest); 181 try { 182 SecretKey key2 = skf.translateKey(key1); 183 } catch (InvalidKeyException ike) { 184 // this is expected 185 return true; 186 } 187 188 return false; 189 } 190 191 /** 192 * Generate a PBKDF2 secret key using given algorithm. 193 * 194 * @param algoToDeriveKey PBKDF2 algorithm 195 * @return PBKDF2 secret key 196 * @throws NoSuchAlgorithmException 197 * @throws InvalidKeySpecException 198 */ 199 private SecretKey getSecretKeyForPBKDF2(String algoToDeriveKey) 200 throws NoSuchAlgorithmException, InvalidKeySpecException { 201 SecretKeyFactory skf = SecretKeyFactory.getInstance(algoToDeriveKey); 202 203 PBEKeySpec spec = new PBEKeySpec(PASS_PHRASE.toCharArray(), 204 this.salt, ITERATION_COUNT, KEY_SIZE); 205 206 return skf.generateSecret(spec); 207 } 208 209 /** 210 * Generate a secrete key as an instance of a class implements PBEKey. 211 * 212 * @return secrete key 213 * @throws InvalidKeySpecException 214 * @throws NoSuchAlgorithmException 215 */ 216 private SecretKey getMyOwnSecretKey() throws InvalidKeySpecException, 217 NoSuchAlgorithmException { 218 return new MyPBKDF2SecretKey(PASS_PHRASE, this.algoToTest, this.salt, 219 ITERATION_COUNT, KEY_SIZE); 220 } 221 } 222 223 /** 224 * An utility class to check the SecretKeyFactory.translateKey() method. 225 */ 226 class MyPBKDF2SecretKey implements PBEKey { 227 228 private final byte[] key; 229 private final byte[] salt; 230 private final String algorithm; 231 private final int keySize, keyLength; 232 private int itereationCount; 233 private final String pass; 234 235 @Override 236 public String getAlgorithm() { 237 return algorithm; 238 } 239 240 @Override 241 public String getFormat() { 242 return "RAW"; 243 } 244 245 @Override 246 public byte[] getEncoded() { 247 byte[] copy = new byte[keyLength]; 248 System.arraycopy(this.key, 0, copy, 0, keyLength); 249 return copy; 250 } 251 252 /** 253 * The key is generating by SecretKeyFactory and its value just copying in 254 * the key field of MySecretKey class. So, this is real key derived using 255 * the given algorithm. 256 * 257 * @param passPhrase some string intended to be a password 258 * @param algo PBKDF2 algorithm 259 * @param salt slat for PBKDF2 260 * @param iterationCount iteration count 261 * @param keySize key size in bits 262 * @throws InvalidKeySpecException 263 * @throws NoSuchAlgorithmException 264 */ 265 public MyPBKDF2SecretKey(String passPhrase, String algo, byte[] salt, 266 int iterationCount, int keySize) 267 throws InvalidKeySpecException, NoSuchAlgorithmException { 268 this.algorithm = algo; 269 this.salt = salt; 270 this.itereationCount = iterationCount; 271 this.keySize = keySize; 272 this.pass = passPhrase; 273 274 PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), 275 this.salt, iterationCount, this.keySize); 276 277 SecretKeyFactory keyFactory 278 = SecretKeyFactory.getInstance(algo); 279 280 SecretKey realKey = keyFactory.generateSecret(spec); 281 282 this.keyLength = realKey.getEncoded().length; 283 284 this.key = new byte[this.keyLength]; 285 System.arraycopy(realKey.getEncoded(), 0, this.key, 0, 286 this.keyLength); 287 } 288 289 @Override 290 public int getIterationCount() { 291 return itereationCount; 292 } 293 294 @Override 295 public byte[] getSalt() { 296 return salt; 297 } 298 299 @Override 300 public char[] getPassword() { 301 return this.pass.toCharArray(); 302 } 303 304 /** 305 * Spoil the generated key (before translation) to cause an 306 * InvalidKeyException 307 */ 308 public void spoil() { 309 this.itereationCount = -1; 310 } 311 312 }