8048820: Implement tests for SecretKeyFactory
Reviewed-by: xuelei
Contributed-by: Zaiyao Liu <zaiyao.liu@oracle.com>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/crypto/SecretKeyFactory/PBKDF2TranslateTest.java Mon May 18 02:11:39 2015 +0000
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Random;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.interfaces.PBEKey;
+import javax.crypto.spec.PBEKeySpec;
+import javax.security.auth.DestroyFailedException;
+
+import static java.lang.System.out;
+
+/*
+ * @test
+ * @bug 8048820
+ * @summary The test verifies if the SecretKeyFactory.translateKey() method
+ * works as expected for the PBKDF2 algorithms.
+ */
+
+public class PBKDF2TranslateTest {
+
+ private static final String PASS_PHRASE = "some hidden string";
+ private static final int ITERATION_COUNT = 1000;
+ private static final int KEY_SIZE = 128;
+ private static final String[] TEST_ALGOS = { "PBKDF2WithHmacSHA1",
+ "PBKDF2WithHmacSHA224", "PBKDF2WithHmacSHA256",
+ "PBKDF2WithHmacSHA384", "PBKDF2WithHmacSHA512" };
+ private final String algoForTest;
+
+ public static void main(String[] args) throws Exception {
+ for (String algo : TEST_ALGOS) {
+ PBKDF2TranslateTest theTest = new PBKDF2TranslateTest(algo);
+ byte[] salt = new byte[8];
+ new Random().nextBytes(salt);
+ theTest.testMyOwnSecretKey(salt);
+ theTest.generateAndTranslateKey(salt);
+ theTest.translateSpoiledKey(salt);
+ }
+ }
+
+ public PBKDF2TranslateTest(String algo) {
+ algoForTest = algo;
+ }
+
+ /**
+ * The test case scenario implemented in the method: - derive PBKDF2 key
+ * using the given algorithm; - translate the key - check if the translated
+ * and original keys have the same key value.
+ *
+ */
+ public void generateAndTranslateKey(byte[] salt)
+ throws NoSuchAlgorithmException, InvalidKeySpecException,
+ InvalidKeyException {
+ // derive PBKDF2 key
+ SecretKey key1 = getSecretKeyForPBKDF2(algoForTest, salt);
+
+ // translate key
+ SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest);
+ SecretKey key2 = skf.translateKey(key1);
+
+ // Check if it still the same after translation
+ if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) {
+ System.out.println("Key1=" + new String(key1.getEncoded())
+ + " key2=" + new String(key2.getEncoded()) + " salt="
+ + new String(salt));
+ throw new RuntimeException(
+ "generateAndTranslateKey test case failed: the key1 and"
+ + " key2 values in its primary encoding format are"
+ + " not the same for " + algoForTest
+ + " algorithm.");
+ }
+ }
+
+ /**
+ * The test case scenario implemented in the method: - derive Key1 for the
+ * given PBKDF2 algorithm - create my own secret Key2 as an instance of a
+ * class implements PBEKey - translate Key2 - check if the key value of the
+ * translated key and Key1 are the same.
+ */
+ private void testMyOwnSecretKey(byte[] salt)
+ throws NoSuchAlgorithmException, InvalidKeySpecException,
+ InvalidKeyException {
+ SecretKey key1 = getSecretKeyForPBKDF2(algoForTest, salt);
+ SecretKey key2 = getMyOwnSecretKey(salt);
+
+ // Is it actually the same?
+ if (!Arrays.equals(key1.getEncoded(), key2.getEncoded())) {
+ throw new RuntimeException(
+ "We shouldn't be here. The key1 and key2 values in its"
+ + " primary encoding format have to be the same!");
+ }
+
+ // translate key
+ SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest);
+ SecretKey key3 = skf.translateKey(key2);
+
+ // Check if it still the same after translation
+ if (!Arrays.equals(key1.getEncoded(), key3.getEncoded())) {
+ System.out.println("Key1=" + new String(key1.getEncoded())
+ + " key3=" + new String(key3.getEncoded()) + " salt="
+ + new String(salt));
+ throw new RuntimeException(
+ "testMyOwnSecretKey test case failed: the key1 and key3"
+ + " values in its primary encoding format are not"
+ + " the same for " + algoForTest + " algorithm.");
+ }
+
+ }
+
+ /**
+ * The test case scenario implemented in the method: - create my own secret
+ * Key2 as an instance of a class implements PBEKey - spoil the key (set
+ * iteration count to 0, for example) - try to translate key -
+ * InvalidKeyException is expected.
+ */
+ public void translateSpoiledKey(byte[] salt)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+ // derive the key
+ SecretKey key1 = getMyOwnSecretKey(salt);
+
+ // spoil the key
+ ((MyPBKDF2SecretKey) key1).spoil();
+
+ // translate key
+ SecretKeyFactory skf = SecretKeyFactory.getInstance(algoForTest);
+ try {
+ skf.translateKey(key1);
+ throw new RuntimeException(
+ "translateSpoiledKey test case failed, should throw"
+ + " InvalidKeyException when spoil the key");
+ } catch (InvalidKeyException ike) {
+ out.println("Expected exception when spoil the key");
+ }
+
+ }
+
+ /**
+ * Generate a PBKDF2 secret key using given algorithm.
+ */
+ private SecretKey getSecretKeyForPBKDF2(String algoDeriveKey, byte[] salt)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+
+ SecretKeyFactory skf = SecretKeyFactory.getInstance(algoDeriveKey);
+ PBEKeySpec spec = new PBEKeySpec(PASS_PHRASE.toCharArray(), salt,
+ ITERATION_COUNT, KEY_SIZE);
+
+ return skf.generateSecret(spec);
+ }
+
+ /**
+ * Generate a secrete key as an instance of a class implements PBEKey.
+ */
+ private SecretKey getMyOwnSecretKey(byte[] salt)
+ throws InvalidKeySpecException, NoSuchAlgorithmException {
+ return new MyPBKDF2SecretKey(PASS_PHRASE, algoForTest, salt,
+ ITERATION_COUNT, KEY_SIZE);
+ }
+
+ /**
+ * An utility class to check the SecretKeyFactory.translateKey() method.
+ */
+ class MyPBKDF2SecretKey implements PBEKey {
+ private final byte[] key;
+ private final byte[] salt;
+ private final String algorithm;
+ private final int keyLength;
+ private final String pass;
+ private int itereationCount;
+
+ /**
+ * The key is generating by SecretKeyFactory and its value just copying
+ * in the key field of MySecretKey class. So, this is real key derived
+ * using the given algo.
+ */
+ public MyPBKDF2SecretKey(String passPhrase, String algo, byte[] salt1,
+ int iterationCount, int keySize)
+ throws InvalidKeySpecException, NoSuchAlgorithmException {
+ algorithm = algo;
+ salt = salt1;
+ itereationCount = iterationCount;
+ pass = passPhrase;
+
+ PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt,
+ iterationCount, keySize);
+
+ SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algo);
+
+ SecretKey realKey = keyFactory.generateSecret(spec);
+
+ keyLength = realKey.getEncoded().length;
+
+ key = new byte[keyLength];
+ System.arraycopy(realKey.getEncoded(), 0, key, 0, keyLength);
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ byte[] copy = new byte[keyLength];
+ System.arraycopy(key, 0, copy, 0, keyLength);
+ return copy;
+ }
+
+ @Override
+ public int getIterationCount() {
+ return itereationCount;
+ }
+
+ @Override
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ @Override
+ public char[] getPassword() {
+ return pass.toCharArray();
+ }
+
+ /**
+ * Spoil the generated key (before translation) to cause an
+ * InvalidKeyException
+ */
+ public void spoil() {
+ itereationCount = -1;
+ }
+
+ @Override
+ public void destroy() throws DestroyFailedException {
+ }
+
+ @Override
+ public boolean isDestroyed() {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/crypto/SecretKeyFactory/SecKFTranslateTest.java Mon May 18 02:11:39 2015 +0000
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.security.auth.DestroyFailedException;
+
+/*
+ * @test
+ * @bug 8048820
+ * @summary The test verifies SecretKey values should remain the same after
+ * translation with SecretKeyFactory.translateKey().
+ */
+
+public class SecKFTranslateTest {
+ private static final String SUN_JCE = "SunJCE";
+
+ public static void main(String[] args) throws Exception {
+
+ SecKFTranslateTest test = new SecKFTranslateTest();
+ test.run();
+ }
+
+ private void run() throws Exception {
+
+ for (Algorithm algorithm : Algorithm.values()) {
+ runTest(algorithm);
+ }
+ }
+
+ private void runTest(Algorithm algo) throws NoSuchAlgorithmException,
+ NoSuchProviderException, InvalidKeyException,
+ InvalidKeySpecException, NoSuchPaddingException,
+ InvalidAlgorithmParameterException, ShortBufferException,
+ IllegalBlockSizeException, BadPaddingException {
+ AlgorithmParameterSpec[] aps = new AlgorithmParameterSpec[1];
+ byte[] plainText = new byte[800];
+
+ SecretKey key1 = algo.intSecurityKey(aps);
+ Random random = new Random();
+ // Initialization
+ SecretKeyFactory skf = SecretKeyFactory.getInstance(algo.toString(),
+ SUN_JCE);
+
+ random.nextBytes(plainText);
+ Cipher ci = Cipher.getInstance(algo.toString(), SUN_JCE);
+ // Encryption
+ ci.init(Cipher.ENCRYPT_MODE, key1, aps[0]);
+ byte[] cipherText = new byte[ci.getOutputSize(plainText.length)];
+ int offset = ci.update(plainText, 0, plainText.length, cipherText, 0);
+ ci.doFinal(cipherText, offset);
+ // translate key
+ SecretKey key2 = skf.translateKey(key1);
+
+ // Decryption
+ ci.init(Cipher.DECRYPT_MODE, key2, aps[0]);
+ byte[] recoveredText = new byte[ci.getOutputSize(plainText.length)];
+ ci.doFinal(cipherText, 0, cipherText.length, recoveredText);
+
+ // Comparison
+ if (!Arrays.equals(plainText, recoveredText)) {
+ System.out.println("Key1:" + new String(key1.getEncoded())
+ + " Key2:" + new String(key2.getEncoded()));
+ throw new RuntimeException("Testing translate key failed with "
+ + algo);
+ }
+
+ }
+}
+
+class MyOwnSecKey implements SecretKey {
+
+ private static final String DEFAULT_ALGO = "PBEWithMD5AndDES";
+ private final byte[] key;
+ private final String algorithm;
+ private final int keySize;
+
+ public MyOwnSecKey(byte[] key1, int offset, String algo)
+ throws InvalidKeyException {
+ algorithm = algo;
+ if (algo.equalsIgnoreCase("DES")) {
+ keySize = 8;
+ } else if (algo.equalsIgnoreCase("DESede")) {
+ keySize = 24;
+ } else {
+ throw new InvalidKeyException(
+ "Inappropriate key format and algorithm");
+ }
+
+ if (key1 == null || key1.length - offset < keySize) {
+ throw new InvalidKeyException("Wrong key size");
+ }
+ key = new byte[keySize];
+ System.arraycopy(key, offset, key, 0, keySize);
+ }
+
+ public MyOwnSecKey(PBEKeySpec ks) throws InvalidKeySpecException {
+ algorithm = DEFAULT_ALGO;
+ key = new String(ks.getPassword()).getBytes();
+ keySize = key.length;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ byte[] copy = new byte[keySize];
+ System.arraycopy(key, 0, copy, 0, keySize);
+ return copy;
+ }
+
+ @Override
+ public void destroy() throws DestroyFailedException {
+ }
+
+ @Override
+ public boolean isDestroyed() {
+ return false;
+ }
+}
+
+enum Algorithm {
+ DES {
+ @Override
+ SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
+ throws InvalidKeyException {
+ int keyLength = 8;
+ byte[] keyVal = new byte[keyLength];
+ new SecureRandom().nextBytes(keyVal);
+ SecretKey key1 = new MyOwnSecKey(keyVal, 0, this.toString());
+ return key1;
+ }
+ },
+ DESEDE {
+ @Override
+ SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
+ throws InvalidKeyException {
+ int keyLength = 24;
+ byte[] keyVal = new byte[keyLength];
+ new SecureRandom().nextBytes(keyVal);
+ SecretKey key1 = new MyOwnSecKey(keyVal, 0, this.toString());
+ return key1;
+ }
+ },
+ PBEWithMD5ANDdes {
+ @Override
+ SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
+ throws InvalidKeySpecException {
+ byte[] salt = new byte[8];
+ int iterCnt = 6;
+ new Random().nextBytes(salt);
+ spec[0] = new PBEParameterSpec(salt, iterCnt);
+ PBEKeySpec pbeKS = new PBEKeySpec(
+ new String("So far so good").toCharArray());
+ SecretKey key1 = new MyOwnSecKey(pbeKS);
+ return key1;
+ }
+ };
+ abstract SecretKey intSecurityKey(AlgorithmParameterSpec[] spec)
+ throws InvalidKeyException, InvalidKeySpecException;
+}