blob: 04855b19b571c5b8bbfc98c2bedb5fb9eba70b9a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.x509;
27
28import java.io.*;
29import java.util.Arrays;
30import java.util.Properties;
31import java.security.Key;
32import java.security.PublicKey;
33import java.security.KeyFactory;
34import java.security.KeyRep;
35import java.security.Security;
36import java.security.Provider;
37import java.security.InvalidKeyException;
38import java.security.NoSuchAlgorithmException;
39import java.security.spec.InvalidKeySpecException;
40import java.security.spec.X509EncodedKeySpec;
41
42import sun.misc.HexDumpEncoder;
43import sun.security.util.*;
44
45/**
46 * Holds an X.509 key, for example a public key found in an X.509
47 * certificate. Includes a description of the algorithm to be used
48 * with the key; these keys normally are used as
49 * "SubjectPublicKeyInfo".
50 *
51 * <P>While this class can represent any kind of X.509 key, it may be
52 * desirable to provide subclasses which understand how to parse keying
53 * data. For example, RSA public keys have two members, one for the
54 * public modulus and one for the prime exponent. If such a class is
55 * provided, it is used when parsing X.509 keys. If one is not provided,
56 * the key still parses correctly.
57 *
58 * @author David Brownell
59 */
60public class X509Key implements PublicKey {
61
62 /** use serialVersionUID from JDK 1.1. for interoperability */
63 private static final long serialVersionUID = -5359250853002055002L;
64
65 /* The algorithm information (name, parameters, etc). */
66 protected AlgorithmId algid;
67
68 /**
69 * The key bytes, without the algorithm information.
70 * @deprecated Use the BitArray form which does not require keys to
71 * be byte aligned.
72 * @see sun.security.x509.X509Key#setKey(BitArray)
73 * @see sun.security.x509.X509Key#getKey()
74 */
75 @Deprecated
76 protected byte[] key = null;
77
78 /*
79 * The number of bits unused in the last byte of the key.
80 * Added to keep the byte[] key form consistent with the BitArray
81 * form. Can de deleted when byte[] key is deleted.
82 */
83 private int unusedBits = 0;
84
85 /* BitArray form of key */
86 private BitArray bitStringKey = null;
87
88 /* The encoding for the key. */
89 protected byte[] encodedKey;
90
91 /**
92 * Default constructor. The key constructed must have its key
93 * and algorithm initialized before it may be used, for example
94 * by using <code>decode</code>.
95 */
96 public X509Key() { }
97
98 /*
99 * Build and initialize as a "default" key. All X.509 key
100 * data is stored and transmitted losslessly, but no knowledge
101 * about this particular algorithm is available.
102 */
103 private X509Key(AlgorithmId algid, BitArray key)
104 throws InvalidKeyException {
105 this.algid = algid;
106 setKey(key);
107 encode();
108 }
109
110 /**
111 * Sets the key in the BitArray form.
112 */
113 protected void setKey(BitArray key) {
114 this.bitStringKey = (BitArray)key.clone();
115
116 /*
117 * Do this to keep the byte array form consistent with
118 * this. Can delete when byte[] key is deleted.
119 */
120 this.key = key.toByteArray();
121 int remaining = key.length() % 8;
122 this.unusedBits =
123 ((remaining == 0) ? 0 : 8 - remaining);
124 }
125
126 /**
127 * Gets the key. The key may or may not be byte aligned.
128 * @return a BitArray containing the key.
129 */
130 protected BitArray getKey() {
131 /*
132 * Do this for consistency in case a subclass
133 * modifies byte[] key directly. Remove when
134 * byte[] key is deleted.
135 * Note: the consistency checks fail when the subclass
136 * modifies a non byte-aligned key (into a byte-aligned key)
137 * using the deprecated byte[] key field.
138 */
139 this.bitStringKey = new BitArray(
140 this.key.length * 8 - this.unusedBits,
141 this.key);
142
143 return (BitArray)bitStringKey.clone();
144 }
145
146 /**
147 * Construct X.509 subject public key from a DER value. If
148 * the runtime environment is configured with a specific class for
149 * this kind of key, a subclass is returned. Otherwise, a generic
150 * X509Key object is returned.
151 *
152 * <P>This mechanism gurantees that keys (and algorithms) may be
153 * freely manipulated and transferred, without risk of losing
154 * information. Also, when a key (or algorithm) needs some special
155 * handling, that specific need can be accomodated.
156 *
157 * @param in the DER-encoded SubjectPublicKeyInfo value
158 * @exception IOException on data format errors
159 */
160 public static PublicKey parse(DerValue in) throws IOException
161 {
162 AlgorithmId algorithm;
163 PublicKey subjectKey;
164
165 if (in.tag != DerValue.tag_Sequence)
166 throw new IOException("corrupt subject key");
167
168 algorithm = AlgorithmId.parse(in.data.getDerValue());
169 try {
170 subjectKey = buildX509Key(algorithm,
171 in.data.getUnalignedBitString());
172
173 } catch (InvalidKeyException e) {
174 throw new IOException("subject key, " + e.getMessage());
175 }
176
177 if (in.data.available() != 0)
178 throw new IOException("excess subject key");
179 return subjectKey;
180 }
181
182 /**
183 * Parse the key bits. This may be redefined by subclasses to take
184 * advantage of structure within the key. For example, RSA public
185 * keys encapsulate two unsigned integers (modulus and exponent) as
186 * DER values within the <code>key</code> bits; Diffie-Hellman and
187 * DSS/DSA keys encapsulate a single unsigned integer.
188 *
189 * <P>This function is called when creating X.509 SubjectPublicKeyInfo
190 * values using the X509Key member functions, such as <code>parse</code>
191 * and <code>decode</code>.
192 *
193 * @exception IOException on parsing errors.
194 * @exception InvalidKeyException on invalid key encodings.
195 */
196 protected void parseKeyBits() throws IOException, InvalidKeyException {
197 encode();
198 }
199
200 /*
201 * Factory interface, building the kind of key associated with this
202 * specific algorithm ID or else returning this generic base class.
203 * See the description above.
204 */
205 static PublicKey buildX509Key(AlgorithmId algid, BitArray key)
206 throws IOException, InvalidKeyException
207 {
208 /*
209 * Use the algid and key parameters to produce the ASN.1 encoding
210 * of the key, which will then be used as the input to the
211 * key factory.
212 */
213 DerOutputStream x509EncodedKeyStream = new DerOutputStream();
214 encode(x509EncodedKeyStream, algid, key);
215 X509EncodedKeySpec x509KeySpec
216 = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray());
217
218 try {
219 // Instantiate the key factory of the appropriate algorithm
220 KeyFactory keyFac = KeyFactory.getInstance(algid.getName());
221
222 // Generate the public key
223 return keyFac.generatePublic(x509KeySpec);
224 } catch (NoSuchAlgorithmException e) {
225 // Return generic X509Key with opaque key data (see below)
226 } catch (InvalidKeySpecException e) {
227 throw new InvalidKeyException(e.getMessage());
228 }
229
230 /*
231 * Try again using JDK1.1-style for backwards compatibility.
232 */
233 String classname = "";
234 try {
235 Properties props;
236 String keytype;
237 Provider sunProvider;
238
239 sunProvider = Security.getProvider("SUN");
240 if (sunProvider == null)
241 throw new InstantiationException();
242 classname = sunProvider.getProperty("PublicKey.X.509." +
243 algid.getName());
244 if (classname == null) {
245 throw new InstantiationException();
246 }
247
248 Class keyClass = null;
249 try {
250 keyClass = Class.forName(classname);
251 } catch (ClassNotFoundException e) {
252 ClassLoader cl = ClassLoader.getSystemClassLoader();
253 if (cl != null) {
254 keyClass = cl.loadClass(classname);
255 }
256 }
257
258 Object inst = null;
259 X509Key result;
260
261 if (keyClass != null)
262 inst = keyClass.newInstance();
263 if (inst instanceof X509Key) {
264 result = (X509Key) inst;
265 result.algid = algid;
266 result.setKey(key);
267 result.parseKeyBits();
268 return result;
269 }
270 } catch (ClassNotFoundException e) {
271 } catch (InstantiationException e) {
272 } catch (IllegalAccessException e) {
273 // this should not happen.
274 throw new IOException (classname + " [internal error]");
275 }
276
277 X509Key result = new X509Key(algid, key);
278 return result;
279 }
280
281 /**
282 * Returns the algorithm to be used with this key.
283 */
284 public String getAlgorithm() {
285 return algid.getName();
286 }
287
288 /**
289 * Returns the algorithm ID to be used with this key.
290 */
291 public AlgorithmId getAlgorithmId() { return algid; }
292
293 /**
294 * Encode SubjectPublicKeyInfo sequence on the DER output stream.
295 *
296 * @exception IOException on encoding errors.
297 */
298 public final void encode(DerOutputStream out) throws IOException
299 {
300 encode(out, this.algid, getKey());
301 }
302
303 /**
304 * Returns the DER-encoded form of the key as a byte array.
305 */
306 public byte[] getEncoded() {
307 try {
308 return getEncodedInternal().clone();
309 } catch (InvalidKeyException e) {
310 // XXX
311 }
312 return null;
313 }
314
315 public byte[] getEncodedInternal() throws InvalidKeyException {
316 byte[] encoded = encodedKey;
317 if (encoded == null) {
318 try {
319 DerOutputStream out = new DerOutputStream();
320 encode(out);
321 encoded = out.toByteArray();
322 } catch (IOException e) {
323 throw new InvalidKeyException("IOException : " +
324 e.getMessage());
325 }
326 encodedKey = encoded;
327 }
328 return encoded;
329 }
330
331 /**
332 * Returns the format for this key: "X.509"
333 */
334 public String getFormat() {
335 return "X.509";
336 }
337
338 /**
339 * Returns the DER-encoded form of the key as a byte array.
340 *
341 * @exception InvalidKeyException on encoding errors.
342 */
343 public byte[] encode() throws InvalidKeyException {
344 return getEncodedInternal().clone();
345 }
346
347 /*
348 * Returns a printable representation of the key
349 */
350 public String toString()
351 {
352 HexDumpEncoder encoder = new HexDumpEncoder();
353
354 return "algorithm = " + algid.toString()
355 + ", unparsed keybits = \n" + encoder.encodeBuffer(key);
356 }
357
358 /**
359 * Initialize an X509Key object from an input stream. The data on that
360 * input stream must be encoded using DER, obeying the X.509
361 * <code>SubjectPublicKeyInfo</code> format. That is, the data is a
362 * sequence consisting of an algorithm ID and a bit string which holds
363 * the key. (That bit string is often used to encapsulate another DER
364 * encoded sequence.)
365 *
366 * <P>Subclasses should not normally redefine this method; they should
367 * instead provide a <code>parseKeyBits</code> method to parse any
368 * fields inside the <code>key</code> member.
369 *
370 * <P>The exception to this rule is that since private keys need not
371 * be encoded using the X.509 <code>SubjectPublicKeyInfo</code> format,
372 * private keys may override this method, <code>encode</code>, and
373 * of course <code>getFormat</code>.
374 *
375 * @param in an input stream with a DER-encoded X.509
376 * SubjectPublicKeyInfo value
377 * @exception InvalidKeyException on parsing errors.
378 */
379 public void decode(InputStream in)
380 throws InvalidKeyException
381 {
382 DerValue val;
383
384 try {
385 val = new DerValue(in);
386 if (val.tag != DerValue.tag_Sequence)
387 throw new InvalidKeyException("invalid key format");
388
389 algid = AlgorithmId.parse(val.data.getDerValue());
390 setKey(val.data.getUnalignedBitString());
391 parseKeyBits();
392 if (val.data.available() != 0)
393 throw new InvalidKeyException ("excess key data");
394
395 } catch (IOException e) {
396 // e.printStackTrace ();
397 throw new InvalidKeyException("IOException: " +
398 e.getMessage());
399 }
400 }
401
402 public void decode(byte[] encodedKey) throws InvalidKeyException {
403 decode(new ByteArrayInputStream(encodedKey));
404 }
405
406 /**
407 * Serialization write ... X.509 keys serialize as
408 * themselves, and they're parsed when they get read back.
409 */
410 private void writeObject(ObjectOutputStream stream) throws IOException {
411 stream.write(getEncoded());
412 }
413
414 /**
415 * Serialization read ... X.509 keys serialize as
416 * themselves, and they're parsed when they get read back.
417 */
418 private void readObject(ObjectInputStream stream) throws IOException {
419 try {
420 decode(stream);
421 } catch (InvalidKeyException e) {
422 e.printStackTrace();
423 throw new IOException("deserialized key is invalid: " +
424 e.getMessage());
425 }
426 }
427
428 public boolean equals(Object obj) {
429 if (this == obj) {
430 return true;
431 }
432 if (obj instanceof Key == false) {
433 return false;
434 }
435 try {
436 byte[] thisEncoded = this.getEncodedInternal();
437 byte[] otherEncoded;
438 if (obj instanceof X509Key) {
439 otherEncoded = ((X509Key)obj).getEncodedInternal();
440 } else {
441 otherEncoded = ((Key)obj).getEncoded();
442 }
443 return Arrays.equals(thisEncoded, otherEncoded);
444 } catch (InvalidKeyException e) {
445 return false;
446 }
447 }
448
449 /**
450 * Calculates a hash code value for the object. Objects
451 * which are equal will also have the same hashcode.
452 */
453 public int hashCode() {
454 try {
455 byte[] b1 = getEncodedInternal();
456 int r = b1.length;
457 for (int i = 0; i < b1.length; i++) {
458 r += (b1[i] & 0xff) * 37;
459 }
460 return r;
461 } catch (InvalidKeyException e) {
462 // should not happen
463 return 0;
464 }
465 }
466
467 /*
468 * Produce SubjectPublicKey encoding from algorithm id and key material.
469 */
470 static void encode(DerOutputStream out, AlgorithmId algid, BitArray key)
471 throws IOException {
472 DerOutputStream tmp = new DerOutputStream();
473 algid.encode(tmp);
474 tmp.putUnalignedBitString(key);
475 out.write(DerValue.tag_Sequence, tmp);
476 }
477}