1/*
2 * Copyright (c) 2008, 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
24import java.io.*;
25import java.util.*;
26import java.security.*;
27import javax.crypto.*;
28import javax.crypto.spec.*;
29
30public class SecretKeysBasic extends PKCS11Test {
31
32    private static final char SEP = File.separatorChar;
33    private static char[] tokenPwd;
34    private static final char[] nssPwd =
35            new char[]{'t', 'e', 's', 't', '1', '2'};
36    private static final char[] solarisPwd =
37            new char[]{'p', 'i', 'n'};
38    private static SecretKey sk1;
39    private static SecretKey sk2;
40    private static SecretKey softkey;
41    private static KeyStore ks;
42    private static final String KS_TYPE = "PKCS11";
43    private static Provider provider;
44
45    public static void main(String[] args) throws Exception {
46        main(new SecretKeysBasic());
47    }
48
49    public void main(Provider p) throws Exception {
50        this.provider = p;
51
52        // create secret key
53        byte[] keyVal = new byte[16];
54        (new SecureRandom()).nextBytes(keyVal);
55        // NSS will throw CKR_HOST_MEMORY if calling C_DecryptInit w/
56        // (keyVal[0] == 0)
57        if (keyVal[0] == 0) {
58            keyVal[0] = 1;
59        }
60        softkey = new SecretKeySpec(keyVal, "AES");
61        dumpKey("softkey", softkey);
62
63        KeyGenerator kg = KeyGenerator.getInstance("DESede", provider);
64        sk1 = kg.generateKey();
65        dumpKey("skey1", sk1);
66        sk2 = kg.generateKey();
67        dumpKey("skey2", sk2);
68
69        String token = System.getProperty("TOKEN");
70
71        if (token == null || token.length() == 0) {
72            System.out.println("Error: missing TOKEN system property");
73            throw new Exception("token arg required");
74        }
75
76        if ("nss".equals(token)) {
77            tokenPwd = nssPwd;
78        } else if ("solaris".equals(token)) {
79            tokenPwd = solarisPwd;
80        }
81
82        int testnum = 1;
83        doTest();
84    }
85
86    private static boolean checkSecretKeyEntry(String alias,
87            SecretKey expected,
88            boolean saveBeforeCheck)
89            throws Exception {
90
91        // A bug in NSS 3.12 (Mozilla bug 471665) causes AES key lengths
92        // to be read incorrectly.  Checking for improper 16 byte length
93        // in key string.
94        if (isNSS(provider) && expected.getAlgorithm().equals("AES") &&
95                (getNSSVersion() >= 3.12 && getNSSVersion() <= 3.122)) {
96            System.out.println("NSS 3.12 bug returns incorrect AES key "+
97                    "length breaking key storage. Aborting...");
98            return true;
99        }
100
101        if (saveBeforeCheck) {
102            ks.setKeyEntry(alias, expected, null, null);
103        }
104        SecretKey result = (SecretKey) (ks.getKey(alias, null));
105        String keyEncFormat = result.getFormat();
106        if (keyEncFormat == null) {
107            // sensitive or un-extractable keys - verify by encrypt/decrypt
108            byte[] data = new byte[64];
109            Cipher c =
110                    Cipher.getInstance(result.getAlgorithm() + "/CBC/NoPadding",
111                    provider);
112            c.init(Cipher.ENCRYPT_MODE, expected);
113            byte[] encOut = c.doFinal(data);
114            c.init(Cipher.DECRYPT_MODE, result, c.getParameters());
115            byte[] decOut = c.doFinal(encOut);
116            if (!Arrays.equals(data, decOut)) {
117                return false;
118            }
119        } else if (keyEncFormat.toUpperCase().equals("RAW")) {
120            if (!Arrays.equals(result.getEncoded(), expected.getEncoded())) {
121                dumpKey("\texpected:", expected);
122                dumpKey("\treturns:", result);
123                return false;
124            }
125        }
126        return true;
127    }
128
129    private static void dumpKey(String info, SecretKey key) {
130        System.out.println(info + "> " + key);
131        System.out.println("\tALGO=" + key.getAlgorithm());
132        if (key.getFormat() != null) {
133            StringBuilder sb = new StringBuilder();
134            for (byte b : key.getEncoded()) {
135                sb.append(String.format("%02x", b & 0xff));
136            }
137            System.out.println("\t[" + key.getFormat() + "] VALUE=" + sb);
138        } else {
139            System.out.println("\tVALUE=n/a");
140        }
141    }
142
143    private static void doTest() throws Exception {
144        // Make sure both NSS libraries are the same version.
145        if (isNSS(provider) &&
146                (getLibsoftokn3Version() != getLibnss3Version())) {
147            System.out.println("libsoftokn3 and libnss3 versions do not match.  Aborting test...");
148            return;
149        }
150
151        if (ks == null) {
152            ks = KeyStore.getInstance(KS_TYPE, provider);
153            ks.load(null, tokenPwd);
154        }
155
156        System.out.println("Number of entries: " + ks.size());
157        if (ks.size() != 0) {
158            System.out.println("Deleting entries under aliases: ");
159            for (Enumeration<String> aliases = ks.aliases();
160                    aliases.hasMoreElements();) {
161                String alias = aliases.nextElement();
162                System.out.println("\t" + alias);
163                ks.deleteEntry(alias);
164            }
165        }
166
167        String alias = "testSKey";
168
169        boolean testResult = checkSecretKeyEntry(alias, softkey, true);
170        if (!testResult) {
171            System.out.println("FAILURE: setKey() w/ softSecretKey failed");
172        }
173
174        if (!checkSecretKeyEntry(alias, sk1, true)) {
175            testResult = false;
176            System.out.println("FAILURE: setKey() w/ skey1 failed");
177        }
178        if (!checkSecretKeyEntry(alias, sk2, true)) {
179            testResult = false;
180            System.out.println("FAILURE: setKey() w/ skey2 failed");
181        }
182
183        ks.store(null);
184        System.out.println("Reloading keystore...");
185
186        ks.load(null, "whatever".toCharArray());
187        if (ks.size() != 1) {
188            System.out.println("FAILURE: reload#1 ks.size() != 1");
189        }
190        if (!checkSecretKeyEntry(alias, sk2, false)) {
191            testResult = false;
192            System.out.println("FAILURE: reload#1 ks entry check failed");
193        }
194
195        ks.deleteEntry(alias);
196        ks.store(null);
197
198        System.out.println("Reloading keystore...");
199        ks.load(null, "whatever".toCharArray());
200        if (ks.size() != 0) {
201            testResult = false;
202            System.out.println("FAILURE: reload#2 ks.size() != 0");
203        }
204        if (!testResult) {
205            throw new Exception("One or more test failed!");
206        }
207    }
208}
209