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