001: /*
002: * @(#)X509Key.java 1.89 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.x509;
029:
030: import java.io.*;
031: import java.util.Arrays;
032: import java.util.Properties;
033: import java.security.Key;
034: import java.security.PublicKey;
035: import java.security.KeyFactory;
036: import java.security.Security;
037: import java.security.Provider;
038: import java.security.InvalidKeyException;
039: import java.security.NoSuchAlgorithmException;
040: import java.security.spec.InvalidKeySpecException;
041: import java.security.spec.X509EncodedKeySpec;
042:
043: import sun.misc.HexDumpEncoder;
044: import sun.security.util.*;
045:
046: /**
047: * Holds an X.509 key, for example a public key found in an X.509
048: * certificate. Includes a description of the algorithm to be used
049: * with the key; these keys normally are used as
050: * "SubjectPublicKeyInfo".
051: *
052: * <P>While this class can represent any kind of X.509 key, it may be
053: * desirable to provide subclasses which understand how to parse keying
054: * data. For example, RSA public keys have two members, one for the
055: * public modulus and one for the prime exponent. If such a class is
056: * provided, it is used when parsing X.509 keys. If one is not provided,
057: * the key still parses correctly.
058: *
059: * @version 1.81, 02/02/00
060: * @author David Brownell
061: */
062: public class X509Key implements PublicKey {
063:
064: /** use serialVersionUID from JDK 1.1. for interoperability */
065: private static final long serialVersionUID = -5359250853002055002L;
066:
067: /* The algorithm information (name, parameters, etc). */
068: protected AlgorithmId algid;
069:
070: /**
071: * The key bytes, without the algorithm information.
072: * @deprecated Use the BitArray form which does not require keys to
073: * be byte aligned.
074: * @see sun.security.x509.X509Key#setKey(BitArray)
075: * @see sun.security.x509.X509Key#getKey()
076: */
077: protected byte[] key = null;
078:
079: /*
080: * The number of bits unused in the last byte of the key.
081: * Added to keep the byte[] key form consistent with the BitArray
082: * form. Can de deleted when byte[] key is deleted.
083: */
084: private int unusedBits = 0;
085:
086: /* BitArray form of key */
087: private BitArray bitStringKey = null;
088:
089: /* The encoding for the key. */
090: protected byte[] encodedKey;
091:
092: /**
093: * Default constructor. The key constructed must have its key
094: * and algorithm initialized before it may be used, for example
095: * by using <code>decode</code>.
096: */
097: public X509Key() {
098: }
099:
100: /*
101: * Build and initialize as a "default" key. All X.509 key
102: * data is stored and transmitted losslessly, but no knowledge
103: * about this particular algorithm is available.
104: */
105: private X509Key(AlgorithmId algid, BitArray key)
106: throws InvalidKeyException {
107: this .algid = algid;
108: setKey(key);
109: encode();
110: }
111:
112: /**
113: * Sets the key in the BitArray form.
114: */
115: protected void setKey(BitArray key) {
116: this .bitStringKey = (BitArray) key.clone();
117:
118: /*
119: * Do this to keep the byte array form consistent with
120: * this. Can delete when byte[] key is deleted.
121: */
122: this .key = key.toByteArray();
123: int remaining = key.length() % 8;
124: this .unusedBits = ((remaining == 0) ? 0 : 8 - remaining);
125: }
126:
127: /**
128: * Gets the key. The key may or may not be byte aligned.
129: * @return a BitArray containing the key.
130: */
131: protected BitArray getKey() {
132: /*
133: * Do this for consistency in case a subclass
134: * modifies byte[] key directly. Remove when
135: * byte[] key is deleted.
136: * Note: the consistency checks fail when the subclass
137: * modifies a non byte-aligned key (into a byte-aligned key)
138: * using the deprecated byte[] key field.
139: */
140: this .bitStringKey = new BitArray(this .key.length * 8
141: - this .unusedBits, 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: AlgorithmId algorithm;
162: PublicKey subjectKey;
163:
164: if (in.tag != DerValue.tag_Sequence)
165: throw new IOException("corrupt subject key");
166:
167: algorithm = AlgorithmId.parse(in.data.getDerValue());
168: try {
169: subjectKey = buildX509Key(algorithm, in.data
170: .getUnalignedBitString());
171:
172: } catch (InvalidKeyException e) {
173: throw new IOException("subject key, " + e.getMessage());
174: }
175:
176: if (in.data.available() != 0)
177: throw new IOException("excess subject key");
178: return subjectKey;
179: }
180:
181: /**
182: * Parse the key bits. This may be redefined by subclasses to take
183: * advantage of structure within the key. For example, RSA public
184: * keys encapsulate two unsigned integers (modulus and exponent) as
185: * DER values within the <code>key</code> bits; Diffie-Hellman and
186: * DSS/DSA keys encapsulate a single unsigned integer.
187: *
188: * <P>This function is called when creating X.509 SubjectPublicKeyInfo
189: * values using the X509Key member functions, such as <code>parse</code>
190: * and <code>decode</code>.
191: *
192: * @exception IOException on parsing errors.
193: * @exception InvalidKeyException on invalid key encodings.
194: */
195: protected void parseKeyBits() throws IOException,
196: 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: * Use the algid and key parameters to produce the ASN.1 encoding
209: * of the key, which will then be used as the input to the
210: * key factory.
211: */
212: DerOutputStream x509EncodedKeyStream = new DerOutputStream();
213: encode(x509EncodedKeyStream, algid, key);
214: X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(
215: x509EncodedKeyStream.toByteArray());
216:
217: try {
218: // Instantiate the key factory of the appropriate algorithm
219: KeyFactory keyFac = KeyFactory.getInstance(algid.getName());
220:
221: // Generate the public key
222: return keyFac.generatePublic(x509KeySpec);
223: } catch (NoSuchAlgorithmException e) {
224: // Return generic X509Key with opaque key data (see below)
225: } catch (InvalidKeySpecException e) {
226: throw new InvalidKeyException(e.getMessage());
227: }
228:
229: /*
230: * Try again using JDK1.1-style for backwards compatibility.
231: */
232: String classname = "";
233: try {
234: Properties props;
235: String keytype;
236: Provider sunProvider;
237:
238: sunProvider = Security.getProvider("SUN");
239: if (sunProvider == null)
240: throw new InstantiationException();
241: classname = sunProvider.getProperty("PublicKey.X.509."
242: + algid.getName());
243: if (classname == null) {
244: throw new InstantiationException();
245: }
246:
247: Class keyClass = null;
248: try {
249: keyClass = Class.forName(classname);
250: } catch (ClassNotFoundException e) {
251: ClassLoader cl = ClassLoader.getSystemClassLoader();
252: if (cl != null) {
253: keyClass = cl.loadClass(classname);
254: }
255: }
256:
257: Object inst = null;
258: X509Key result;
259:
260: if (keyClass != null)
261: inst = keyClass.newInstance();
262: if (inst instanceof X509Key) {
263: result = (X509Key) inst;
264: result.algid = algid;
265: result.setKey(key);
266: result.parseKeyBits();
267: return result;
268: }
269: } catch (ClassNotFoundException e) {
270: } catch (InstantiationException e) {
271: } catch (IllegalAccessException e) {
272: // this should not happen.
273: throw new IOException(classname + " [internal error]");
274: }
275:
276: X509Key result = new X509Key(algid, key);
277: return result;
278: }
279:
280: /**
281: * Returns the algorithm to be used with this key.
282: */
283: public String getAlgorithm() {
284: return algid.getName();
285: }
286:
287: /**
288: * Returns the algorithm ID to be used with this key.
289: */
290: public AlgorithmId getAlgorithmId() {
291: return algid;
292: }
293:
294: /**
295: * Encode SubjectPublicKeyInfo sequence on the DER output stream.
296: *
297: * @exception IOException on encoding errors.
298: */
299: public final void encode(DerOutputStream out) throws IOException {
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 (byte[]) getEncodedInternal().clone();
309: } catch (InvalidKeyException e) {
310: //
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 (byte[]) getEncodedInternal().clone();
345: }
346:
347: /*
348: * Returns a printable representation of the key
349: */
350: public String toString() {
351: HexDumpEncoder encoder = new HexDumpEncoder();
352:
353: return "algorithm = " + algid.toString()
354: + ", unparsed keybits = \n" + encoder.encodeBuffer(key);
355: }
356:
357: /**
358: * Initialize an X509Key object from an input stream. The data on that
359: * input stream must be encoded using DER, obeying the X.509
360: * <code>SubjectPublicKeyInfo</code> format. That is, the data is a
361: * sequence consisting of an algorithm ID and a bit string which holds
362: * the key. (That bit string is often used to encapsulate another DER
363: * encoded sequence.)
364: *
365: * <P>Subclasses should not normally redefine this method; they should
366: * instead provide a <code>parseKeyBits</code> method to parse any
367: * fields inside the <code>key</code> member.
368: *
369: * <P>The exception to this rule is that since private keys need not
370: * be encoded using the X.509 <code>SubjectPublicKeyInfo</code> format,
371: * private keys may override this method, <code>encode</code>, and
372: * of course <code>getFormat</code>.
373: *
374: * @param in an input stream with a DER-encoded X.509
375: * SubjectPublicKeyInfo value
376: * @exception InvalidKeyException on parsing errors.
377: */
378: public void decode(InputStream in) throws InvalidKeyException {
379: DerValue val;
380:
381: try {
382: val = new DerValue(in);
383: if (val.tag != DerValue.tag_Sequence)
384: throw new InvalidKeyException("invalid key format");
385:
386: algid = AlgorithmId.parse(val.data.getDerValue());
387: setKey(val.data.getUnalignedBitString());
388: parseKeyBits();
389: if (val.data.available() != 0)
390: throw new InvalidKeyException("excess key data");
391:
392: } catch (IOException e) {
393: // e.printStackTrace ();
394: throw new InvalidKeyException("IOException: "
395: + e.getMessage());
396: }
397: }
398:
399: public void decode(byte[] encodedKey) throws InvalidKeyException {
400: decode(new ByteArrayInputStream(encodedKey));
401: }
402:
403: /**
404: * Serialization write ... X.509 keys serialize as
405: * themselves, and they're parsed when they get read back.
406: */
407: private synchronized void writeObject(
408: java.io.ObjectOutputStream stream) throws IOException {
409: // no need to clone, because getEncoded() already returns a clone
410: stream.write(getEncoded());
411: }
412:
413: /**
414: * Serialization read ... X.509 keys serialize as
415: * themselves, and they're parsed when they get read back.
416: */
417: private synchronized void readObject(ObjectInputStream stream)
418: throws IOException {
419:
420: try {
421: decode(stream);
422:
423: } catch (InvalidKeyException e) {
424: e.printStackTrace();
425: throw new IOException("deserialized key is invalid: "
426: + e.getMessage());
427: }
428: }
429:
430: public boolean equals(Object obj) {
431: if (this == obj) {
432: return true;
433: }
434: if (obj instanceof Key == false) {
435: return false;
436: }
437: try {
438: byte[] this Encoded = this .getEncodedInternal();
439: byte[] otherEncoded;
440: if (obj instanceof X509Key) {
441: otherEncoded = ((X509Key) obj).getEncodedInternal();
442: } else {
443: otherEncoded = ((Key) obj).getEncoded();
444: }
445: return Arrays.equals(this Encoded, otherEncoded);
446: } catch (InvalidKeyException e) {
447: return false;
448: }
449: }
450:
451: /**
452: * Calculates a hash code value for the object. Objects
453: * which are equal will also have the same hashcode.
454: */
455: public int hashCode() {
456: try {
457: byte[] b1 = getEncodedInternal();
458: int r = b1.length;
459: for (int i = 0; i < b1.length; i++) {
460: r += (b1[i] & 0xff) * 37;
461: }
462: return r;
463: } catch (InvalidKeyException e) {
464: // should not happen
465: return 0;
466: }
467: }
468:
469: /*
470: * Produce SubjectPublicKey encoding from algorithm id and key material.
471: */
472: static void encode(DerOutputStream out, AlgorithmId algid,
473: BitArray key) throws IOException {
474: DerOutputStream tmp = new DerOutputStream();
475: algid.encode(tmp);
476: tmp.putUnalignedBitString(key);
477: out.write(DerValue.tag_Sequence, tmp);
478: }
479: }
|