001: /*
002: * @(#)PKCS8Key.java 1.44 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.pkcs;
029:
030: import java.io.*;
031: import java.util.Properties;
032: import java.math.*;
033: import java.security.Key;
034: import java.security.PrivateKey;
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.PKCS8EncodedKeySpec;
042:
043: import sun.misc.HexDumpEncoder;
044: import sun.security.x509.*;
045: import sun.security.util.*;
046:
047: /**
048: * Holds a PKCS#8 key, for example a private key
049: *
050: * @version 1.38, 02/02/00
051: * @author Dave Brownell
052: * @author Benjamin Renaud
053: */
054: public class PKCS8Key implements PrivateKey {
055:
056: /** use serialVersionUID from JDK 1.1. for interoperability */
057: private static final long serialVersionUID = -3836890099307167124L;
058:
059: /* The algorithm information (name, parameters, etc). */
060: protected AlgorithmId algid;
061:
062: /* The key bytes, without the algorithm information */
063: protected byte[] key;
064:
065: /* The encoded for the key. */
066: protected byte[] encodedKey;
067:
068: /* The version for this key */
069: public static final BigInteger version = BigInteger.ZERO;
070:
071: /**
072: * Default constructor. The key constructed must have its key
073: * and algorithm initialized before it may be used, for example
074: * by using <code>decode</code>.
075: */
076: public PKCS8Key() {
077: }
078:
079: /*
080: * Build and initialize as a "default" key. All PKCS#8 key
081: * data is stored and transmitted losslessly, but no knowledge
082: * about this particular algorithm is available.
083: */
084: private PKCS8Key(AlgorithmId algid, byte key[])
085: throws InvalidKeyException {
086: this .algid = algid;
087: this .key = key;
088: encode();
089: }
090:
091: /*
092: * Binary backwards compatibility. New uses should call parseKey().
093: */
094: public static PKCS8Key parse(DerValue in) throws IOException {
095: PrivateKey key;
096:
097: key = parseKey(in);
098: if (key instanceof PKCS8Key)
099: return (PKCS8Key) key;
100:
101: throw new IOException("Provider did not return PKCS8Key");
102: }
103:
104: /**
105: * Construct PKCS#8 subject public key from a DER value. If
106: * the runtime environment is configured with a specific class for
107: * this kind of key, a subclass is returned. Otherwise, a generic
108: * PKCS8Key object is returned.
109: *
110: * <P>This mechanism gurantees that keys (and algorithms) may be
111: * freely manipulated and transferred, without risk of losing
112: * information. Also, when a key (or algorithm) needs some special
113: * handling, that specific need can be accomodated.
114: *
115: * @param in the DER-encoded SubjectPublicKeyInfo value
116: * @exception IOException on data format errors
117: */
118: public static PrivateKey parseKey(DerValue in) throws IOException {
119: AlgorithmId algorithm;
120: PrivateKey privKey;
121:
122: if (in.tag != DerValue.tag_Sequence)
123: throw new IOException("corrupt private key");
124:
125: BigInteger parsedVersion = in.data.getBigInteger();
126: if (!version.equals(parsedVersion)) {
127: throw new IOException("version mismatch: (supported: "
128: + Debug.toHexString(version) + ", parsed: "
129: + Debug.toHexString(parsedVersion));
130: }
131:
132: algorithm = AlgorithmId.parse(in.data.getDerValue());
133:
134: try {
135: privKey = buildPKCS8Key(algorithm, in.data.getOctetString());
136:
137: } catch (InvalidKeyException e) {
138: throw new IOException("corrupt private key");
139: }
140:
141: if (in.data.available() != 0)
142: throw new IOException("excess private key");
143: return privKey;
144: }
145:
146: /**
147: * Parse the key bits. This may be redefined by subclasses to take
148: * advantage of structure within the key. For example, RSA public
149: * keys encapsulate two unsigned integers (modulus and exponent) as
150: * DER values within the <code>key</code> bits; Diffie-Hellman and
151: * DSS/DSA keys encapsulate a single unsigned integer.
152: *
153: * <P>This function is called when creating PKCS#8 SubjectPublicKeyInfo
154: * values using the PKCS8Key member functions, such as <code>parse</code>
155: * and <code>decode</code>.
156: *
157: * @exception IOException if a parsing error occurs.
158: * @exception InvalidKeyException if the key encoding is invalid.
159: */
160: protected void parseKeyBits() throws IOException,
161: InvalidKeyException {
162: encode();
163: }
164:
165: /*
166: * Factory interface, building the kind of key associated with this
167: * specific algorithm ID or else returning this generic base class.
168: * See the description above.
169: */
170: static PrivateKey buildPKCS8Key(AlgorithmId algid, byte[] key)
171: throws IOException, InvalidKeyException {
172: /*
173: * Use the algid and key parameters to produce the ASN.1 encoding
174: * of the key, which will then be used as the input to the
175: * key factory.
176: */
177: DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream();
178: encode(pkcs8EncodedKeyStream, algid, key);
179: PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(
180: pkcs8EncodedKeyStream.toByteArray());
181:
182: try {
183: // Instantiate the key factory of the appropriate algorithm
184: KeyFactory keyFac = KeyFactory.getInstance(algid.getName());
185:
186: // Generate the private key
187: return keyFac.generatePrivate(pkcs8KeySpec);
188: } catch (NoSuchAlgorithmException e) {
189: // Return generic PKCS8Key with opaque key data (see below)
190: } catch (InvalidKeySpecException e) {
191: // Return generic PKCS8Key with opaque key data (see below)
192: }
193:
194: /*
195: * Try again using JDK1.1-style for backwards compatibility.
196: */
197: String classname = "";
198: try {
199: Properties props;
200: String keytype;
201: Provider sunProvider;
202:
203: sunProvider = Security.getProvider("SUN");
204: if (sunProvider == null)
205: throw new InstantiationException();
206: classname = sunProvider.getProperty("PrivateKey.PKCS#8."
207: + algid.getName());
208: if (classname == null) {
209: throw new InstantiationException();
210: }
211:
212: Class keyClass = null;
213: try {
214: keyClass = Class.forName(classname);
215: } catch (ClassNotFoundException e) {
216: ClassLoader cl = ClassLoader.getSystemClassLoader();
217: if (cl != null) {
218: keyClass = cl.loadClass(classname);
219: }
220: }
221:
222: Object inst = null;
223: PKCS8Key result;
224:
225: if (keyClass != null)
226: inst = keyClass.newInstance();
227: if (inst instanceof PKCS8Key) {
228: result = (PKCS8Key) inst;
229: result.algid = algid;
230: result.key = key;
231: result.parseKeyBits();
232: return result;
233: }
234: } catch (ClassNotFoundException e) {
235: } catch (InstantiationException e) {
236: } catch (IllegalAccessException e) {
237: // this should not happen.
238: throw new IOException(classname + " [internal error]");
239: }
240:
241: PKCS8Key result = new PKCS8Key();
242: result.algid = algid;
243: result.key = key;
244: return result;
245: }
246:
247: /**
248: * Returns the algorithm to be used with this key.
249: */
250: public String getAlgorithm() {
251: return algid.getName();
252: }
253:
254: /**
255: * Returns the algorithm ID to be used with this key.
256: */
257: public AlgorithmId getAlgorithmId() {
258: return algid;
259: }
260:
261: /**
262: * PKCS#8 sequence on the DER output stream.
263: */
264: public final void encode(DerOutputStream out) throws IOException {
265: encode(out, this .algid, this .key);
266: }
267:
268: /**
269: * Returns the DER-encoded form of the key as a byte array.
270: */
271: public synchronized byte[] getEncoded() {
272: byte[] result = null;
273: try {
274: result = encode();
275: } catch (InvalidKeyException e) {
276: }
277: return result;
278: }
279:
280: /**
281: * Returns the format for this key: "PKCS#8"
282: */
283: public String getFormat() {
284: return "PKCS#8";
285: }
286:
287: /**
288: * Returns the DER-encoded form of the key as a byte array.
289: *
290: * @exception InvalidKeyException if an encoding error occurs.
291: */
292: public byte[] encode() throws InvalidKeyException {
293: if (encodedKey == null) {
294: try {
295: DerOutputStream out;
296:
297: out = new DerOutputStream();
298: encode(out);
299: encodedKey = out.toByteArray();
300:
301: } catch (IOException e) {
302: throw new InvalidKeyException("IOException : "
303: + e.getMessage());
304: }
305: }
306: return (byte[]) encodedKey.clone();
307: }
308:
309: /*
310: * Returns a printable representation of the key
311: */
312: public String toString() {
313: HexDumpEncoder encoder = new HexDumpEncoder();
314:
315: return "algorithm = " + algid.toString()
316: + ", unparsed keybits = \n" + encoder.encodeBuffer(key);
317: }
318:
319: /**
320: * Initialize an PKCS8Key object from an input stream. The data
321: * on that input stream must be encoded using DER, obeying the
322: * PKCS#8 format: a sequence consisting of a version, an algorithm
323: * ID and a bit string which holds the key. (That bit string is
324: * often used to encapsulate another DER encoded sequence.)
325: *
326: * <P>Subclasses should not normally redefine this method; they should
327: * instead provide a <code>parseKeyBits</code> method to parse any
328: * fields inside the <code>key</code> member.
329: *
330: * @param in an input stream with a DER-encoded PKCS#8
331: * SubjectPublicKeyInfo value
332: *
333: * @exception InvalidKeyException if a parsing error occurs.
334: */
335: public void decode(InputStream in) throws InvalidKeyException {
336: DerValue val;
337:
338: try {
339: val = new DerValue(in);
340: if (val.tag != DerValue.tag_Sequence)
341: throw new InvalidKeyException("invalid key format");
342:
343: BigInteger version = val.data.getBigInteger();
344: if (!version.equals(this .version)) {
345: throw new IOException("version mismatch: (supported: "
346: + Debug.toHexString(this .version)
347: + ", parsed: " + Debug.toHexString(version));
348: }
349: algid = AlgorithmId.parse(val.data.getDerValue());
350: key = val.data.getOctetString();
351: parseKeyBits();
352: if (val.data.available() != 0)
353: throw new InvalidKeyException("excess key data");
354:
355: } catch (IOException e) {
356: // e.printStackTrace ();
357: throw new InvalidKeyException("IOException : "
358: + e.getMessage());
359: }
360: }
361:
362: public void decode(byte[] encodedKey) throws InvalidKeyException {
363: decode(new ByteArrayInputStream(encodedKey));
364: }
365:
366: /**
367: * Serialization write ... PKCS#8 keys serialize as
368: * themselves, and they're parsed when they get read back.
369: */
370: private synchronized void writeObject(
371: java.io.ObjectOutputStream stream) throws IOException {
372: // no need to clone, because getEncoded() already returns a clone
373: stream.write(getEncoded());
374: }
375:
376: /**
377: * Serialization read ... PKCS#8 keys serialize as
378: * themselves, and they're parsed when they get read back.
379: */
380: private synchronized void readObject(ObjectInputStream stream)
381: throws IOException {
382:
383: try {
384: decode(stream);
385:
386: } catch (InvalidKeyException e) {
387: e.printStackTrace();
388: throw new IOException("deserialized key is invalid: "
389: + e.getMessage());
390: }
391: }
392:
393: /*
394: * Produce PKCS#8 encoding from algorithm id and key material.
395: */
396: static void encode(DerOutputStream out, AlgorithmId algid,
397: byte[] key) throws IOException {
398: DerOutputStream tmp = new DerOutputStream();
399: tmp.putInteger(version);
400: algid.encode(tmp);
401: tmp.putOctetString(key);
402: out.write(DerValue.tag_Sequence, tmp);
403: }
404:
405: /**
406: * Compares two private keys. This returns false if the object with which
407: * to compare is not of type <code>Key</code>.
408: * Otherwise, the encoding of this key object is compared with the
409: * encoding of the given key object.
410: *
411: * @param object the object with which to compare
412: * @return <code>true</code> if this key has the same encoding as the
413: * object argument; <code>false</code> otherwise.
414: */
415: public boolean equals(Object object) {
416: if (this == object) {
417: return true;
418: }
419:
420: if (object instanceof Key) {
421:
422: // this encoding
423: byte[] b1;
424: if (encodedKey != null) {
425: b1 = encodedKey;
426: } else {
427: b1 = getEncoded();
428: }
429:
430: // that encoding
431: byte[] b2 = ((Key) object).getEncoded();
432:
433: // do the comparison
434: int i;
435: if (b1.length != b2.length)
436: return false;
437: for (i = 0; i < b1.length; i++) {
438: if (b1[i] != b2[i]) {
439: return false;
440: }
441: }
442: return true;
443: }
444:
445: return false;
446: }
447:
448: /**
449: * Calculates a hash code value for this object. Objects
450: * which are equal will also have the same hashcode.
451: */
452: public int hashCode() {
453: int retval = 0;
454: byte[] b1 = getEncoded();
455:
456: for (int i = 1; i < b1.length; i++) {
457: retval += b1[i] * i;
458: }
459: return (retval);
460: }
461: }
|