1/*
2 * Copyright (c) 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 static java.lang.System.out;
25
26import java.lang.Integer;
27import java.lang.String;
28import java.lang.System;
29import java.security.AlgorithmParameters;
30import java.security.InvalidAlgorithmParameterException;
31import java.security.InvalidKeyException;
32import java.security.Key;
33import java.security.KeyPair;
34import java.security.NoSuchAlgorithmException;
35import java.security.KeyPairGenerator;
36import java.security.Provider;
37import java.security.Security;
38import java.security.spec.AlgorithmParameterSpec;
39import java.security.spec.InvalidKeySpecException;
40import java.util.Arrays;
41import java.util.HashMap;
42import java.util.Map;
43import java.util.Random;
44
45import javax.crypto.IllegalBlockSizeException;
46import javax.crypto.NoSuchPaddingException;
47import javax.crypto.SecretKey;
48import javax.crypto.Cipher;
49import javax.crypto.KeyGenerator;
50import javax.crypto.SecretKeyFactory;
51import javax.crypto.spec.PBEKeySpec;
52import javax.crypto.spec.PBEParameterSpec;
53
54/*
55 * @test
56 * @bug 8048599
57 * @summary  Tests for key wrap and unwrap operations
58 */
59
60public class TestCipherKeyWrapperTest {
61    private static final String SUN_JCE = "SunJCE";
62    // Blowfish Variable key length: 32 bits to 448 bits
63    private static final int BLOWFISH_MIN_KEYSIZE = 32;
64    private static final int BLOWFISH_MAX_KEYSIZE = 448;
65    private static final int LINIMITED_KEYSIZE = 128;
66    private static final String NOPADDING = "NoPaDDing";
67    private static final String[] PBE_ALGORITHM_AR = { "pbeWithMD5ANDdes",
68            "PBEWithMD5AndDES/CBC/PKCS5Padding", "PBEWithMD5AndTripleDES",
69            "PBEWithMD5AndTripleDES/CBC/PKCS5Padding", "PBEwithSHA1AndDESede",
70            "PBEwithSHA1AndDESede/CBC/PKCS5Padding", "PBEwithSHA1AndRC2_40",
71            "PBEwithSHA1Andrc2_40/CBC/PKCS5Padding", "PBEWithSHA1AndRC2_128",
72            "PBEWithSHA1andRC2_128/CBC/PKCS5Padding", "PBEWithSHA1AndRC4_40",
73            "PBEWithsha1AndRC4_40/ECB/NoPadding", "PBEWithSHA1AndRC4_128",
74            "pbeWithSHA1AndRC4_128/ECB/NoPadding", "PBEWithHmacSHA1AndAES_128",
75            "PBEWithHmacSHA224AndAES_128", "PBEWithHmacSHA256AndAES_128",
76            "PBEWithHmacSHA384AndAES_128", "PBEWithHmacSHA512AndAES_128",
77            "PBEWithHmacSHA1AndAES_256", "PBEWithHmacSHA224AndAES_256",
78            "PBEWithHmacSHA256AndAES_256", "PBEWithHmacSHA384AndAES_256",
79            "PBEWithHmacSHA512AndAES_256" };
80    private static final String[] MODEL_AR = { "ECb", "pCbC", "cbC", "cFB",
81            "cFB24", "cFB40", "OfB48", "OFB64" };
82    private static final String[] PADDING_AR = { NOPADDING, "PKCS5Padding" };
83
84    private enum AlgorithmWrapper {
85        AESWrap("AES", "AESWrap", -1),
86        AESWrap_128("AES", "AESWrap_128", 128),
87        AESWrap_192("AES", "AESWrap_192", 192),
88        AESWrap_256("AES", "AESWrap_256", 256),
89        DESedeWrap("desede", "DESedeWrap", -1),
90        NegtiveWrap("AES", "DESedeWrap", -1);
91
92        private final String algorithm;
93        private final String wrapper;
94        private final int keySize;
95
96        private AlgorithmWrapper(String algorithm, String wrapper, int kSize) {
97            this.algorithm = algorithm;
98            this.wrapper = wrapper;
99            this.keySize = kSize;
100        }
101
102        public String getAlgorithm() {
103            return algorithm;
104        }
105
106        public String getWrapper() {
107            return wrapper;
108        }
109
110        public int getKeySize() {
111            return keySize;
112        }
113
114    };
115
116    public static void main(String[] args) throws Exception {
117
118        TestCipherKeyWrapperTest test = new TestCipherKeyWrapperTest();
119        // AESWrap and DESedeWrap test
120        for (AlgorithmWrapper algoWrapper : AlgorithmWrapper.values()) {
121            String algo = algoWrapper.getAlgorithm();
122            String wrapper = algoWrapper.getWrapper();
123            try {
124                int keySize = algoWrapper.getKeySize();
125                // only run the tests on longer key lengths if unlimited
126                // version of JCE jurisdiction policy files are installed
127                if (!(Cipher.getMaxAllowedKeyLength(algo) == Integer.MAX_VALUE)
128                        && keySize > LINIMITED_KEYSIZE) {
129                    out.println(algo + " will not run if unlimited version of"
130                            + " JCE jurisdiction policy files are installed");
131                    continue;
132                }
133                test.wrapperAesDESedeKeyTest(algo, wrapper, keySize);
134                if (algoWrapper == AlgorithmWrapper.NegtiveWrap) {
135                    throw new RuntimeException("Expected not throw when algo"
136                            + " and wrapAlgo are not match:" + algo);
137                }
138            } catch (InvalidKeyException e) {
139                if (algoWrapper == AlgorithmWrapper.NegtiveWrap) {
140                    out.println("Expepted exception when algo"
141                            + " and wrapAlgo are not match:" + algo);
142                } else {
143                    throw e;
144                }
145            }
146        }
147        test.wrapperBlowfishKeyTest();
148        // PBE and public wrapper test.
149        String[] publicPrivateAlgos = new String[] { "DiffieHellman", "DSA",
150                "RSA" };
151        Provider provider = Security.getProvider(SUN_JCE);
152        if (provider == null) {
153            throw new RuntimeException("SUN_JCE provider not exist");
154        }
155
156        test.wrapperPBEKeyTest(provider);
157        // Public and private key wrap test
158        test.wrapperPublicPriviteKeyTest(provider, publicPrivateAlgos);
159    }
160
161    private void wrapperAesDESedeKeyTest(String algo, String wrapAlgo,
162            int keySize) throws InvalidKeyException, NoSuchAlgorithmException,
163            NoSuchPaddingException, IllegalBlockSizeException,
164            InvalidAlgorithmParameterException {
165        // Initialization
166        KeyGenerator kg = KeyGenerator.getInstance(algo);
167        if (keySize != -1) {
168            kg.init(keySize);
169        }
170        SecretKey key = kg.generateKey();
171        wrapTest(algo, wrapAlgo, key, key, Cipher.SECRET_KEY, false);
172    }
173
174    private void wrapperBlowfishKeyTest() throws InvalidKeyException,
175            NoSuchAlgorithmException, NoSuchPaddingException,
176            IllegalBlockSizeException, InvalidAlgorithmParameterException {
177        // how many kinds of padding mode
178        int padKinds;
179        // Keysize should be multiple of 8 bytes.
180        int KeyCutter = 8;
181        int kSize = BLOWFISH_MIN_KEYSIZE;
182        String algorithm = "Blowfish";
183        int maxAllowKeyLength = Cipher.getMaxAllowedKeyLength(algorithm);
184        boolean unLimitPolicy = maxAllowKeyLength == Integer.MAX_VALUE;
185        SecretKey key = null;
186        while (kSize <= BLOWFISH_MAX_KEYSIZE) {
187            for (String mode : MODEL_AR) {
188                // PKCS5padding is meaningful only for ECB, CBC, PCBC
189                if (mode.equalsIgnoreCase(MODEL_AR[0])
190                        || mode.equalsIgnoreCase(MODEL_AR[1])
191                        || mode.equalsIgnoreCase(MODEL_AR[2])) {
192                    padKinds = PADDING_AR.length;
193                } else {
194                    padKinds = 1;
195                }
196                // Initialization
197                KeyGenerator kg = KeyGenerator.getInstance(algorithm);
198                for (int k = 0; k < padKinds; k++) {
199                    String transformation = algorithm + "/" + mode + "/"
200                            + PADDING_AR[k];
201                    if (NOPADDING.equals(PADDING_AR[k]) && kSize % 64 != 0) {
202                        out.println(transformation
203                                + " will not run if input length not multiple"
204                                + " of 8 bytes when padding is " + NOPADDING);
205                        continue;
206                    }
207                    kg.init(kSize);
208                    key = kg.generateKey();
209                    // only run the tests on longer key lengths if unlimited
210                    // version of JCE jurisdiction policy files are installed
211                    if (!unLimitPolicy && kSize > LINIMITED_KEYSIZE) {
212                        out.println("keyStrength > 128 within " + algorithm
213                                + " will not run under global policy");
214                    } else {
215                        wrapTest(transformation, transformation, key, key,
216                                Cipher.SECRET_KEY, false);
217                    }
218                }
219            }
220            if (kSize <= LINIMITED_KEYSIZE) {
221                KeyCutter = 8;
222            } else {
223                KeyCutter = 48;
224            }
225            kSize += KeyCutter;
226        }
227    }
228
229    private void wrapperPBEKeyTest(Provider p) throws InvalidKeySpecException,
230            InvalidKeyException, NoSuchPaddingException,
231            IllegalBlockSizeException, InvalidAlgorithmParameterException,
232            NoSuchAlgorithmException {
233        for (String alg : PBE_ALGORITHM_AR) {
234            String baseAlgo = alg.split("/")[0].toUpperCase();
235            // only run the tests on longer key lengths if unlimited version
236            // of JCE jurisdiction policy files are installed
237
238            if (Cipher.getMaxAllowedKeyLength(alg) < Integer.MAX_VALUE
239                    && (baseAlgo.endsWith("TRIPLEDES") || alg
240                            .endsWith("AES_256"))) {
241                out.println("keyStrength > 128 within " + alg
242                        + " will not run under global policy");
243                continue;
244            }
245            SecretKeyFactory skf = SecretKeyFactory.getInstance(baseAlgo, p);
246            SecretKey key = skf.generateSecret(new PBEKeySpec("Secret Lover"
247                    .toCharArray()));
248            wrapTest(alg, alg, key, key, Cipher.SECRET_KEY, true);
249        }
250    }
251
252    private void wrapperPublicPriviteKeyTest(Provider p, String[] algorithms)
253            throws NoSuchAlgorithmException, InvalidKeyException,
254            NoSuchPaddingException, IllegalBlockSizeException,
255            InvalidAlgorithmParameterException {
256        for (String algo : algorithms) {
257            // Key pair generated
258            System.out.println("Generate key pair (algorithm: " + algo
259                    + ", provider: " + p.getName() + ")");
260            KeyPairGenerator kpg = KeyPairGenerator.getInstance(algo);
261            kpg.initialize(512);
262            KeyPair kp = kpg.genKeyPair();
263            // key generated
264            String algoWrap = "DES";
265            KeyGenerator kg = KeyGenerator.getInstance(algoWrap, p);
266            Key key = kg.generateKey();
267            wrapTest(algo, algoWrap, key, kp.getPrivate(), Cipher.PRIVATE_KEY,
268                    false);
269            wrapTest(algo, algoWrap, key, kp.getPublic(), Cipher.PUBLIC_KEY,
270                    false);
271        }
272    }
273
274    private void wrapTest(String transformation, String wrapAlgo, Key initKey,
275            Key wrapKey, int keyType, boolean isPBE)
276            throws NoSuchAlgorithmException, NoSuchPaddingException,
277            InvalidKeyException, IllegalBlockSizeException,
278            InvalidAlgorithmParameterException {
279        String algo = transformation.split("/")[0];
280        boolean isAESBlowfish = algo.indexOf("AES") != -1
281                || algo.indexOf("Blowfish") != -1;
282        AlgorithmParameters aps = null;
283        AlgorithmParameterSpec pbeParams = null;
284        if (isPBE) {
285            byte[] salt = new byte[8];
286            int iterCnt = 1000;
287            new Random().nextBytes(salt);
288            pbeParams = new PBEParameterSpec(salt, iterCnt);
289        }
290        // Wrap & UnWrap operation
291        Cipher wrapCI = Cipher.getInstance(wrapAlgo);
292        if (isPBE && !isAESBlowfish) {
293            wrapCI.init(Cipher.WRAP_MODE, initKey, pbeParams);
294        } else if (isAESBlowfish) {
295            wrapCI.init(Cipher.WRAP_MODE, initKey);
296            aps = wrapCI.getParameters();
297        } else {
298            wrapCI.init(Cipher.WRAP_MODE, initKey);
299        }
300        out.println("keysize : " + wrapKey.getEncoded().length);
301        byte[] keyWrapper = wrapCI.wrap(wrapKey);
302        if (isPBE && !isAESBlowfish) {
303            wrapCI.init(Cipher.UNWRAP_MODE, initKey, pbeParams);
304        } else if (isAESBlowfish) {
305            wrapCI.init(Cipher.UNWRAP_MODE, initKey, aps);
306        } else {
307            wrapCI.init(Cipher.UNWRAP_MODE, initKey);
308        }
309        Key unwrappedKey = wrapCI.unwrap(keyWrapper, algo, keyType);
310        // Comparison
311        if (!Arrays.equals(wrapKey.getEncoded(), unwrappedKey.getEncoded())) {
312            throw new RuntimeException("Comparation failed testing "
313                    + transformation + ":" + wrapAlgo + ":" + keyType);
314        }
315    }
316}
317