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.pkcs; 27 28 import java.io.*; 29 import java.util.Properties; 30 import java.math.*; 31 import java.security.Key; 32 import java.security.KeyRep; 33 import java.security.PrivateKey; 34 import java.security.KeyFactory; 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.PKCS8EncodedKeySpec; 41 42 import sun.misc.HexDumpEncoder; 43 import sun.security.x509.*; 44 import sun.security.util.*; 45 46 /** 47 * Holds a PKCS#8 key, for example a private key 48 * 49 * @author Dave Brownell 50 * @author Benjamin Renaud 51 */ 52 public class PKCS8Key implements PrivateKey { 53 54 /** use serialVersionUID from JDK 1.1. for interoperability */ 55 private static final long serialVersionUID = -3836890099307167124L; 56 57 /* The algorithm information (name, parameters, etc). */ 58 protected AlgorithmId algid; 59 60 /* The key bytes, without the algorithm information */ 61 protected byte[] key; 62 63 /* The encoded for the key. */ 64 protected byte[] encodedKey; 65 66 /* The version for this key */ 67 public static final BigInteger version = BigInteger.ZERO; 68 69 /** 70 * Default constructor. The key constructed must have its key 71 * and algorithm initialized before it may be used, for example 72 * by using <code>decode</code>. 73 */ PKCS8Key()74 public PKCS8Key() { } 75 76 /* 77 * Build and initialize as a "default" key. All PKCS#8 key 78 * data is stored and transmitted losslessly, but no knowledge 79 * about this particular algorithm is available. 80 */ PKCS8Key(AlgorithmId algid, byte key [])81 private PKCS8Key (AlgorithmId algid, byte key []) 82 throws InvalidKeyException { 83 this.algid = algid; 84 this.key = key; 85 encode(); 86 } 87 88 /* 89 * Binary backwards compatibility. New uses should call parseKey(). 90 */ parse(DerValue in)91 public static PKCS8Key parse (DerValue in) throws IOException { 92 PrivateKey key; 93 94 key = parseKey(in); 95 if (key instanceof PKCS8Key) 96 return (PKCS8Key)key; 97 98 throw new IOException("Provider did not return PKCS8Key"); 99 } 100 101 /** 102 * Construct PKCS#8 subject public key from a DER value. If 103 * the runtime environment is configured with a specific class for 104 * this kind of key, a subclass is returned. Otherwise, a generic 105 * PKCS8Key object is returned. 106 * 107 * <P>This mechanism gurantees that keys (and algorithms) may be 108 * freely manipulated and transferred, without risk of losing 109 * information. Also, when a key (or algorithm) needs some special 110 * handling, that specific need can be accomodated. 111 * 112 * @param in the DER-encoded SubjectPublicKeyInfo value 113 * @exception IOException on data format errors 114 */ parseKey(DerValue in)115 public static PrivateKey parseKey (DerValue in) throws IOException 116 { 117 AlgorithmId algorithm; 118 PrivateKey privKey; 119 120 if (in.tag != DerValue.tag_Sequence) 121 throw new IOException ("corrupt private key"); 122 123 BigInteger parsedVersion = in.data.getBigInteger(); 124 if (!version.equals(parsedVersion)) { 125 throw new IOException("version mismatch: (supported: " + 126 Debug.toHexString(version) + 127 ", parsed: " + 128 Debug.toHexString(parsedVersion)); 129 } 130 131 algorithm = AlgorithmId.parse (in.data.getDerValue ()); 132 133 try { 134 privKey = buildPKCS8Key (algorithm, in.data.getOctetString ()); 135 136 } catch (InvalidKeyException e) { 137 throw new IOException("corrupt private key"); 138 } 139 140 if (in.data.available () != 0) 141 throw new IOException ("excess private key"); 142 return privKey; 143 } 144 145 /** 146 * Parse the key bits. This may be redefined by subclasses to take 147 * advantage of structure within the key. For example, RSA public 148 * keys encapsulate two unsigned integers (modulus and exponent) as 149 * DER values within the <code>key</code> bits; Diffie-Hellman and 150 * DSS/DSA keys encapsulate a single unsigned integer. 151 * 152 * <P>This function is called when creating PKCS#8 SubjectPublicKeyInfo 153 * values using the PKCS8Key member functions, such as <code>parse</code> 154 * and <code>decode</code>. 155 * 156 * @exception IOException if a parsing error occurs. 157 * @exception InvalidKeyException if the key encoding is invalid. 158 */ parseKeyBits()159 protected void parseKeyBits () throws IOException, InvalidKeyException { 160 encode(); 161 } 162 163 /* 164 * Factory interface, building the kind of key associated with this 165 * specific algorithm ID or else returning this generic base class. 166 * See the description above. 167 */ buildPKCS8Key(AlgorithmId algid, byte[] key)168 static PrivateKey buildPKCS8Key (AlgorithmId algid, byte[] key) 169 throws IOException, InvalidKeyException 170 { 171 /* 172 * Use the algid and key parameters to produce the ASN.1 encoding 173 * of the key, which will then be used as the input to the 174 * key factory. 175 */ 176 DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream(); 177 encode(pkcs8EncodedKeyStream, algid, key); 178 PKCS8EncodedKeySpec pkcs8KeySpec 179 = new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray()); 180 181 try { 182 // Instantiate the key factory of the appropriate algorithm 183 KeyFactory keyFac = KeyFactory.getInstance(algid.getName()); 184 185 // Generate the private key 186 return keyFac.generatePrivate(pkcs8KeySpec); 187 } catch (NoSuchAlgorithmException e) { 188 // Return generic PKCS8Key with opaque key data (see below) 189 } catch (InvalidKeySpecException e) { 190 // Return generic PKCS8Key with opaque key data (see below) 191 } 192 193 /* 194 * Try again using JDK1.1-style for backwards compatibility. 195 */ 196 String classname = ""; 197 try { 198 Properties props; 199 String keytype; 200 Provider sunProvider; 201 202 sunProvider = Security.getProvider("SUN"); 203 if (sunProvider == null) 204 throw new InstantiationException(); 205 classname = sunProvider.getProperty("PrivateKey.PKCS#8." + 206 algid.getName()); 207 if (classname == null) { 208 throw new InstantiationException(); 209 } 210 211 Class<?> keyClass = null; 212 try { 213 keyClass = Class.forName(classname); 214 } catch (ClassNotFoundException e) { 215 ClassLoader cl = ClassLoader.getSystemClassLoader(); 216 if (cl != null) { 217 keyClass = cl.loadClass(classname); 218 } 219 } 220 221 Object inst = null; 222 PKCS8Key result; 223 224 if (keyClass != null) 225 inst = keyClass.newInstance(); 226 if (inst instanceof PKCS8Key) { 227 result = (PKCS8Key) inst; 228 result.algid = algid; 229 result.key = key; 230 result.parseKeyBits(); 231 return result; 232 } 233 } catch (ClassNotFoundException e) { 234 } catch (InstantiationException e) { 235 } catch (IllegalAccessException e) { 236 // this should not happen. 237 throw new IOException (classname + " [internal error]"); 238 } 239 240 PKCS8Key result = new PKCS8Key(); 241 result.algid = algid; 242 result.key = key; 243 return result; 244 } 245 246 /** 247 * Returns the algorithm to be used with this key. 248 */ getAlgorithm()249 public String getAlgorithm() { 250 return algid.getName(); 251 } 252 253 /** 254 * Returns the algorithm ID to be used with this key. 255 */ getAlgorithmId()256 public AlgorithmId getAlgorithmId () { return algid; } 257 258 /** 259 * PKCS#8 sequence on the DER output stream. 260 */ encode(DerOutputStream out)261 public final void encode(DerOutputStream out) throws IOException 262 { 263 encode(out, this.algid, this.key); 264 } 265 266 /** 267 * Returns the DER-encoded form of the key as a byte array. 268 */ getEncoded()269 public synchronized byte[] getEncoded() { 270 byte[] result = null; 271 try { 272 result = encode(); 273 } catch (InvalidKeyException e) { 274 } 275 return result; 276 } 277 278 /** 279 * Returns the format for this key: "PKCS#8" 280 */ getFormat()281 public String getFormat() { 282 return "PKCS#8"; 283 } 284 285 /** 286 * Returns the DER-encoded form of the key as a byte array. 287 * 288 * @exception InvalidKeyException if an encoding error occurs. 289 */ encode()290 public byte[] encode() throws InvalidKeyException { 291 if (encodedKey == null) { 292 try { 293 DerOutputStream out; 294 295 out = new DerOutputStream (); 296 encode (out); 297 encodedKey = out.toByteArray(); 298 299 } catch (IOException e) { 300 throw new InvalidKeyException ("IOException : " + 301 e.getMessage()); 302 } 303 } 304 return encodedKey.clone(); 305 } 306 307 /** 308 * Initialize an PKCS8Key object from an input stream. The data 309 * on that input stream must be encoded using DER, obeying the 310 * PKCS#8 format: a sequence consisting of a version, an algorithm 311 * ID and a bit string which holds the key. (That bit string is 312 * often used to encapsulate another DER encoded sequence.) 313 * 314 * <P>Subclasses should not normally redefine this method; they should 315 * instead provide a <code>parseKeyBits</code> method to parse any 316 * fields inside the <code>key</code> member. 317 * 318 * @param in an input stream with a DER-encoded PKCS#8 319 * SubjectPublicKeyInfo value 320 * 321 * @exception InvalidKeyException if a parsing error occurs. 322 */ decode(InputStream in)323 public void decode(InputStream in) throws InvalidKeyException 324 { 325 DerValue val; 326 327 try { 328 val = new DerValue (in); 329 if (val.tag != DerValue.tag_Sequence) 330 throw new InvalidKeyException ("invalid key format"); 331 332 333 BigInteger version = val.data.getBigInteger(); 334 if (!version.equals(PKCS8Key.version)) { 335 throw new IOException("version mismatch: (supported: " + 336 Debug.toHexString(PKCS8Key.version) + 337 ", parsed: " + 338 Debug.toHexString(version)); 339 } 340 algid = AlgorithmId.parse (val.data.getDerValue ()); 341 key = val.data.getOctetString (); 342 parseKeyBits (); 343 344 if (val.data.available () != 0) { 345 // OPTIONAL attributes not supported yet 346 } 347 348 } catch (IOException e) { 349 // e.printStackTrace (); 350 throw new InvalidKeyException("IOException : " + 351 e.getMessage()); 352 } 353 } 354 decode(byte[] encodedKey)355 public void decode(byte[] encodedKey) throws InvalidKeyException { 356 decode(new ByteArrayInputStream(encodedKey)); 357 } 358 writeReplace()359 protected Object writeReplace() throws java.io.ObjectStreamException { 360 return new KeyRep(KeyRep.Type.PRIVATE, 361 getAlgorithm(), 362 getFormat(), 363 getEncoded()); 364 } 365 366 /** 367 * Serialization read ... PKCS#8 keys serialize as 368 * themselves, and they're parsed when they get read back. 369 */ readObject(ObjectInputStream stream)370 private void readObject (ObjectInputStream stream) 371 throws IOException { 372 373 try { 374 decode(stream); 375 376 } catch (InvalidKeyException e) { 377 e.printStackTrace(); 378 throw new IOException("deserialized key is invalid: " + 379 e.getMessage()); 380 } 381 } 382 383 /* 384 * Produce PKCS#8 encoding from algorithm id and key material. 385 */ encode(DerOutputStream out, AlgorithmId algid, byte[] key)386 static void encode(DerOutputStream out, AlgorithmId algid, byte[] key) 387 throws IOException { 388 DerOutputStream tmp = new DerOutputStream(); 389 tmp.putInteger(version); 390 algid.encode(tmp); 391 tmp.putOctetString(key); 392 out.write(DerValue.tag_Sequence, tmp); 393 } 394 395 /** 396 * Compares two private keys. This returns false if the object with which 397 * to compare is not of type <code>Key</code>. 398 * Otherwise, the encoding of this key object is compared with the 399 * encoding of the given key object. 400 * 401 * @param object the object with which to compare 402 * @return <code>true</code> if this key has the same encoding as the 403 * object argument; <code>false</code> otherwise. 404 */ equals(Object object)405 public boolean equals(Object object) { 406 if (this == object) { 407 return true; 408 } 409 410 if (object instanceof Key) { 411 412 // this encoding 413 byte[] b1; 414 if (encodedKey != null) { 415 b1 = encodedKey; 416 } else { 417 b1 = getEncoded(); 418 } 419 420 // that encoding 421 byte[] b2 = ((Key)object).getEncoded(); 422 423 // do the comparison 424 int i; 425 if (b1.length != b2.length) 426 return false; 427 for (i = 0; i < b1.length; i++) { 428 if (b1[i] != b2[i]) { 429 return false; 430 } 431 } 432 return true; 433 } 434 435 return false; 436 } 437 438 /** 439 * Calculates a hash code value for this object. Objects 440 * which are equal will also have the same hashcode. 441 */ hashCode()442 public int hashCode() { 443 int retval = 0; 444 byte[] b1 = getEncoded(); 445 446 for (int i = 1; i < b1.length; i++) { 447 retval += b1[i] * i; 448 } 449 return(retval); 450 } 451 } 452