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
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.Security;
35import java.security.Provider;
36import java.security.InvalidKeyException;
37import java.security.NoSuchAlgorithmException;
38import java.security.spec.InvalidKeySpecException;
39import java.security.spec.X509EncodedKeySpec;
40
41import sun.misc.HexDumpEncoder;
42import 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 */
59public 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     */
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(), 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     */
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(), 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     */
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}
478