1/* 2 * Copyright (c) 1996, 2017, 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 26package sun.security.pkcs; 27 28import java.io.*; 29import java.util.Properties; 30import java.math.*; 31import java.security.Key; 32import java.security.KeyRep; 33import java.security.PrivateKey; 34import java.security.KeyFactory; 35import java.security.MessageDigest; 36import java.security.Security; 37import java.security.Provider; 38import java.security.InvalidKeyException; 39import java.security.NoSuchAlgorithmException; 40import java.security.spec.InvalidKeySpecException; 41import java.security.spec.PKCS8EncodedKeySpec; 42 43import sun.security.util.HexDumpEncoder; 44import sun.security.x509.*; 45import sun.security.util.*; 46 47/** 48 * Holds a PKCS#8 key, for example a private key 49 * 50 * @author Dave Brownell 51 * @author Benjamin Renaud 52 */ 53public class PKCS8Key implements PrivateKey { 54 55 /** use serialVersionUID from JDK 1.1. for interoperability */ 56 private static final long serialVersionUID = -3836890099307167124L; 57 58 /* The algorithm information (name, parameters, etc). */ 59 protected AlgorithmId algid; 60 61 /* The key bytes, without the algorithm information */ 62 protected byte[] key; 63 64 /* The encoded for the key. */ 65 protected byte[] encodedKey; 66 67 /* The version for this key */ 68 public static final BigInteger version = BigInteger.ZERO; 69 70 /** 71 * Default constructor. The key constructed must have its key 72 * and algorithm initialized before it may be used, for example 73 * by using <code>decode</code>. 74 */ 75 public PKCS8Key() { } 76 77 /* 78 * Build and initialize as a "default" key. All PKCS#8 key 79 * data is stored and transmitted losslessly, but no knowledge 80 * about this particular algorithm is available. 81 */ 82 private PKCS8Key (AlgorithmId algid, byte[] key) 83 throws InvalidKeyException { 84 this.algid = algid; 85 this.key = key; 86 encode(); 87 } 88 89 /* 90 * Binary backwards compatibility. New uses should call parseKey(). 91 */ 92 public static PKCS8Key parse (DerValue in) throws IOException { 93 PrivateKey key; 94 95 key = parseKey(in); 96 if (key instanceof PKCS8Key) 97 return (PKCS8Key)key; 98 99 throw new IOException("Provider did not return PKCS8Key"); 100 } 101 102 /** 103 * Construct PKCS#8 subject public key from a DER value. If 104 * the runtime environment is configured with a specific class for 105 * this kind of key, a subclass is returned. Otherwise, a generic 106 * PKCS8Key object is returned. 107 * 108 * <P>This mechanism gurantees that keys (and algorithms) may be 109 * freely manipulated and transferred, without risk of losing 110 * information. Also, when a key (or algorithm) needs some special 111 * handling, that specific need can be accomodated. 112 * 113 * @param in the DER-encoded SubjectPublicKeyInfo value 114 * @exception IOException on data format errors 115 */ 116 public static PrivateKey parseKey (DerValue in) throws IOException 117 { 118 AlgorithmId algorithm; 119 PrivateKey privKey; 120 121 if (in.tag != DerValue.tag_Sequence) 122 throw new IOException ("corrupt private key"); 123 124 BigInteger parsedVersion = in.data.getBigInteger(); 125 if (!version.equals(parsedVersion)) { 126 throw new IOException("version mismatch: (supported: " + 127 Debug.toHexString(version) + 128 ", parsed: " + 129 Debug.toHexString(parsedVersion)); 130 } 131 132 algorithm = AlgorithmId.parse (in.data.getDerValue ()); 133 134 try { 135 privKey = buildPKCS8Key (algorithm, in.data.getOctetString ()); 136 137 } catch (InvalidKeyException e) { 138 throw new IOException("corrupt private key"); 139 } 140 141 if (in.data.available () != 0) 142 throw new IOException ("excess private key"); 143 return privKey; 144 } 145 146 /** 147 * Parse the key bits. This may be redefined by subclasses to take 148 * advantage of structure within the key. For example, RSA public 149 * keys encapsulate two unsigned integers (modulus and exponent) as 150 * DER values within the <code>key</code> bits; Diffie-Hellman and 151 * DSS/DSA keys encapsulate a single unsigned integer. 152 * 153 * <P>This function is called when creating PKCS#8 SubjectPublicKeyInfo 154 * values using the PKCS8Key member functions, such as <code>parse</code> 155 * and <code>decode</code>. 156 * 157 * @exception IOException if a parsing error occurs. 158 * @exception InvalidKeyException if the key encoding is invalid. 159 */ 160 protected void parseKeyBits () throws IOException, InvalidKeyException { 161 encode(); 162 } 163 164 /* 165 * Factory interface, building the kind of key associated with this 166 * specific algorithm ID or else returning this generic base class. 167 * See the description above. 168 */ 169 static PrivateKey buildPKCS8Key (AlgorithmId algid, byte[] key) 170 throws IOException, InvalidKeyException 171 { 172 /* 173 * Use the algid and key parameters to produce the ASN.1 encoding 174 * of the key, which will then be used as the input to the 175 * key factory. 176 */ 177 DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream(); 178 encode(pkcs8EncodedKeyStream, algid, key); 179 PKCS8EncodedKeySpec pkcs8KeySpec 180 = new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray()); 181 182 try { 183 // Instantiate the key factory of the appropriate algorithm 184 KeyFactory keyFac = KeyFactory.getInstance(algid.getName()); 185 186 // Generate the private key 187 return keyFac.generatePrivate(pkcs8KeySpec); 188 } catch (NoSuchAlgorithmException e) { 189 // Return generic PKCS8Key with opaque key data (see below) 190 } catch (InvalidKeySpecException e) { 191 // Return generic PKCS8Key with opaque key data (see below) 192 } 193 194 /* 195 * Try again using JDK1.1-style for backwards compatibility. 196 */ 197 String classname = ""; 198 try { 199 Properties props; 200 String keytype; 201 Provider sunProvider; 202 203 sunProvider = Security.getProvider("SUN"); 204 if (sunProvider == null) 205 throw new InstantiationException(); 206 classname = sunProvider.getProperty("PrivateKey.PKCS#8." + 207 algid.getName()); 208 if (classname == null) { 209 throw new InstantiationException(); 210 } 211 212 Class<?> keyClass = null; 213 try { 214 keyClass = Class.forName(classname); 215 } catch (ClassNotFoundException e) { 216 ClassLoader cl = ClassLoader.getSystemClassLoader(); 217 if (cl != null) { 218 keyClass = cl.loadClass(classname); 219 } 220 } 221 222 @SuppressWarnings("deprecation") 223 Object inst = (keyClass != null) ? keyClass.newInstance() : null; 224 PKCS8Key result; 225 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 */ 249 public String getAlgorithm() { 250 return algid.getName(); 251 } 252 253 /** 254 * Returns the algorithm ID to be used with this key. 255 */ 256 public AlgorithmId getAlgorithmId () { return algid; } 257 258 /** 259 * PKCS#8 sequence on the DER output stream. 260 */ 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 */ 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 */ 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 */ 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 */ 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 355 public void decode(byte[] encodedKey) throws InvalidKeyException { 356 decode(new ByteArrayInputStream(encodedKey)); 357 } 358 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 */ 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 */ 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 */ 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 // time-constant comparison 424 return MessageDigest.isEqual(b1, b2); 425 } 426 return false; 427 } 428 429 /** 430 * Calculates a hash code value for this object. Objects 431 * which are equal will also have the same hashcode. 432 */ 433 public int hashCode() { 434 int retval = 0; 435 byte[] b1 = getEncoded(); 436 437 for (int i = 1; i < b1.length; i++) { 438 retval += b1[i] * i; 439 } 440 return(retval); 441 } 442} 443