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      */
  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             @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      */
 283     public String getAlgorithm() {
 284         return algid.getName();
 285     }
 286 
 287     /**
 288      * Returns the algorithm ID to be used with this key.
 289      */
 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      */
 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      */
 305     public byte[] getEncoded() {
 306         try {
 307             return getEncodedInternal().clone();
 308         } catch (InvalidKeyException e) {
 309             // XXX
 310         }
 311         return null;
 312     }
 313 
 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      */
 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      */
 342     public byte[] encode() throws InvalidKeyException {
 343         return getEncodedInternal().clone();
 344     }
 345 
 346     /*
 347      * Returns a printable representation of the key
 348      */
 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      */
 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             // e.printStackTrace ();
 396             throw new InvalidKeyException("IOException: " +
 397                                           e.getMessage());
 398         }
 399     }
 400 
 401     public void decode(byte[] encodedKey) throws InvalidKeyException {
 402         decode(new ByteArrayInputStream(encodedKey));
 403     }
 404 
 405     /**
 406      * Serialization write ... X.509 keys serialize as
 407      * themselves, and they're parsed when they get read back.
 408      */
 409     private void writeObject(ObjectOutputStream stream) throws IOException {
 410         stream.write(getEncoded());
 411     }
 412 
 413     /**
 414      * Serialization read ... X.509 keys serialize as
 415      * themselves, and they're parsed when they get read back.
 416      */
 417     private void readObject(ObjectInputStream stream) throws IOException {
 418         try {
 419             decode(stream);
 420         } catch (InvalidKeyException e) {
 421             e.printStackTrace();
 422             throw new IOException("deserialized key is invalid: " +
 423                                   e.getMessage());
 424         }
 425     }
 426 
 427     public boolean equals(Object obj) {
 428         if (this == obj) {
 429             return true;
 430         }
 431         if (obj instanceof Key == false) {
 432             return false;
 433         }
 434         try {
 435             byte[] thisEncoded = this.getEncodedInternal();
 436             byte[] otherEncoded;
 437             if (obj instanceof X509Key) {
 438                 otherEncoded = ((X509Key)obj).getEncodedInternal();
 439             } else {
 440                 otherEncoded = ((Key)obj).getEncoded();
 441             }
 442             return Arrays.equals(thisEncoded, otherEncoded);
 443         } catch (InvalidKeyException e) {
 444             return false;
 445         }
 446     }
 447 
 448     /**
 449      * Calculates a hash code value for the object. Objects
 450      * which are equal will also have the same hashcode.
 451      */
 452     public int hashCode() {
 453         try {
 454             byte[] b1 = getEncodedInternal();
 455             int r = b1.length;
 456             for (int i = 0; i < b1.length; i++) {
 457                 r += (b1[i] & 0xff) * 37;
 458             }
 459             return r;
 460         } catch (InvalidKeyException e) {
 461             // should not happen
 462             return 0;
 463         }
 464     }
 465 
 466     /*
 467      * Produce SubjectPublicKey encoding from algorithm id and key material.
 468      */
 469     static void encode(DerOutputStream out, AlgorithmId algid, BitArray key)
 470         throws IOException {
 471             DerOutputStream tmp = new DerOutputStream();
 472             algid.encode(tmp);
 473             tmp.putUnalignedBitString(key);
 474             out.write(DerValue.tag_Sequence, tmp);
 475     }
 476 }