1/*
2 * Copyright (c) 1997, 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.provider;
27
28import java.security.Key;
29import java.security.PublicKey;
30import java.security.PrivateKey;
31import java.security.KeyFactorySpi;
32import java.security.InvalidKeyException;
33import java.security.AccessController;
34import java.security.interfaces.DSAParams;
35import java.security.spec.DSAPublicKeySpec;
36import java.security.spec.DSAPrivateKeySpec;
37import java.security.spec.KeySpec;
38import java.security.spec.InvalidKeySpecException;
39import java.security.spec.X509EncodedKeySpec;
40import java.security.spec.PKCS8EncodedKeySpec;
41
42import sun.security.action.GetPropertyAction;
43
44/**
45 * This class implements the DSA key factory of the Sun provider.
46 *
47 * @author Jan Luehe
48 *
49 *
50 * @since 1.2
51 */
52
53public class DSAKeyFactory extends KeyFactorySpi {
54
55    // package private for DSAKeyPairGenerator
56    static final boolean SERIAL_INTEROP;
57    private static final String SERIAL_PROP = "sun.security.key.serial.interop";
58
59    static {
60
61        /**
62         * Check to see if we need to maintain interoperability for serialized
63         * keys between JDK 5.0 -> JDK 1.4.  In other words, determine whether
64         * a key object serialized in JDK 5.0 must be deserializable in
65         * JDK 1.4.
66         *
67         * If true, then we generate sun.security.provider.DSAPublicKey.
68         * If false, then we generate sun.security.provider.DSAPublicKeyImpl.
69         *
70         * By default this is false.
71         * This incompatibility was introduced by 4532506.
72         */
73        String prop = GetPropertyAction.privilegedGetProperty(SERIAL_PROP);
74        SERIAL_INTEROP = "true".equalsIgnoreCase(prop);
75    }
76
77    /**
78     * Generates a public key object from the provided key specification
79     * (key material).
80     *
81     * @param keySpec the specification (key material) of the public key
82     *
83     * @return the public key
84     *
85     * @exception InvalidKeySpecException if the given key specification
86     * is inappropriate for this key factory to produce a public key.
87     */
88    protected PublicKey engineGeneratePublic(KeySpec keySpec)
89    throws InvalidKeySpecException {
90        try {
91            if (keySpec instanceof DSAPublicKeySpec) {
92                DSAPublicKeySpec dsaPubKeySpec = (DSAPublicKeySpec)keySpec;
93                if (SERIAL_INTEROP) {
94                    return new DSAPublicKey(dsaPubKeySpec.getY(),
95                                        dsaPubKeySpec.getP(),
96                                        dsaPubKeySpec.getQ(),
97                                        dsaPubKeySpec.getG());
98                } else {
99                    return new DSAPublicKeyImpl(dsaPubKeySpec.getY(),
100                                        dsaPubKeySpec.getP(),
101                                        dsaPubKeySpec.getQ(),
102                                        dsaPubKeySpec.getG());
103                }
104            } else if (keySpec instanceof X509EncodedKeySpec) {
105                if (SERIAL_INTEROP) {
106                    return new DSAPublicKey
107                        (((X509EncodedKeySpec)keySpec).getEncoded());
108                } else {
109                    return new DSAPublicKeyImpl
110                        (((X509EncodedKeySpec)keySpec).getEncoded());
111                }
112            } else {
113                throw new InvalidKeySpecException
114                    ("Inappropriate key specification");
115            }
116        } catch (InvalidKeyException e) {
117            throw new InvalidKeySpecException
118                ("Inappropriate key specification: " + e.getMessage());
119        }
120    }
121
122    /**
123     * Generates a private key object from the provided key specification
124     * (key material).
125     *
126     * @param keySpec the specification (key material) of the private key
127     *
128     * @return the private key
129     *
130     * @exception InvalidKeySpecException if the given key specification
131     * is inappropriate for this key factory to produce a private key.
132     */
133    protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
134    throws InvalidKeySpecException {
135        try {
136            if (keySpec instanceof DSAPrivateKeySpec) {
137                DSAPrivateKeySpec dsaPrivKeySpec = (DSAPrivateKeySpec)keySpec;
138                return new DSAPrivateKey(dsaPrivKeySpec.getX(),
139                                         dsaPrivKeySpec.getP(),
140                                         dsaPrivKeySpec.getQ(),
141                                         dsaPrivKeySpec.getG());
142
143            } else if (keySpec instanceof PKCS8EncodedKeySpec) {
144                return new DSAPrivateKey
145                    (((PKCS8EncodedKeySpec)keySpec).getEncoded());
146
147            } else {
148                throw new InvalidKeySpecException
149                    ("Inappropriate key specification");
150            }
151        } catch (InvalidKeyException e) {
152            throw new InvalidKeySpecException
153                ("Inappropriate key specification: " + e.getMessage());
154        }
155    }
156
157    /**
158     * Returns a specification (key material) of the given key object
159     * in the requested format.
160     *
161     * @param key the key
162     *
163     * @param keySpec the requested format in which the key material shall be
164     * returned
165     *
166     * @return the underlying key specification (key material) in the
167     * requested format
168     *
169     * @exception InvalidKeySpecException if the requested key specification is
170     * inappropriate for the given key, or the given key cannot be processed
171     * (e.g., the given key has an unrecognized algorithm or format).
172     */
173    protected <T extends KeySpec>
174        T engineGetKeySpec(Key key, Class<T> keySpec)
175    throws InvalidKeySpecException {
176
177        DSAParams params;
178
179        try {
180
181            if (key instanceof java.security.interfaces.DSAPublicKey) {
182
183                // Determine valid key specs
184                Class<?> dsaPubKeySpec = Class.forName
185                    ("java.security.spec.DSAPublicKeySpec");
186                Class<?> x509KeySpec = Class.forName
187                    ("java.security.spec.X509EncodedKeySpec");
188
189                if (dsaPubKeySpec.isAssignableFrom(keySpec)) {
190                    java.security.interfaces.DSAPublicKey dsaPubKey
191                        = (java.security.interfaces.DSAPublicKey)key;
192                    params = dsaPubKey.getParams();
193                    return keySpec.cast(new DSAPublicKeySpec(dsaPubKey.getY(),
194                                                             params.getP(),
195                                                             params.getQ(),
196                                                             params.getG()));
197
198                } else if (x509KeySpec.isAssignableFrom(keySpec)) {
199                    return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
200
201                } else {
202                    throw new InvalidKeySpecException
203                        ("Inappropriate key specification");
204                }
205
206            } else if (key instanceof java.security.interfaces.DSAPrivateKey) {
207
208                // Determine valid key specs
209                Class<?> dsaPrivKeySpec = Class.forName
210                    ("java.security.spec.DSAPrivateKeySpec");
211                Class<?> pkcs8KeySpec = Class.forName
212                    ("java.security.spec.PKCS8EncodedKeySpec");
213
214                if (dsaPrivKeySpec.isAssignableFrom(keySpec)) {
215                    java.security.interfaces.DSAPrivateKey dsaPrivKey
216                        = (java.security.interfaces.DSAPrivateKey)key;
217                    params = dsaPrivKey.getParams();
218                    return keySpec.cast(new DSAPrivateKeySpec(dsaPrivKey.getX(),
219                                                              params.getP(),
220                                                              params.getQ(),
221                                                              params.getG()));
222
223                } else if (pkcs8KeySpec.isAssignableFrom(keySpec)) {
224                    return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
225
226                } else {
227                    throw new InvalidKeySpecException
228                        ("Inappropriate key specification");
229                }
230
231            } else {
232                throw new InvalidKeySpecException("Inappropriate key type");
233            }
234
235        } catch (ClassNotFoundException e) {
236            throw new InvalidKeySpecException
237                ("Unsupported key specification: " + e.getMessage());
238        }
239    }
240
241    /**
242     * Translates a key object, whose provider may be unknown or potentially
243     * untrusted, into a corresponding key object of this key factory.
244     *
245     * @param key the key whose provider is unknown or untrusted
246     *
247     * @return the translated key
248     *
249     * @exception InvalidKeyException if the given key cannot be processed by
250     * this key factory.
251     */
252    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
253
254        try {
255
256            if (key instanceof java.security.interfaces.DSAPublicKey) {
257                // Check if key originates from this factory
258                if (key instanceof sun.security.provider.DSAPublicKey) {
259                    return key;
260                }
261                // Convert key to spec
262                DSAPublicKeySpec dsaPubKeySpec
263                    = engineGetKeySpec(key, DSAPublicKeySpec.class);
264                // Create key from spec, and return it
265                return engineGeneratePublic(dsaPubKeySpec);
266
267            } else if (key instanceof java.security.interfaces.DSAPrivateKey) {
268                // Check if key originates from this factory
269                if (key instanceof sun.security.provider.DSAPrivateKey) {
270                    return key;
271                }
272                // Convert key to spec
273                DSAPrivateKeySpec dsaPrivKeySpec
274                    = engineGetKeySpec(key, DSAPrivateKeySpec.class);
275                // Create key from spec, and return it
276                return engineGeneratePrivate(dsaPrivKeySpec);
277
278            } else {
279                throw new InvalidKeyException("Wrong algorithm type");
280            }
281
282        } catch (InvalidKeySpecException e) {
283            throw new InvalidKeyException("Cannot translate key: "
284                                          + e.getMessage());
285        }
286    }
287}
288