J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2005-2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 20 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 21 | * have any questions. |
| 22 | */ |
| 23 | |
| 24 | /* |
| 25 | * @test |
| 26 | * @bug 5083253 |
| 27 | * @summary Verify that PBKDF2WithHmacSHA1 SecretKeyFactory works. |
| 28 | * @author Valerie Peng |
| 29 | */ |
| 30 | import java.io.*; |
| 31 | import java.math.BigInteger; |
| 32 | import java.security.*; |
| 33 | import javax.crypto.*; |
| 34 | import javax.crypto.spec.*; |
| 35 | import javax.crypto.interfaces.*; |
| 36 | import java.util.*; |
| 37 | |
| 38 | public class PBKDF2HmacSHA1FactoryTest { |
| 39 | |
| 40 | private static final String ALGO = "PBKDF2WithHmacSHA1"; |
| 41 | static final int[] KEY_SIZES = { 128, 256 }; // in bits |
| 42 | |
| 43 | /* |
| 44 | * Use test vectors found in the appendix B of RFC 3962 |
| 45 | * "Advanced Encryption Standard (AES) Encryption for Kerberos 5" |
| 46 | */ |
| 47 | private static final TestVector[] TEST_VECTORS = { |
| 48 | new TestVector(1, "password", "ATHENA.MIT.EDUraeburn", |
| 49 | "cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837"), |
| 50 | new TestVector(2, "password", "ATHENA.MIT.EDUraeburn", |
| 51 | "01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"), |
| 52 | new TestVector(1200, "password", "ATHENA.MIT.EDUraeburn", |
| 53 | "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"), |
| 54 | new TestVector(5, "password", fromHexString("1234567878563412"), |
| 55 | "d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"), |
| 56 | new TestVector(1200, |
| 57 | "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", |
| 58 | "pass phrase equals block size", |
| 59 | "139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1"), |
| 60 | new TestVector(1200, |
| 61 | "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", |
| 62 | "pass phrase exceeds block size", |
| 63 | "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a"), |
| 64 | new TestVector(50, fromHexString("f09d849e"), |
| 65 | "EXAMPLE.COMpianist", |
| 66 | "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52"), |
| 67 | }; |
| 68 | |
| 69 | private static void test() throws Exception { |
| 70 | SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGO, "SunJCE"); |
| 71 | |
| 72 | for (int i = 0; i < TEST_VECTORS.length; i++) { |
| 73 | System.out.println("=>Testing vector#" + (i+1)); |
| 74 | TestVector tv = TEST_VECTORS[i]; |
| 75 | for (int j = 0; j < KEY_SIZES.length; j++) { |
| 76 | PBEKeySpec keySpec = tv.keySpecs[j]; |
| 77 | PBEKey key = (PBEKey) skf.generateSecret(keySpec); |
| 78 | byte[] derivedKey = key.getEncoded(); |
| 79 | if (!(key.getFormat().equalsIgnoreCase("RAW"))) { |
| 80 | throw new Exception("Wrong format for derived key"); |
| 81 | } |
| 82 | if (derivedKey.length != KEY_SIZES[j]/8) { |
| 83 | throw new Exception("Wrong length for derived key"); |
| 84 | } |
| 85 | // Test generateSecret(...) using test vectors |
| 86 | if (!tv.expectedVals[j].equals(toHexString(derivedKey))) { |
| 87 | System.out.println("got: " + toHexString(derivedKey)); |
| 88 | System.out.println("expected: " + tv.expectedVals[j]); |
| 89 | throw new Exception("Wrong value for derived key"); |
| 90 | } |
| 91 | |
| 92 | // Test getKeySpec(...) |
| 93 | PBEKeySpec keySpec2 = (PBEKeySpec) |
| 94 | skf.getKeySpec(key, PBEKeySpec.class); |
| 95 | if (!isEqual(keySpec, keySpec2)) { |
| 96 | throw new Exception("Wrong derived keySpec"); |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | private static boolean isEqual(PBEKeySpec spec1, PBEKeySpec spec2) { |
| 102 | if ((spec1 == null) || (spec2 == null)) return false; |
| 103 | if (Arrays.equals(spec1.getPassword(), spec2.getPassword()) && |
| 104 | Arrays.equals(spec1.getSalt(), spec2.getSalt()) && |
| 105 | spec1.getIterationCount() == spec2.getIterationCount() && |
| 106 | spec1.getKeyLength() == spec2.getKeyLength()) { |
| 107 | return true; |
| 108 | } |
| 109 | return false; |
| 110 | } |
| 111 | |
| 112 | private static String toHexString(byte[] bytes) { |
| 113 | String mapping = "0123456789abcdef"; |
| 114 | StringBuilder sb = new StringBuilder(bytes.length*2); |
| 115 | for (int i = 0; i < bytes.length; i++) { |
| 116 | int low = bytes[i] & 0x0f; |
| 117 | int high = ((bytes[i] >> 4) & 0x0f); |
| 118 | char[] res = new char[2]; |
| 119 | res[0] = mapping.charAt(high); |
| 120 | res[1] = mapping.charAt(low); |
| 121 | sb.append(res); |
| 122 | } |
| 123 | return sb.toString(); |
| 124 | } |
| 125 | private static byte[] fromHexString(String value) { |
| 126 | byte[] bytes = new byte[value.length()/2]; |
| 127 | String mapping = "0123456789abcdef"; |
| 128 | StringBuilder sb = new StringBuilder(bytes.length*2); |
| 129 | for (int i = 0; i < bytes.length; i++) { |
| 130 | String high = value.substring(2*i, 2*i+1); |
| 131 | String low = value.substring(2*i+1, 2*i+2); |
| 132 | bytes[i] = (byte) ((mapping.indexOf(high) << 4) + |
| 133 | mapping.indexOf(low)); |
| 134 | } |
| 135 | return bytes; |
| 136 | } |
| 137 | public static void main (String[] args) throws Exception { |
| 138 | test(); |
| 139 | System.out.println("Test Passed!"); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | class TestVector { |
| 144 | |
| 145 | PBEKeySpec[] keySpecs; |
| 146 | String[] expectedVals; |
| 147 | |
| 148 | TestVector(int iterCount, String password, String saltString, |
| 149 | String expectedVal) { |
| 150 | try { |
| 151 | init(iterCount, password, saltString.getBytes("UTF-8"), |
| 152 | expectedVal); |
| 153 | } catch (Exception ex) { |
| 154 | keySpecs = null; |
| 155 | expectedVals = null; |
| 156 | } |
| 157 | } |
| 158 | TestVector(int iterCount, byte[] passwordUTF8, String saltString, |
| 159 | String expectedVal) { |
| 160 | try { |
| 161 | init(iterCount, new String(passwordUTF8, "UTF-8"), |
| 162 | saltString.getBytes("UTF-8"), expectedVal); |
| 163 | } catch (Exception ex) { |
| 164 | keySpecs = null; |
| 165 | expectedVals = null; |
| 166 | } |
| 167 | } |
| 168 | TestVector(int iterCount, String password, byte[] salt, |
| 169 | String expectedVal) { |
| 170 | init(iterCount, password, salt, expectedVal); |
| 171 | } |
| 172 | private void init(int iterCount, String password, byte[] salt, |
| 173 | String expectedVal) { |
| 174 | try { |
| 175 | int numOfKeySizes = PBKDF2HmacSHA1FactoryTest.KEY_SIZES.length; |
| 176 | keySpecs = new PBEKeySpec[numOfKeySizes]; |
| 177 | expectedVals = new String[numOfKeySizes]; |
| 178 | for (int i = 0; i < numOfKeySizes; i++) { |
| 179 | int keySize = PBKDF2HmacSHA1FactoryTest.KEY_SIZES[i]; |
| 180 | keySpecs[i] = new PBEKeySpec(password.toCharArray(), |
| 181 | salt, iterCount, keySize); |
| 182 | expectedVals[i] = expectedVal.substring(0, keySize/4); |
| 183 | } |
| 184 | } catch (Exception ex) { |
| 185 | keySpecs = null; |
| 186 | expectedVals = null; |
| 187 | } |
| 188 | } |
| 189 | } |