Home | History | Annotate | Download | only in x509
      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.misc.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      */
     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