1/*
2 * Copyright (c) 2003, 2011, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.pkcs11;
27
28import java.util.*;
29
30import java.security.*;
31import java.security.spec.*;
32
33import javax.crypto.*;
34import javax.crypto.spec.*;
35
36import static sun.security.pkcs11.TemplateManager.*;
37import sun.security.pkcs11.wrapper.*;
38import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
39
40/**
41 * SecretKeyFactory implementation class. This class currently supports
42 * DES, DESede, AES, ARCFOUR, and Blowfish.
43 *
44 * @author  Andreas Sterbenz
45 * @since   1.5
46 */
47final class P11SecretKeyFactory extends SecretKeyFactorySpi {
48
49    // token instance
50    private final Token token;
51
52    // algorithm name
53    private final String algorithm;
54
55    P11SecretKeyFactory(Token token, String algorithm) {
56        super();
57        this.token = token;
58        this.algorithm = algorithm;
59    }
60
61    private static final Map<String,Long> keyTypes;
62
63    static {
64        keyTypes = new HashMap<String,Long>();
65        addKeyType("RC4",      CKK_RC4);
66        addKeyType("ARCFOUR",  CKK_RC4);
67        addKeyType("DES",      CKK_DES);
68        addKeyType("DESede",   CKK_DES3);
69        addKeyType("AES",      CKK_AES);
70        addKeyType("Blowfish", CKK_BLOWFISH);
71
72        // we don't implement RC2 or IDEA, but we want to be able to generate
73        // keys for those SSL/TLS ciphersuites.
74        addKeyType("RC2",      CKK_RC2);
75        addKeyType("IDEA",     CKK_IDEA);
76
77        addKeyType("TlsPremasterSecret",    PCKK_TLSPREMASTER);
78        addKeyType("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER);
79        addKeyType("TlsMasterSecret",       PCKK_TLSMASTER);
80        addKeyType("Generic",               CKK_GENERIC_SECRET);
81    }
82
83    private static void addKeyType(String name, long id) {
84        Long l = Long.valueOf(id);
85        keyTypes.put(name, l);
86        keyTypes.put(name.toUpperCase(Locale.ENGLISH), l);
87    }
88
89    static long getKeyType(String algorithm) {
90        Long l = keyTypes.get(algorithm);
91        if (l == null) {
92            algorithm = algorithm.toUpperCase(Locale.ENGLISH);
93            l = keyTypes.get(algorithm);
94            if (l == null) {
95                if (algorithm.startsWith("HMAC")) {
96                    return PCKK_HMAC;
97                } else if (algorithm.startsWith("SSLMAC")) {
98                    return PCKK_SSLMAC;
99                }
100            }
101        }
102        return (l != null) ? l.longValue() : -1;
103    }
104
105    /**
106     * Convert an arbitrary key of algorithm into a P11Key of provider.
107     * Used in engineTranslateKey(), P11Cipher.init(), and P11Mac.init().
108     */
109    static P11Key convertKey(Token token, Key key, String algo)
110            throws InvalidKeyException {
111        return convertKey(token, key, algo, null);
112    }
113
114    /**
115     * Convert an arbitrary key of algorithm w/ custom attributes into a
116     * P11Key of provider.
117     * Used in P11KeyStore.storeSkey.
118     */
119    static P11Key convertKey(Token token, Key key, String algo,
120            CK_ATTRIBUTE[] extraAttrs)
121            throws InvalidKeyException {
122        token.ensureValid();
123        if (key == null) {
124            throw new InvalidKeyException("Key must not be null");
125        }
126        if (key instanceof SecretKey == false) {
127            throw new InvalidKeyException("Key must be a SecretKey");
128        }
129        long algoType;
130        if (algo == null) {
131            algo = key.getAlgorithm();
132            algoType = getKeyType(algo);
133        } else {
134            algoType = getKeyType(algo);
135            long keyAlgorithmType = getKeyType(key.getAlgorithm());
136            if (algoType != keyAlgorithmType) {
137                if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) {
138                    // ignore key algorithm for MACs
139                } else {
140                    throw new InvalidKeyException
141                            ("Key algorithm must be " + algo);
142                }
143            }
144        }
145        if (key instanceof P11Key) {
146            P11Key p11Key = (P11Key)key;
147            if (p11Key.token == token) {
148                if (extraAttrs != null) {
149                    Session session = null;
150                    try {
151                        session = token.getObjSession();
152                        long newKeyID = token.p11.C_CopyObject(session.id(),
153                                p11Key.keyID, extraAttrs);
154                        p11Key = (P11Key) (P11Key.secretKey(session,
155                                newKeyID, p11Key.algorithm, p11Key.keyLength,
156                                extraAttrs));
157                    } catch (PKCS11Exception p11e) {
158                        throw new InvalidKeyException
159                                ("Cannot duplicate the PKCS11 key", p11e);
160                    } finally {
161                        token.releaseSession(session);
162                    }
163                }
164                return p11Key;
165            }
166        }
167        P11Key p11Key = token.secretCache.get(key);
168        if (p11Key != null) {
169            return p11Key;
170        }
171        if ("RAW".equalsIgnoreCase(key.getFormat()) == false) {
172            throw new InvalidKeyException("Encoded format must be RAW");
173        }
174        byte[] encoded = key.getEncoded();
175        p11Key = createKey(token, encoded, algo, algoType, extraAttrs);
176        token.secretCache.put(key, p11Key);
177        return p11Key;
178    }
179
180    static void fixDESParity(byte[] key, int offset) {
181        for (int i = 0; i < 8; i++) {
182            int b = key[offset] & 0xfe;
183            b |= (Integer.bitCount(b) & 1) ^ 1;
184            key[offset++] = (byte)b;
185        }
186    }
187
188    private static P11Key createKey(Token token, byte[] encoded,
189            String algorithm, long keyType, CK_ATTRIBUTE[] extraAttrs)
190            throws InvalidKeyException {
191        int n = encoded.length << 3;
192        int keyLength = n;
193        try {
194            switch ((int)keyType) {
195                case (int)CKK_DES:
196                    keyLength =
197                        P11KeyGenerator.checkKeySize(CKM_DES_KEY_GEN, n, token);
198                    fixDESParity(encoded, 0);
199                    break;
200                case (int)CKK_DES3:
201                    keyLength =
202                        P11KeyGenerator.checkKeySize(CKM_DES3_KEY_GEN, n, token);
203                    fixDESParity(encoded, 0);
204                    fixDESParity(encoded, 8);
205                    if (keyLength == 112) {
206                        keyType = CKK_DES2;
207                    } else {
208                        keyType = CKK_DES3;
209                        fixDESParity(encoded, 16);
210                    }
211                    break;
212                case (int)CKK_AES:
213                    keyLength =
214                        P11KeyGenerator.checkKeySize(CKM_AES_KEY_GEN, n, token);
215                    break;
216                case (int)CKK_RC4:
217                    keyLength =
218                        P11KeyGenerator.checkKeySize(CKM_RC4_KEY_GEN, n, token);
219                    break;
220                case (int)CKK_BLOWFISH:
221                    keyLength =
222                        P11KeyGenerator.checkKeySize(CKM_BLOWFISH_KEY_GEN, n,
223                        token);
224                    break;
225                case (int)CKK_GENERIC_SECRET:
226                case (int)PCKK_TLSPREMASTER:
227                case (int)PCKK_TLSRSAPREMASTER:
228                case (int)PCKK_TLSMASTER:
229                    keyType = CKK_GENERIC_SECRET;
230                    break;
231                case (int)PCKK_SSLMAC:
232                case (int)PCKK_HMAC:
233                    if (n == 0) {
234                        throw new InvalidKeyException
235                                ("MAC keys must not be empty");
236                    }
237                    keyType = CKK_GENERIC_SECRET;
238                    break;
239                default:
240                    throw new InvalidKeyException("Unknown algorithm " +
241                            algorithm);
242            }
243        } catch (InvalidAlgorithmParameterException iape) {
244            throw new InvalidKeyException("Invalid key for " + algorithm,
245                    iape);
246        } catch (ProviderException pe) {
247            throw new InvalidKeyException("Could not create key", pe);
248        }
249        Session session = null;
250        try {
251            CK_ATTRIBUTE[] attributes;
252            if (extraAttrs != null) {
253                attributes = new CK_ATTRIBUTE[3 + extraAttrs.length];
254                System.arraycopy(extraAttrs, 0, attributes, 3,
255                        extraAttrs.length);
256            } else {
257                attributes = new CK_ATTRIBUTE[3];
258            }
259            attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
260            attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType);
261            attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded);
262            attributes = token.getAttributes
263                (O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
264            session = token.getObjSession();
265            long keyID = token.p11.C_CreateObject(session.id(), attributes);
266            P11Key p11Key = (P11Key)P11Key.secretKey
267                (session, keyID, algorithm, keyLength, attributes);
268            return p11Key;
269        } catch (PKCS11Exception e) {
270            throw new InvalidKeyException("Could not create key", e);
271        } finally {
272            token.releaseSession(session);
273        }
274    }
275
276    // see JCE spec
277    protected SecretKey engineGenerateSecret(KeySpec keySpec)
278            throws InvalidKeySpecException {
279        token.ensureValid();
280        if (keySpec == null) {
281            throw new InvalidKeySpecException("KeySpec must not be null");
282        }
283        if (keySpec instanceof SecretKeySpec) {
284            try {
285                Key key = convertKey(token, (SecretKey)keySpec, algorithm);
286                return (SecretKey)key;
287            } catch (InvalidKeyException e) {
288                throw new InvalidKeySpecException(e);
289            }
290        } else if (algorithm.equalsIgnoreCase("DES")) {
291            if (keySpec instanceof DESKeySpec) {
292                byte[] keyBytes = ((DESKeySpec)keySpec).getKey();
293                keySpec = new SecretKeySpec(keyBytes, "DES");
294                return engineGenerateSecret(keySpec);
295            }
296        } else if (algorithm.equalsIgnoreCase("DESede")) {
297            if (keySpec instanceof DESedeKeySpec) {
298                byte[] keyBytes = ((DESedeKeySpec)keySpec).getKey();
299                keySpec = new SecretKeySpec(keyBytes, "DESede");
300                return engineGenerateSecret(keySpec);
301            }
302        }
303        throw new InvalidKeySpecException
304                ("Unsupported spec: " + keySpec.getClass().getName());
305    }
306
307    private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException {
308        try {
309            key = engineTranslateKey(key);
310            if ("RAW".equalsIgnoreCase(key.getFormat()) == false) {
311                throw new InvalidKeySpecException
312                    ("Could not obtain key bytes");
313            }
314            byte[] k = key.getEncoded();
315            return k;
316        } catch (InvalidKeyException e) {
317            throw new InvalidKeySpecException(e);
318        }
319    }
320
321    // see JCE spec
322    protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpec)
323            throws InvalidKeySpecException {
324        token.ensureValid();
325        if ((key == null) || (keySpec == null)) {
326            throw new InvalidKeySpecException
327                ("key and keySpec must not be null");
328        }
329        if (SecretKeySpec.class.isAssignableFrom(keySpec)) {
330            return new SecretKeySpec(getKeyBytes(key), algorithm);
331        } else if (algorithm.equalsIgnoreCase("DES")) {
332            try {
333                if (DESKeySpec.class.isAssignableFrom(keySpec)) {
334                    return new DESKeySpec(getKeyBytes(key));
335                }
336            } catch (InvalidKeyException e) {
337                throw new InvalidKeySpecException(e);
338            }
339        } else if (algorithm.equalsIgnoreCase("DESede")) {
340            try {
341                if (DESedeKeySpec.class.isAssignableFrom(keySpec)) {
342                    return new DESedeKeySpec(getKeyBytes(key));
343                }
344            } catch (InvalidKeyException e) {
345                throw new InvalidKeySpecException(e);
346            }
347        }
348        throw new InvalidKeySpecException
349                ("Unsupported spec: " + keySpec.getName());
350    }
351
352    // see JCE spec
353    protected SecretKey engineTranslateKey(SecretKey key)
354            throws InvalidKeyException {
355        return (SecretKey)convertKey(token, key, algorithm);
356    }
357
358}
359