1 /*
2  * Copyright (c) 1996, 2013, 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.Security;
35 import java.security.Provider;
36 import java.security.InvalidKeyException;
37 import java.security.NoSuchAlgorithmException;
38 import java.security.spec.InvalidKeySpecException;
39 import java.security.spec.X509EncodedKeySpec;
40 
41 import sun.security.util.HexDumpEncoder;
42 import sun.security.util.*;
43 
44 /**
45  * Holds an X.509 key, for example a public key found in an X.509
46  * certificate.  Includes a description of the algorithm to be used
47  * with the key; these keys normally are used as
48  * "SubjectPublicKeyInfo".
49  *
50  * <P>While this class can represent any kind of X.509 key, it may be
51  * desirable to provide subclasses which understand how to parse keying
52  * data.   For example, RSA public keys have two members, one for the
53  * public modulus and one for the prime exponent.  If such a class is
54  * provided, it is used when parsing X.509 keys.  If one is not provided,
55  * the key still parses correctly.
56  *
57  * @author David Brownell
58  */
59 public class X509Key implements PublicKey {
60 
61     /** use serialVersionUID from JDK 1.1. for interoperability */
62     private static final long serialVersionUID = -5359250853002055002L;
63 
64     /* The algorithm information (name, parameters, etc). */
65     protected AlgorithmId algid;
66 
67     /**
68      * The key bytes, without the algorithm information.
69      * @deprecated Use the BitArray form which does not require keys to
70      * be byte aligned.
71      * @see sun.security.x509.X509Key#setKey(BitArray)
72      * @see sun.security.x509.X509Key#getKey()
73      */
74     @Deprecated
75     protected byte[] key = null;
76 
77     /*
78      * The number of bits unused in the last byte of the key.
79      * Added to keep the byte[] key form consistent with the BitArray
80      * form. Can de deleted when byte[] key is deleted.
81      */
82     @Deprecated
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             @SuppressWarnings("deprecation")
259             Object      inst = (keyClass != null) ? keyClass.newInstance() : null;
260             X509Key     result;
261 
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      */
getAlgorithm()283     public String getAlgorithm() {
284         return algid.getName();
285     }
286 
287     /**
288      * Returns the algorithm ID to be used with this key.
289      */
getAlgorithmId()290     public AlgorithmId  getAlgorithmId() { return algid; }
291 
292     /**
293      * Encode SubjectPublicKeyInfo sequence on the DER output stream.
294      *
295      * @exception IOException on encoding errors.
296      */
encode(DerOutputStream out)297     public final void encode(DerOutputStream out) throws IOException
298     {
299         encode(out, this.algid, getKey());
300     }
301 
302     /**
303      * Returns the DER-encoded form of the key as a byte array.
304      */
getEncoded()305     public byte[] getEncoded() {
306         try {
307             return getEncodedInternal().clone();
308         } catch (InvalidKeyException e) {
309             // XXX
310         }
311         return null;
312     }
313 
getEncodedInternal()314     public byte[] getEncodedInternal() throws InvalidKeyException {
315         byte[] encoded = encodedKey;
316         if (encoded == null) {
317             try {
318                 DerOutputStream out = new DerOutputStream();
319                 encode(out);
320                 encoded = out.toByteArray();
321             } catch (IOException e) {
322                 throw new InvalidKeyException("IOException : " +
323                                                e.getMessage());
324             }
325             encodedKey = encoded;
326         }
327         return encoded;
328     }
329 
330     /**
331      * Returns the format for this key: "X.509"
332      */
getFormat()333     public String getFormat() {
334         return "X.509";
335     }
336 
337     /**
338      * Returns the DER-encoded form of the key as a byte array.
339      *
340      * @exception InvalidKeyException on encoding errors.
341      */
encode()342     public byte[] encode() throws InvalidKeyException {
343         return getEncodedInternal().clone();
344     }
345 
346     /*
347      * Returns a printable representation of the key
348      */
toString()349     public String toString()
350     {
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      */
decode(InputStream in)378     public void decode(InputStream in)
379     throws InvalidKeyException
380     {
381         DerValue        val;
382 
383         try {
384             val = new DerValue(in);
385             if (val.tag != DerValue.tag_Sequence)
386                 throw new InvalidKeyException("invalid key format");
387 
388             algid = AlgorithmId.parse(val.data.getDerValue());
389             setKey(val.data.getUnalignedBitString());
390             parseKeyBits();
391             if (val.data.available() != 0)
392                 throw new InvalidKeyException ("excess key data");
393 
394         } catch (IOException e) {
395             throw new InvalidKeyException("IOException: " +
396                                           e.getMessage());
397         }
398     }
399 
decode(byte[] encodedKey)400     public void decode(byte[] encodedKey) throws InvalidKeyException {
401         decode(new ByteArrayInputStream(encodedKey));
402     }
403 
404     /**
405      * Serialization write ... X.509 keys serialize as
406      * themselves, and they're parsed when they get read back.
407      */
writeObject(ObjectOutputStream stream)408     private void writeObject(ObjectOutputStream stream) throws IOException {
409         stream.write(getEncoded());
410     }
411 
412     /**
413      * Serialization read ... X.509 keys serialize as
414      * themselves, and they're parsed when they get read back.
415      */
readObject(ObjectInputStream stream)416     private void readObject(ObjectInputStream stream) throws IOException {
417         try {
418             decode(stream);
419         } catch (InvalidKeyException e) {
420             e.printStackTrace();
421             throw new IOException("deserialized key is invalid: " +
422                                   e.getMessage());
423         }
424     }
425 
equals(Object obj)426     public boolean equals(Object obj) {
427         if (this == obj) {
428             return true;
429         }
430         if (obj instanceof Key == false) {
431             return false;
432         }
433         try {
434             byte[] thisEncoded = this.getEncodedInternal();
435             byte[] otherEncoded;
436             if (obj instanceof X509Key) {
437                 otherEncoded = ((X509Key)obj).getEncodedInternal();
438             } else {
439                 otherEncoded = ((Key)obj).getEncoded();
440             }
441             return Arrays.equals(thisEncoded, otherEncoded);
442         } catch (InvalidKeyException e) {
443             return false;
444         }
445     }
446 
447     /**
448      * Calculates a hash code value for the object. Objects
449      * which are equal will also have the same hashcode.
450      */
hashCode()451     public int hashCode() {
452         try {
453             byte[] b1 = getEncodedInternal();
454             int r = b1.length;
455             for (int i = 0; i < b1.length; i++) {
456                 r += (b1[i] & 0xff) * 37;
457             }
458             return r;
459         } catch (InvalidKeyException e) {
460             // should not happen
461             return 0;
462         }
463     }
464 
465     /*
466      * Produce SubjectPublicKey encoding from algorithm id and key material.
467      */
encode(DerOutputStream out, AlgorithmId algid, BitArray key)468     static void encode(DerOutputStream out, AlgorithmId algid, BitArray key)
469         throws IOException {
470             DerOutputStream tmp = new DerOutputStream();
471             algid.encode(tmp);
472             tmp.putUnalignedBitString(key);
473             out.write(DerValue.tag_Sequence, tmp);
474     }
475 }
476