• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1996, 2010, 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 
26 package sun.security.x509;
27 
28 import java.io.*;
29 import java.util.Arrays;
30 import java.util.Properties;
31 import java.security.Key;
32 import java.security.PublicKey;
33 import java.security.KeyFactory;
34 import java.security.KeyRep;
35 import java.security.Security;
36 import java.security.Provider;
37 import java.security.InvalidKeyException;
38 import java.security.NoSuchAlgorithmException;
39 import java.security.spec.InvalidKeySpecException;
40 import java.security.spec.X509EncodedKeySpec;
41 
42 import sun.misc.HexDumpEncoder;
43 import 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  */
60 public 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      */
X509Key()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      */
X509Key(AlgorithmId algid, BitArray key)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      */
setKey(BitArray key)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      */
getKey()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      */
parse(DerValue in)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(), e);
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      */
parseKeyBits()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      */
buildX509Key(AlgorithmId algid, BitArray key)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(), e);
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      */
getAlgorithm()284     public String getAlgorithm() {
285         return algid.getName();
286     }
287 
288     /**
289      * Returns the algorithm ID to be used with this key.
290      */
getAlgorithmId()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      */
encode(DerOutputStream out)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      */
getEncoded()306     public byte[] getEncoded() {
307         try {
308             return getEncodedInternal().clone();
309         } catch (InvalidKeyException e) {
310             // XXX
311         }
312         return null;
313     }
314 
getEncodedInternal()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      */
getFormat()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      */
encode()343     public byte[] encode() throws InvalidKeyException {
344         return getEncodedInternal().clone();
345     }
346 
347     /*
348      * Returns a printable representation of the key
349      */
toString()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      */
decode(InputStream in)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 
decode(byte[] encodedKey)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      */
writeObject(ObjectOutputStream stream)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      */
readObject(ObjectInputStream stream)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 
equals(Object obj)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      */
hashCode()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      */
encode(DerOutputStream out, AlgorithmId algid, BitArray key)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 }
478