1 /*
   2  * Copyright (c) 1996, 2005, 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      */
  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 }