1 /* 2 * Copyright (c) 1996, 2010, 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 */ 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 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 */ getAlgorithm()284 public String getAlgorithm() { 285 return algid.getName(); 286 } 287 288 /** 289 * Returns the algorithm ID to be used with this key. 290 */ getAlgorithmId()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 */ encode(DerOutputStream out)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 */ getEncoded()306 public byte[] getEncoded() { 307 try { 308 return getEncodedInternal().clone(); 309 } catch (InvalidKeyException e) { 310 // XXX 311 } 312 return null; 313 } 314 getEncodedInternal()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 */ getFormat()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 */ encode()343 public byte[] encode() throws InvalidKeyException { 344 return getEncodedInternal().clone(); 345 } 346 347 /* 348 * Returns a printable representation of the key 349 */ toString()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 */ decode(InputStream in)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 decode(byte[] encodedKey)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 */ writeObject(ObjectOutputStream stream)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 */ readObject(ObjectInputStream stream)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 equals(Object obj)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 */ hashCode()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 */ encode(DerOutputStream out, AlgorithmId algid, BitArray key)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