1/*
2 * Copyright (c) 2005, 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 com.sun.crypto.provider;
27
28import java.security.InvalidKeyException;
29import java.security.spec.KeySpec;
30import java.security.spec.InvalidKeySpecException;
31import javax.crypto.SecretKey;
32import javax.crypto.SecretKeyFactorySpi;
33import javax.crypto.spec.PBEKeySpec;
34
35/**
36 * This class implements a key factory for PBE keys derived using
37 * PBKDF2 with HmacSHA1 psuedo random function(PRF) as defined in
38 * PKCS#5 v2.0.
39 *
40 * @author Valerie Peng
41 *
42 */
43public final class PBKDF2HmacSHA1Factory extends SecretKeyFactorySpi {
44
45    /**
46     * Empty constructor
47     */
48    public PBKDF2HmacSHA1Factory() {
49    }
50
51    /**
52     * Generates a <code>SecretKey</code> object from the provided key
53     * specification (key material).
54     *
55     * @param keySpec the specification (key material) of the secret key
56     *
57     * @return the secret key
58     *
59     * @exception InvalidKeySpecException if the given key specification
60     * is inappropriate for this key factory to produce a public key.
61     */
62    protected SecretKey engineGenerateSecret(KeySpec keySpec)
63        throws InvalidKeySpecException
64    {
65        if (!(keySpec instanceof PBEKeySpec)) {
66            throw new InvalidKeySpecException("Invalid key spec");
67        }
68        PBEKeySpec ks = (PBEKeySpec) keySpec;
69        return new PBKDF2KeyImpl(ks, "HmacSHA1");
70    }
71
72    /**
73     * Returns a specification (key material) of the given key
74     * in the requested format.
75     *
76     * @param key the key
77     *
78     * @param keySpecCl the requested format in which the key material shall be
79     * returned
80     *
81     * @return the underlying key specification (key material) in the
82     * requested format
83     *
84     * @exception InvalidKeySpecException if the requested key
85     * specification is inappropriate for the given key, or the
86     * given key cannot be processed (e.g., the given key has an
87     * unrecognized algorithm or format).
88     */
89    protected KeySpec engineGetKeySpec(SecretKey key, Class<?> keySpecCl)
90        throws InvalidKeySpecException {
91        if (key instanceof javax.crypto.interfaces.PBEKey) {
92            // Check if requested key spec is amongst the valid ones
93            if ((keySpecCl != null)
94                && PBEKeySpec.class.isAssignableFrom(keySpecCl)) {
95                javax.crypto.interfaces.PBEKey pKey =
96                    (javax.crypto.interfaces.PBEKey) key;
97                return new PBEKeySpec
98                    (pKey.getPassword(), pKey.getSalt(),
99                     pKey.getIterationCount(), pKey.getEncoded().length*8);
100            } else {
101                throw new InvalidKeySpecException("Invalid key spec");
102            }
103        } else {
104            throw new InvalidKeySpecException("Invalid key " +
105                                               "format/algorithm");
106        }
107    }
108
109    /**
110     * Translates a <code>SecretKey</code> object, whose provider may be
111     * unknown or potentially untrusted, into a corresponding
112     * <code>SecretKey</code> object of this key factory.
113     *
114     * @param key the key whose provider is unknown or untrusted
115     *
116     * @return the translated key
117     *
118     * @exception InvalidKeyException if the given key cannot be processed by
119     * this key factory.
120     */
121    protected SecretKey engineTranslateKey(SecretKey key)
122        throws InvalidKeyException {
123        if ((key != null) &&
124            (key.getAlgorithm().equalsIgnoreCase("PBKDF2WithHmacSHA1")) &&
125            (key.getFormat().equalsIgnoreCase("RAW"))) {
126
127            // Check if key originates from this factory
128            if (key instanceof com.sun.crypto.provider.PBKDF2KeyImpl) {
129                return key;
130            }
131            // Check if key implements the PBEKey
132            if (key instanceof javax.crypto.interfaces.PBEKey) {
133                javax.crypto.interfaces.PBEKey pKey =
134                    (javax.crypto.interfaces.PBEKey) key;
135                try {
136                    PBEKeySpec spec =
137                        new PBEKeySpec(pKey.getPassword(),
138                                       pKey.getSalt(),
139                                       pKey.getIterationCount(),
140                                       pKey.getEncoded().length*8);
141                    return new PBKDF2KeyImpl(spec, "HmacSHA1");
142                } catch (InvalidKeySpecException re) {
143                    InvalidKeyException ike = new InvalidKeyException
144                        ("Invalid key component(s)");
145                    ike.initCause(re);
146                    throw ike;
147                }
148            }
149        }
150        throw new InvalidKeyException("Invalid key format/algorithm");
151    }
152}
153