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.security.util.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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 @SuppressWarnings("deprecation") 222 Object inst = (keyClass != null) ? keyClass.newInstance() : null; 223 PKCS8Key result; 224 225 if (inst instanceof PKCS8Key) { 226 result = (PKCS8Key) inst; 227 result.algid = algid; 228 result.key = key; 229 result.parseKeyBits(); 230 return result; 231 } 232 } catch (ClassNotFoundException e) { 233 } catch (InstantiationException e) { 234 } catch (IllegalAccessException e) { 235 // this should not happen. 236 throw new IOException (classname + " [internal error]"); 237 } 238 239 PKCS8Key result = new PKCS8Key(); 240 result.algid = algid; 241 result.key = key; 242 return result; 243 } 244 245 /** 246 * Returns the algorithm to be used with this key. 247 */ 248 public String getAlgorithm() { 249 return algid.getName(); 250 } 251 252 /** 253 * Returns the algorithm ID to be used with this key. 254 */ 255 public AlgorithmId getAlgorithmId () { return algid; } 256 257 /** 258 * PKCS#8 sequence on the DER output stream. 259 */ 260 public final void encode(DerOutputStream out) throws IOException 261 { 262 encode(out, this.algid, this.key); 263 } 264 265 /** 266 * Returns the DER-encoded form of the key as a byte array. 267 */ 268 public synchronized byte[] getEncoded() { 269 byte[] result = null; 270 try { 271 result = encode(); 272 } catch (InvalidKeyException e) { 273 } 274 return result; 275 } 276 277 /** 278 * Returns the format for this key: "PKCS#8" 279 */ 280 public String getFormat() { 281 return "PKCS#8"; 282 } 283 284 /** 285 * Returns the DER-encoded form of the key as a byte array. 286 * 287 * @exception InvalidKeyException if an encoding error occurs. 288 */ 289 public byte[] encode() throws InvalidKeyException { 290 if (encodedKey == null) { 291 try { 292 DerOutputStream out; 293 294 out = new DerOutputStream (); 295 encode (out); 296 encodedKey = out.toByteArray(); 297 298 } catch (IOException e) { 299 throw new InvalidKeyException ("IOException : " + 300 e.getMessage()); 301 } 302 } 303 return encodedKey.clone(); 304 } 305 306 /** 307 * Initialize an PKCS8Key object from an input stream. The data 308 * on that input stream must be encoded using DER, obeying the 309 * PKCS#8 format: a sequence consisting of a version, an algorithm 310 * ID and a bit string which holds the key. (That bit string is 311 * often used to encapsulate another DER encoded sequence.) 312 * 313 * <P>Subclasses should not normally redefine this method; they should 314 * instead provide a <code>parseKeyBits</code> method to parse any 315 * fields inside the <code>key</code> member. 316 * 317 * @param in an input stream with a DER-encoded PKCS#8 318 * SubjectPublicKeyInfo value 319 * 320 * @exception InvalidKeyException if a parsing error occurs. 321 */ 322 public void decode(InputStream in) throws InvalidKeyException 323 { 324 DerValue val; 325 326 try { 327 val = new DerValue (in); 328 if (val.tag != DerValue.tag_Sequence) 329 throw new InvalidKeyException ("invalid key format"); 330 331 332 BigInteger version = val.data.getBigInteger(); 333 if (!version.equals(PKCS8Key.version)) { 334 throw new IOException("version mismatch: (supported: " + 335 Debug.toHexString(PKCS8Key.version) + 336 ", parsed: " + 337 Debug.toHexString(version)); 338 } 339 algid = AlgorithmId.parse (val.data.getDerValue ()); 340 key = val.data.getOctetString (); 341 parseKeyBits (); 342 343 if (val.data.available () != 0) { 344 // OPTIONAL attributes not supported yet 345 } 346 347 } catch (IOException e) { 348 // e.printStackTrace (); 349 throw new InvalidKeyException("IOException : " + 350 e.getMessage()); 351 } 352 } 353 354 public void decode(byte[] encodedKey) throws InvalidKeyException { 355 decode(new ByteArrayInputStream(encodedKey)); 356 } 357 358 protected Object writeReplace() throws java.io.ObjectStreamException { 359 return new KeyRep(KeyRep.Type.PRIVATE, 360 getAlgorithm(), 361 getFormat(), 362 getEncoded()); 363 } 364 365 /** 366 * Serialization read ... PKCS#8 keys serialize as 367 * themselves, and they're parsed when they get read back. 368 */ 369 private void readObject (ObjectInputStream stream) 370 throws IOException { 371 372 try { 373 decode(stream); 374 375 } catch (InvalidKeyException e) { 376 e.printStackTrace(); 377 throw new IOException("deserialized key is invalid: " + 378 e.getMessage()); 379 } 380 } 381 382 /* 383 * Produce PKCS#8 encoding from algorithm id and key material. 384 */ 385 static void encode(DerOutputStream out, AlgorithmId algid, byte[] key) 386 throws IOException { 387 DerOutputStream tmp = new DerOutputStream(); 388 tmp.putInteger(version); 389 algid.encode(tmp); 390 tmp.putOctetString(key); 391 out.write(DerValue.tag_Sequence, tmp); 392 } 393 394 /** 395 * Compares two private keys. This returns false if the object with which 396 * to compare is not of type <code>Key</code>. 397 * Otherwise, the encoding of this key object is compared with the 398 * encoding of the given key object. 399 * 400 * @param object the object with which to compare 401 * @return <code>true</code> if this key has the same encoding as the 402 * object argument; <code>false</code> otherwise. 403 */ 404 public boolean equals(Object object) { 405 if (this == object) { 406 return true; 407 } 408 409 if (object instanceof Key) { 410 411 // this encoding 412 byte[] b1; 413 if (encodedKey != null) { 414 b1 = encodedKey; 415 } else { 416 b1 = getEncoded(); 417 } 418 419 // that encoding 420 byte[] b2 = ((Key)object).getEncoded(); 421 422 // do the comparison 423 int i; 424 if (b1.length != b2.length) 425 return false; 426 for (i = 0; i < b1.length; i++) { 427 if (b1[i] != b2[i]) { 428 return false; 429 } 430 } 431 return true; 432 } 433 434 return false; 435 } 436 437 /** 438 * Calculates a hash code value for this object. Objects 439 * which are equal will also have the same hashcode. 440 */ 441 public int hashCode() { 442 int retval = 0; 443 byte[] b1 = getEncoded(); 444 445 for (int i = 1; i < b1.length; i++) { 446 retval += b1[i] * i; 447 } 448 return(retval); 449 } 450 }