1 /* 2 * Copyright (c) 1999, 2020, 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.pkcs12; 27 28 import java.io.*; 29 import java.security.AccessController; 30 import java.security.MessageDigest; 31 import java.security.NoSuchAlgorithmException; 32 import java.security.Key; 33 import java.security.KeyFactory; 34 import java.security.KeyStore; 35 import java.security.KeyStoreSpi; 36 import java.security.KeyStoreException; 37 import java.security.PKCS12Attribute; 38 import java.security.PrivateKey; 39 import java.security.PrivilegedAction; 40 import java.security.UnrecoverableEntryException; 41 import java.security.UnrecoverableKeyException; 42 import java.security.SecureRandom; 43 import java.security.Security; 44 import java.security.cert.Certificate; 45 import java.security.cert.CertificateFactory; 46 import java.security.cert.X509Certificate; 47 import java.security.cert.CertificateException; 48 import java.security.spec.AlgorithmParameterSpec; 49 import java.security.spec.InvalidParameterSpecException; 50 import java.security.spec.KeySpec; 51 import java.security.spec.PKCS8EncodedKeySpec; 52 import java.util.*; 53 54 import java.security.AlgorithmParameters; 55 import java.security.InvalidAlgorithmParameterException; 56 import javax.crypto.spec.PBEParameterSpec; 57 import javax.crypto.spec.PBEKeySpec; 58 import javax.crypto.spec.SecretKeySpec; 59 import javax.crypto.SecretKeyFactory; 60 import javax.crypto.SecretKey; 61 import javax.crypto.Cipher; 62 import javax.crypto.Mac; 63 import javax.security.auth.DestroyFailedException; 64 import javax.security.auth.x500.X500Principal; 65 66 import sun.security.util.Debug; 67 import sun.security.util.DerInputStream; 68 import sun.security.util.DerOutputStream; 69 import sun.security.util.DerValue; 70 import sun.security.util.ObjectIdentifier; 71 import sun.security.pkcs.ContentInfo; 72 import sun.security.util.SecurityProperties; 73 import sun.security.x509.AlgorithmId; 74 import sun.security.pkcs.EncryptedPrivateKeyInfo; 75 76 77 /** 78 * This class provides the keystore implementation referred to as "PKCS12". 79 * Implements the PKCS#12 PFX protected using the Password privacy mode. 80 * The contents are protected using Password integrity mode. 81 * 82 * Currently these PBE algorithms are used by default: 83 * - PBEWithSHA1AndDESede to encrypt private keys, iteration count 50000. 84 * - PBEWithSHA1AndRC2_40 to encrypt certificates, iteration count 50000. 85 * 86 * The default Mac algorithm is HmacPBESHA1, iteration count 100000. 87 * 88 * Supported encryption of various implementations : 89 * 90 * Software and mode. Certificate encryption Private key encryption 91 * --------------------------------------------------------------------- 92 * MSIE4 (domestic 40 bit RC2. 40 bit RC2 93 * and xport versions) 94 * PKCS#12 export. 95 * 96 * MSIE4, 5 (domestic 40 bit RC2, 40 bit RC2, 97 * and export versions) 3 key triple DES 3 key triple DES 98 * PKCS#12 import. 99 * 100 * MSIE5 40 bit RC2 3 key triple DES, 101 * PKCS#12 export. with SHA1 (168 bits) 102 * 103 * Netscape Communicator 40 bit RC2 3 key triple DES, 104 * (domestic and export with SHA1 (168 bits) 105 * versions) PKCS#12 export 106 * 107 * Netscape Communicator 40 bit ciphers only All. 108 * (export version) 109 * PKCS#12 import. 110 * 111 * Netscape Communicator All. All. 112 * (domestic or fortified 113 * version) PKCS#12 import. 114 * 115 * OpenSSL PKCS#12 code. All. All. 116 * --------------------------------------------------------------------- 117 * 118 * NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry. 119 * PKCS#12 is mainly used to deliver private keys with their associated 120 * certificate chain and aliases. In a PKCS12 keystore, entries are 121 * identified by the alias, and a localKeyId is required to match the 122 * private key with the certificate. Trusted certificate entries are identified 123 * by the presence of an trustedKeyUsage attribute. 124 * 125 * @author Seema Malkani 126 * @author Jeff Nisewanger 127 * @author Jan Luehe 128 * 129 * @see java.security.KeyStoreSpi 130 */ 131 public final class PKCS12KeyStore extends KeyStoreSpi { 132 133 public static final int VERSION_3 = 3; 134 135 private static final int MAX_ITERATION_COUNT = 5000000; 136 private static final int SALT_LEN = 20; 137 138 // friendlyName, localKeyId, trustedKeyUsage 139 private static final String[] CORE_ATTRIBUTES = { 140 "1.2.840.113549.1.9.20", 141 "1.2.840.113549.1.9.21", 142 "2.16.840.1.113894.746875.1.1" 143 }; 144 145 private static final Debug debug = Debug.getInstance("pkcs12"); 146 147 private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; 148 private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; 149 private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5}; 150 151 private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20}; 152 private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21}; 153 154 private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1}; 155 156 private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; 157 // TODO: temporary Oracle OID 158 /* 159 * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) 160 * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } 161 */ 162 private static final int TrustedKeyUsage[] = 163 {2, 16, 840, 1, 113894, 746875, 1, 1}; 164 private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0}; 165 166 private static final ObjectIdentifier PKCS8ShroudedKeyBag_OID; 167 private static final ObjectIdentifier CertBag_OID; 168 private static final ObjectIdentifier SecretBag_OID; 169 private static final ObjectIdentifier PKCS9FriendlyName_OID; 170 private static final ObjectIdentifier PKCS9LocalKeyId_OID; 171 private static final ObjectIdentifier PKCS9CertType_OID; 172 private static final ObjectIdentifier pbes2_OID; 173 private static final ObjectIdentifier TrustedKeyUsage_OID; 174 private static final ObjectIdentifier[] AnyUsage; 175 176 private int counter = 0; 177 178 // private key count 179 // Note: This is a workaround to allow null localKeyID attribute 180 // in pkcs12 with one private key entry and associated cert-chain 181 private int privateKeyCount = 0; 182 183 // secret key count 184 private int secretKeyCount = 0; 185 186 // certificate count 187 private int certificateCount = 0; 188 189 // Alg/params used for *this* keystore. Initialized as -1 for ic and 190 // null for algorithm names. When an existing file is read, they will be 191 // assigned inside engineLoad() so storing an existing keystore uses the 192 // old alg/params. This makes sure if a keystore is created password-less 193 // it will be password-less forever. Otherwise, engineStore() will read 194 // the default values. These fields are always reset when load() is called. 195 private String certProtectionAlgorithm = null; 196 private int certPbeIterationCount = -1; 197 private String macAlgorithm = null; 198 private int macIterationCount = -1; 199 200 // the source of randomness 201 private SecureRandom random; 202 203 static { 204 try { 205 PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag); 206 CertBag_OID = new ObjectIdentifier(certBag); 207 SecretBag_OID = new ObjectIdentifier(secretBag); 208 PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name); 209 PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId); 210 PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType); 211 pbes2_OID = new ObjectIdentifier(pbes2); 212 TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage); 213 AnyUsage = new ObjectIdentifier[]{ 214 new ObjectIdentifier(AnyExtendedKeyUsage)}; 215 } catch (IOException ioe) { 216 throw new AssertionError("OID not initialized", ioe); 217 } 218 } 219 220 // A keystore entry and associated attributes 221 private static class Entry { 222 Date date; // the creation date of this entry 223 String alias; 224 byte[] keyId; 225 Set<KeyStore.Entry.Attribute> attributes; 226 } 227 228 // A key entry 229 private static class KeyEntry extends Entry { 230 } 231 232 // A private key entry and its supporting certificate chain 233 private static class PrivateKeyEntry extends KeyEntry { 234 byte[] protectedPrivKey; 235 Certificate chain[]; 236 }; 237 238 // A secret key 239 private static class SecretKeyEntry extends KeyEntry { 240 byte[] protectedSecretKey; 241 }; 242 243 // A certificate entry 244 private static class CertEntry extends Entry { 245 final X509Certificate cert; 246 ObjectIdentifier[] trustedKeyUsage; 247 248 CertEntry(X509Certificate cert, byte[] keyId, String alias) { 249 this(cert, keyId, alias, null, null); 250 } 251 252 CertEntry(X509Certificate cert, byte[] keyId, String alias, 253 ObjectIdentifier[] trustedKeyUsage, 254 Set<? extends KeyStore.Entry.Attribute> attributes) { 255 this.date = new Date(); 256 this.cert = cert; 257 this.keyId = keyId; 258 this.alias = alias; 259 this.trustedKeyUsage = trustedKeyUsage; 260 this.attributes = new HashSet<>(); 261 if (attributes != null) { 262 this.attributes.addAll(attributes); 263 } 264 } 265 } 266 267 /** 268 * Retries an action with password "\0" if "" fails. 269 * @param <T> the return type 270 */ 271 @FunctionalInterface 272 private interface RetryWithZero<T> { 273 274 T tryOnce(char[] password) throws Exception; 275 276 static <S> S run(RetryWithZero<S> f, char[] password) throws Exception { 277 try { 278 return f.tryOnce(password); 279 } catch (Exception e) { 280 if (password.length == 0) { 281 // Retry using an empty password with a NUL terminator. 282 if (debug != null) { 283 debug.println("Retry with a NUL password"); 284 } 285 return f.tryOnce(new char[1]); 286 } 287 throw e; 288 } 289 } 290 } 291 292 /** 293 * Private keys and certificates are stored in a map. 294 * Map entries are keyed by alias names. 295 */ 296 private Map<String, Entry> entries = 297 Collections.synchronizedMap(new LinkedHashMap<String, Entry>()); 298 299 private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>(); 300 private LinkedHashMap<X500Principal, X509Certificate> certsMap = 301 new LinkedHashMap<X500Principal, X509Certificate>(); 302 private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>(); 303 304 /** 305 * Returns the key associated with the given alias, using the given 306 * password to recover it. 307 * 308 * @param alias the alias name 309 * @param password the password for recovering the key 310 * 311 * @return the requested key, or null if the given alias does not exist 312 * or does not identify a <i>key entry</i>. 313 * 314 * @exception NoSuchAlgorithmException if the algorithm for recovering the 315 * key cannot be found 316 * @exception UnrecoverableKeyException if the key cannot be recovered 317 * (e.g., the given password is wrong). 318 */ 319 public Key engineGetKey(String alias, char[] password) 320 throws NoSuchAlgorithmException, UnrecoverableKeyException 321 { 322 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 323 Key key = null; 324 325 if (entry == null || (!(entry instanceof KeyEntry))) { 326 return null; 327 } 328 329 // get the encoded private key or secret key 330 byte[] encrBytes = null; 331 if (entry instanceof PrivateKeyEntry) { 332 encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey; 333 } else if (entry instanceof SecretKeyEntry) { 334 encrBytes = ((SecretKeyEntry) entry).protectedSecretKey; 335 } else { 336 throw new UnrecoverableKeyException("Error locating key"); 337 } 338 339 byte[] encryptedKey; 340 AlgorithmParameters algParams; 341 ObjectIdentifier algOid; 342 343 try { 344 // get the encrypted private key 345 EncryptedPrivateKeyInfo encrInfo = 346 new EncryptedPrivateKeyInfo(encrBytes); 347 encryptedKey = encrInfo.getEncryptedData(); 348 349 // parse Algorithm parameters 350 DerValue val = new DerValue(encrInfo.getAlgorithm().encode()); 351 DerInputStream in = val.toDerInputStream(); 352 algOid = in.getOID(); 353 algParams = parseAlgParameters(algOid, in); 354 355 } catch (IOException ioe) { 356 UnrecoverableKeyException uke = 357 new UnrecoverableKeyException("Private key not stored as " 358 + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe); 359 uke.initCause(ioe); 360 throw uke; 361 } 362 363 try { 364 PBEParameterSpec pbeSpec; 365 int ic; 366 367 if (algParams != null) { 368 try { 369 pbeSpec = 370 algParams.getParameterSpec(PBEParameterSpec.class); 371 } catch (InvalidParameterSpecException ipse) { 372 throw new IOException("Invalid PBE algorithm parameters"); 373 } 374 ic = pbeSpec.getIterationCount(); 375 376 if (ic > MAX_ITERATION_COUNT) { 377 throw new IOException("key PBE iteration count too large"); 378 } 379 } else { 380 ic = 0; 381 } 382 383 key = RetryWithZero.run(pass -> { 384 // Use JCE 385 SecretKey skey = getPBEKey(pass); 386 Cipher cipher = Cipher.getInstance( 387 mapPBEParamsToAlgorithm(algOid, algParams)); 388 cipher.init(Cipher.DECRYPT_MODE, skey, algParams); 389 byte[] keyInfo = cipher.doFinal(encryptedKey); 390 /* 391 * Parse the key algorithm and then use a JCA key factory 392 * to re-create the key. 393 */ 394 DerValue val = new DerValue(keyInfo); 395 DerInputStream in = val.toDerInputStream(); 396 int i = in.getInteger(); 397 DerValue[] value = in.getSequence(2); 398 if (value.length < 1 || value.length > 2) { 399 throw new IOException("Invalid length for AlgorithmIdentifier"); 400 } 401 AlgorithmId algId = new AlgorithmId(value[0].getOID()); 402 String keyAlgo = algId.getName(); 403 404 // decode private key 405 if (entry instanceof PrivateKeyEntry) { 406 KeyFactory kfac = KeyFactory.getInstance(keyAlgo); 407 PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo); 408 Key tmp = kfac.generatePrivate(kspec); 409 410 if (debug != null) { 411 debug.println("Retrieved a protected private key at alias" + 412 " '" + alias + "' (" + 413 mapPBEParamsToAlgorithm(algOid, algParams) + 414 " iterations: " + ic + ")"); 415 } 416 return tmp; 417 // decode secret key 418 } else { 419 byte[] keyBytes = in.getOctetString(); 420 SecretKeySpec secretKeySpec = 421 new SecretKeySpec(keyBytes, keyAlgo); 422 423 // Special handling required for PBE: needs a PBEKeySpec 424 Key tmp; 425 if (keyAlgo.startsWith("PBE")) { 426 SecretKeyFactory sKeyFactory = 427 SecretKeyFactory.getInstance(keyAlgo); 428 KeySpec pbeKeySpec = 429 sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class); 430 tmp = sKeyFactory.generateSecret(pbeKeySpec); 431 } else { 432 tmp = secretKeySpec; 433 } 434 435 if (debug != null) { 436 debug.println("Retrieved a protected secret key at alias " + 437 "'" + alias + "' (" + 438 mapPBEParamsToAlgorithm(algOid, algParams) + 439 " iterations: " + ic + ")"); 440 } 441 return tmp; 442 } 443 }, password); 444 445 } catch (Exception e) { 446 UnrecoverableKeyException uke = 447 new UnrecoverableKeyException("Get Key failed: " + 448 e.getMessage()); 449 uke.initCause(e); 450 throw uke; 451 } 452 return key; 453 } 454 455 /** 456 * Returns the certificate chain associated with the given alias. 457 * 458 * @param alias the alias name 459 * 460 * @return the certificate chain (ordered with the user's certificate first 461 * and the root certificate authority last), or null if the given alias 462 * does not exist or does not contain a certificate chain (i.e., the given 463 * alias identifies either a <i>trusted certificate entry</i> or a 464 * <i>key entry</i> without a certificate chain). 465 */ 466 public Certificate[] engineGetCertificateChain(String alias) { 467 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 468 if (entry != null && entry instanceof PrivateKeyEntry) { 469 if (((PrivateKeyEntry) entry).chain == null) { 470 return null; 471 } else { 472 473 if (debug != null) { 474 debug.println("Retrieved a " + 475 ((PrivateKeyEntry) entry).chain.length + 476 "-certificate chain at alias '" + alias + "'"); 477 } 478 479 return ((PrivateKeyEntry) entry).chain.clone(); 480 } 481 } else { 482 return null; 483 } 484 } 485 486 /** 487 * Returns the certificate associated with the given alias. 488 * 489 * <p>If the given alias name identifies a 490 * <i>trusted certificate entry</i>, the certificate associated with that 491 * entry is returned. If the given alias name identifies a 492 * <i>key entry</i>, the first element of the certificate chain of that 493 * entry is returned, or null if that entry does not have a certificate 494 * chain. 495 * 496 * @param alias the alias name 497 * 498 * @return the certificate, or null if the given alias does not exist or 499 * does not contain a certificate. 500 */ 501 public Certificate engineGetCertificate(String alias) { 502 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 503 if (entry == null) { 504 return null; 505 } 506 if (entry instanceof CertEntry && 507 ((CertEntry) entry).trustedKeyUsage != null) { 508 509 if (debug != null) { 510 if (Arrays.equals(AnyUsage, 511 ((CertEntry) entry).trustedKeyUsage)) { 512 debug.println("Retrieved a certificate at alias '" + alias + 513 "' (trusted for any purpose)"); 514 } else { 515 debug.println("Retrieved a certificate at alias '" + alias + 516 "' (trusted for limited purposes)"); 517 } 518 } 519 520 return ((CertEntry) entry).cert; 521 522 } else if (entry instanceof PrivateKeyEntry) { 523 if (((PrivateKeyEntry) entry).chain == null) { 524 return null; 525 } else { 526 527 if (debug != null) { 528 debug.println("Retrieved a certificate at alias '" + alias + 529 "'"); 530 } 531 532 return ((PrivateKeyEntry) entry).chain[0]; 533 } 534 535 } else { 536 return null; 537 } 538 } 539 540 /** 541 * Returns the creation date of the entry identified by the given alias. 542 * 543 * @param alias the alias name 544 * 545 * @return the creation date of this entry, or null if the given alias does 546 * not exist 547 */ 548 public Date engineGetCreationDate(String alias) { 549 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 550 if (entry != null) { 551 return new Date(entry.date.getTime()); 552 } else { 553 return null; 554 } 555 } 556 557 /** 558 * Assigns the given key to the given alias, protecting it with the given 559 * password. 560 * 561 * <p>If the given key is of type <code>java.security.PrivateKey</code>, 562 * it must be accompanied by a certificate chain certifying the 563 * corresponding public key. 564 * 565 * <p>If the given alias already exists, the keystore information 566 * associated with it is overridden by the given key (and possibly 567 * certificate chain). 568 * 569 * @param alias the alias name 570 * @param key the key to be associated with the alias 571 * @param password the password to protect the key 572 * @param chain the certificate chain for the corresponding public 573 * key (only required if the given key is of type 574 * <code>java.security.PrivateKey</code>). 575 * 576 * @exception KeyStoreException if the given key cannot be protected, or 577 * this operation fails for some other reason 578 */ 579 public synchronized void engineSetKeyEntry(String alias, Key key, 580 char[] password, Certificate[] chain) 581 throws KeyStoreException 582 { 583 KeyStore.PasswordProtection passwordProtection = 584 new KeyStore.PasswordProtection(password); 585 586 try { 587 setKeyEntry(alias, key, passwordProtection, chain, null); 588 589 } finally { 590 try { 591 passwordProtection.destroy(); 592 } catch (DestroyFailedException dfe) { 593 // ignore 594 } 595 } 596 } 597 598 /* 599 * Sets a key entry (with attributes, when present) 600 */ 601 private void setKeyEntry(String alias, Key key, 602 KeyStore.PasswordProtection passwordProtection, Certificate[] chain, 603 Set<KeyStore.Entry.Attribute> attributes) 604 throws KeyStoreException 605 { 606 try { 607 Entry entry; 608 609 if (key instanceof PrivateKey) { 610 PrivateKeyEntry keyEntry = new PrivateKeyEntry(); 611 keyEntry.date = new Date(); 612 613 if ((key.getFormat().equals("PKCS#8")) || 614 (key.getFormat().equals("PKCS8"))) { 615 616 if (debug != null) { 617 debug.println( 618 "Setting a protected private key at alias '" + 619 alias + "'"); 620 } 621 622 // Encrypt the private key 623 keyEntry.protectedPrivKey = 624 encryptPrivateKey(key.getEncoded(), passwordProtection); 625 } else { 626 throw new KeyStoreException("Private key is not encoded" + 627 "as PKCS#8"); 628 } 629 630 // clone the chain 631 if (chain != null) { 632 // validate cert-chain 633 if ((chain.length > 1) && (!validateChain(chain))) 634 throw new KeyStoreException("Certificate chain is " + 635 "not valid"); 636 keyEntry.chain = chain.clone(); 637 certificateCount += chain.length; 638 639 if (debug != null) { 640 debug.println("Setting a " + chain.length + 641 "-certificate chain at alias '" + alias + "'"); 642 } 643 } 644 privateKeyCount++; 645 entry = keyEntry; 646 647 } else if (key instanceof SecretKey) { 648 SecretKeyEntry keyEntry = new SecretKeyEntry(); 649 keyEntry.date = new Date(); 650 651 // Encode secret key in a PKCS#8 652 DerOutputStream pkcs8 = new DerOutputStream(); 653 DerOutputStream secretKeyInfo = new DerOutputStream(); 654 secretKeyInfo.putInteger(0); 655 AlgorithmId algId = AlgorithmId.get(key.getAlgorithm()); 656 algId.encode(secretKeyInfo); 657 secretKeyInfo.putOctetString(key.getEncoded()); 658 pkcs8.write(DerValue.tag_Sequence, secretKeyInfo); 659 660 // Encrypt the secret key (using same PBE as for private keys) 661 keyEntry.protectedSecretKey = 662 encryptPrivateKey(pkcs8.toByteArray(), passwordProtection); 663 664 if (debug != null) { 665 debug.println("Setting a protected secret key at alias '" + 666 alias + "'"); 667 } 668 secretKeyCount++; 669 entry = keyEntry; 670 671 } else { 672 throw new KeyStoreException("Unsupported Key type"); 673 } 674 675 entry.attributes = new HashSet<>(); 676 if (attributes != null) { 677 entry.attributes.addAll(attributes); 678 } 679 // set the keyId to current date 680 entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); 681 // set the alias 682 entry.alias = alias.toLowerCase(Locale.ENGLISH); 683 // add the entry 684 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 685 686 } catch (Exception nsae) { 687 throw new KeyStoreException("Key protection" + 688 " algorithm not found: " + nsae, nsae); 689 } 690 } 691 692 /** 693 * Assigns the given key (that has already been protected) to the given 694 * alias. 695 * 696 * <p>If the protected key is of type 697 * <code>java.security.PrivateKey</code>, it must be accompanied by a 698 * certificate chain certifying the corresponding public key. If the 699 * underlying keystore implementation is of type <code>jks</code>, 700 * <code>key</code> must be encoded as an 701 * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard. 702 * 703 * <p>If the given alias already exists, the keystore information 704 * associated with it is overridden by the given key (and possibly 705 * certificate chain). 706 * 707 * @param alias the alias name 708 * @param key the key (in protected format) to be associated with the alias 709 * @param chain the certificate chain for the corresponding public 710 * key (only useful if the protected key is of type 711 * <code>java.security.PrivateKey</code>). 712 * 713 * @exception KeyStoreException if this operation fails. 714 */ 715 public synchronized void engineSetKeyEntry(String alias, byte[] key, 716 Certificate[] chain) 717 throws KeyStoreException 718 { 719 // Private key must be encoded as EncryptedPrivateKeyInfo 720 // as defined in PKCS#8 721 try { 722 new EncryptedPrivateKeyInfo(key); 723 } catch (IOException ioe) { 724 throw new KeyStoreException("Private key is not stored" 725 + " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe); 726 } 727 728 PrivateKeyEntry entry = new PrivateKeyEntry(); 729 entry.date = new Date(); 730 731 if (debug != null) { 732 debug.println("Setting a protected private key at alias '" + 733 alias + "'"); 734 } 735 736 try { 737 // set the keyId to current date 738 entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); 739 } catch (UnsupportedEncodingException ex) { 740 // Won't happen 741 } 742 // set the alias 743 entry.alias = alias.toLowerCase(Locale.ENGLISH); 744 745 entry.protectedPrivKey = key.clone(); 746 if (chain != null) { 747 // validate cert-chain 748 if ((chain.length > 1) && (!validateChain(chain))) { 749 throw new KeyStoreException("Certificate chain is " 750 + "not valid"); 751 } 752 entry.chain = chain.clone(); 753 certificateCount += chain.length; 754 755 if (debug != null) { 756 debug.println("Setting a " + entry.chain.length + 757 "-certificate chain at alias '" + alias + "'"); 758 } 759 } 760 761 // add the entry 762 privateKeyCount++; 763 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 764 } 765 766 767 /* 768 * Generate random salt 769 */ 770 private byte[] getSalt() 771 { 772 // Generate a random salt. 773 byte[] salt = new byte[SALT_LEN]; 774 if (random == null) { 775 random = new SecureRandom(); 776 } 777 random.nextBytes(salt); 778 return salt; 779 } 780 781 /* 782 * Generate PBE Algorithm Parameters 783 */ 784 private AlgorithmParameters getPBEAlgorithmParameters( 785 String algorithm, int iterationCount) throws IOException { 786 AlgorithmParameters algParams = null; 787 788 // create PBE parameters from salt and iteration count 789 PBEParameterSpec paramSpec = 790 new PBEParameterSpec(getSalt(), iterationCount); 791 try { 792 algParams = AlgorithmParameters.getInstance(algorithm); 793 algParams.init(paramSpec); 794 } catch (Exception e) { 795 throw new IOException("getPBEAlgorithmParameters failed: " + 796 e.getMessage(), e); 797 } 798 return algParams; 799 } 800 801 /* 802 * parse Algorithm Parameters 803 */ 804 private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm, 805 DerInputStream in) throws IOException 806 { 807 AlgorithmParameters algParams = null; 808 try { 809 DerValue params; 810 if (in.available() == 0) { 811 params = null; 812 } else { 813 params = in.getDerValue(); 814 if (params.tag == DerValue.tag_Null) { 815 params = null; 816 } 817 } 818 if (params != null) { 819 if (algorithm.equals((Object)pbes2_OID)) { 820 algParams = AlgorithmParameters.getInstance("PBES2"); 821 } else { 822 algParams = AlgorithmParameters.getInstance("PBE"); 823 } 824 algParams.init(params.toByteArray()); 825 } 826 } catch (Exception e) { 827 throw new IOException("parseAlgParameters failed: " + 828 e.getMessage(), e); 829 } 830 return algParams; 831 } 832 833 /* 834 * Generate PBE key 835 */ 836 private SecretKey getPBEKey(char[] password) throws IOException 837 { 838 SecretKey skey = null; 839 840 try { 841 PBEKeySpec keySpec = new PBEKeySpec(password); 842 SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); 843 skey = skFac.generateSecret(keySpec); 844 keySpec.clearPassword(); 845 } catch (Exception e) { 846 throw new IOException("getSecretKey failed: " + 847 e.getMessage(), e); 848 } 849 return skey; 850 } 851 852 /* 853 * Encrypt private key or secret key using Password-based encryption (PBE) 854 * as defined in PKCS#5. 855 * 856 * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is 857 * used to derive the key and IV. 858 * 859 * @return encrypted private key or secret key encoded as 860 * EncryptedPrivateKeyInfo 861 */ 862 private byte[] encryptPrivateKey(byte[] data, 863 KeyStore.PasswordProtection passwordProtection) 864 throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException 865 { 866 byte[] key = null; 867 868 try { 869 String algorithm; 870 AlgorithmParameters algParams; 871 AlgorithmId algid; 872 873 // Initialize PBE algorithm and parameters 874 algorithm = passwordProtection.getProtectionAlgorithm(); 875 if (algorithm != null) { 876 AlgorithmParameterSpec algParamSpec = 877 passwordProtection.getProtectionParameters(); 878 if (algParamSpec != null) { 879 algParams = AlgorithmParameters.getInstance(algorithm); 880 algParams.init(algParamSpec); 881 } else { 882 algParams = getPBEAlgorithmParameters(algorithm, 883 defaultKeyPbeIterationCount()); 884 } 885 } else { 886 // Check default key protection algorithm for PKCS12 keystores 887 algorithm = defaultKeyProtectionAlgorithm(); 888 algParams = getPBEAlgorithmParameters(algorithm, 889 defaultKeyPbeIterationCount()); 890 } 891 892 ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm); 893 if (pbeOID == null) { 894 throw new IOException("PBE algorithm '" + algorithm + 895 " 'is not supported for key entry protection"); 896 } 897 898 // Use JCE 899 SecretKey skey = getPBEKey(passwordProtection.getPassword()); 900 Cipher cipher = Cipher.getInstance(algorithm); 901 cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); 902 byte[] encryptedKey = cipher.doFinal(data); 903 algid = new AlgorithmId(pbeOID, cipher.getParameters()); 904 905 if (debug != null) { 906 debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + 907 ")"); 908 } 909 910 // wrap encrypted private key in EncryptedPrivateKeyInfo 911 // as defined in PKCS#8 912 EncryptedPrivateKeyInfo encrInfo = 913 new EncryptedPrivateKeyInfo(algid, encryptedKey); 914 key = encrInfo.getEncoded(); 915 } catch (Exception e) { 916 UnrecoverableKeyException uke = 917 new UnrecoverableKeyException("Encrypt Private Key failed: " 918 + e.getMessage()); 919 uke.initCause(e); 920 throw uke; 921 } 922 923 return key; 924 } 925 926 /* 927 * Map a PBE algorithm name onto its object identifier 928 */ 929 private static ObjectIdentifier mapPBEAlgorithmToOID(String algorithm) 930 throws NoSuchAlgorithmException { 931 // Check for PBES2 algorithms 932 if (algorithm.toLowerCase(Locale.ENGLISH).startsWith("pbewithhmacsha")) { 933 return pbes2_OID; 934 } 935 return AlgorithmId.get(algorithm).getOID(); 936 } 937 938 /* 939 * Map a PBE algorithm parameters onto its algorithm name 940 */ 941 private static String mapPBEParamsToAlgorithm(ObjectIdentifier algorithm, 942 AlgorithmParameters algParams) throws NoSuchAlgorithmException { 943 // Check for PBES2 algorithms 944 if (algorithm.equals((Object)pbes2_OID) && algParams != null) { 945 return algParams.toString(); 946 } 947 return new AlgorithmId(algorithm).getName(); 948 } 949 950 /** 951 * Assigns the given certificate to the given alias. 952 * 953 * <p>If the given alias already exists in this keystore and identifies a 954 * <i>trusted certificate entry</i>, the certificate associated with it is 955 * overridden by the given certificate. 956 * 957 * @param alias the alias name 958 * @param cert the certificate 959 * 960 * @exception KeyStoreException if the given alias already exists and does 961 * not identify a <i>trusted certificate entry</i>, or this operation fails 962 * for some other reason. 963 */ 964 public synchronized void engineSetCertificateEntry(String alias, 965 Certificate cert) throws KeyStoreException 966 { 967 setCertEntry(alias, cert, null); 968 } 969 970 /* 971 * Sets a trusted cert entry (with attributes, when present) 972 */ 973 private void setCertEntry(String alias, Certificate cert, 974 Set<KeyStore.Entry.Attribute> attributes) throws KeyStoreException { 975 976 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 977 if (entry != null && entry instanceof KeyEntry) { 978 throw new KeyStoreException("Cannot overwrite own certificate"); 979 } 980 981 CertEntry certEntry = 982 new CertEntry((X509Certificate) cert, null, alias, AnyUsage, 983 attributes); 984 certificateCount++; 985 entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry); 986 987 if (debug != null) { 988 debug.println("Setting a trusted certificate at alias '" + alias + 989 "'"); 990 } 991 } 992 993 /** 994 * Deletes the entry identified by the given alias from this keystore. 995 * 996 * @param alias the alias name 997 * 998 * @exception KeyStoreException if the entry cannot be removed. 999 */ 1000 public synchronized void engineDeleteEntry(String alias) 1001 throws KeyStoreException 1002 { 1003 if (debug != null) { 1004 debug.println("Removing entry at alias '" + alias + "'"); 1005 } 1006 1007 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1008 if (entry instanceof PrivateKeyEntry) { 1009 PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; 1010 if (keyEntry.chain != null) { 1011 certificateCount -= keyEntry.chain.length; 1012 } 1013 privateKeyCount--; 1014 } else if (entry instanceof CertEntry) { 1015 certificateCount--; 1016 } else if (entry instanceof SecretKeyEntry) { 1017 secretKeyCount--; 1018 } 1019 entries.remove(alias.toLowerCase(Locale.ENGLISH)); 1020 } 1021 1022 /** 1023 * Lists all the alias names of this keystore. 1024 * 1025 * @return enumeration of the alias names 1026 */ 1027 public Enumeration<String> engineAliases() { 1028 return Collections.enumeration(entries.keySet()); 1029 } 1030 1031 /** 1032 * Checks if the given alias exists in this keystore. 1033 * 1034 * @param alias the alias name 1035 * 1036 * @return true if the alias exists, false otherwise 1037 */ 1038 public boolean engineContainsAlias(String alias) { 1039 return entries.containsKey(alias.toLowerCase(Locale.ENGLISH)); 1040 } 1041 1042 /** 1043 * Retrieves the number of entries in this keystore. 1044 * 1045 * @return the number of entries in this keystore 1046 */ 1047 public int engineSize() { 1048 return entries.size(); 1049 } 1050 1051 /** 1052 * Returns true if the entry identified by the given alias is a 1053 * <i>key entry</i>, and false otherwise. 1054 * 1055 * @return true if the entry identified by the given alias is a 1056 * <i>key entry</i>, false otherwise. 1057 */ 1058 public boolean engineIsKeyEntry(String alias) { 1059 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1060 if (entry != null && entry instanceof KeyEntry) { 1061 return true; 1062 } else { 1063 return false; 1064 } 1065 } 1066 1067 /** 1068 * Returns true if the entry identified by the given alias is a 1069 * <i>trusted certificate entry</i>, and false otherwise. 1070 * 1071 * @return true if the entry identified by the given alias is a 1072 * <i>trusted certificate entry</i>, false otherwise. 1073 */ 1074 public boolean engineIsCertificateEntry(String alias) { 1075 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1076 if (entry != null && entry instanceof CertEntry && 1077 ((CertEntry) entry).trustedKeyUsage != null) { 1078 return true; 1079 } else { 1080 return false; 1081 } 1082 } 1083 1084 /** 1085 * Determines if the keystore {@code Entry} for the specified 1086 * {@code alias} is an instance or subclass of the specified 1087 * {@code entryClass}. 1088 * 1089 * @param alias the alias name 1090 * @param entryClass the entry class 1091 * 1092 * @return true if the keystore {@code Entry} for the specified 1093 * {@code alias} is an instance or subclass of the 1094 * specified {@code entryClass}, false otherwise 1095 * 1096 * @since 1.5 1097 */ 1098 @Override 1099 public boolean 1100 engineEntryInstanceOf(String alias, 1101 Class<? extends KeyStore.Entry> entryClass) 1102 { 1103 if (entryClass == KeyStore.TrustedCertificateEntry.class) { 1104 return engineIsCertificateEntry(alias); 1105 } 1106 1107 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1108 if (entryClass == KeyStore.PrivateKeyEntry.class) { 1109 return (entry != null && entry instanceof PrivateKeyEntry); 1110 } 1111 if (entryClass == KeyStore.SecretKeyEntry.class) { 1112 return (entry != null && entry instanceof SecretKeyEntry); 1113 } 1114 return false; 1115 } 1116 1117 /** 1118 * Returns the (alias) name of the first keystore entry whose certificate 1119 * matches the given certificate. 1120 * 1121 * <p>This method attempts to match the given certificate with each 1122 * keystore entry. If the entry being considered 1123 * is a <i>trusted certificate entry</i>, the given certificate is 1124 * compared to that entry's certificate. If the entry being considered is 1125 * a <i>key entry</i>, the given certificate is compared to the first 1126 * element of that entry's certificate chain (if a chain exists). 1127 * 1128 * @param cert the certificate to match with. 1129 * 1130 * @return the (alias) name of the first entry with matching certificate, 1131 * or null if no such entry exists in this keystore. 1132 */ 1133 public String engineGetCertificateAlias(Certificate cert) { 1134 Certificate certElem = null; 1135 1136 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1137 String alias = e.nextElement(); 1138 Entry entry = entries.get(alias); 1139 if (entry instanceof PrivateKeyEntry) { 1140 if (((PrivateKeyEntry) entry).chain != null) { 1141 certElem = ((PrivateKeyEntry) entry).chain[0]; 1142 } 1143 } else if (entry instanceof CertEntry && 1144 ((CertEntry) entry).trustedKeyUsage != null) { 1145 certElem = ((CertEntry) entry).cert; 1146 } else { 1147 continue; 1148 } 1149 if (certElem != null && certElem.equals(cert)) { 1150 return alias; 1151 } 1152 } 1153 return null; 1154 } 1155 1156 /** 1157 * Stores this keystore to the given output stream, and protects its 1158 * integrity with the given password. 1159 * 1160 * @param stream the output stream to which this keystore is written. 1161 * @param password the password to generate the keystore integrity check 1162 * 1163 * @exception IOException if there was an I/O problem with data 1164 * @exception NoSuchAlgorithmException if the appropriate data integrity 1165 * algorithm could not be found 1166 * @exception CertificateException if any of the certificates included in 1167 * the keystore data could not be stored 1168 */ 1169 public synchronized void engineStore(OutputStream stream, char[] password) 1170 throws IOException, NoSuchAlgorithmException, CertificateException 1171 { 1172 1173 // -- Create PFX 1174 DerOutputStream pfx = new DerOutputStream(); 1175 1176 // PFX version (always write the latest version) 1177 DerOutputStream version = new DerOutputStream(); 1178 version.putInteger(VERSION_3); 1179 byte[] pfxVersion = version.toByteArray(); 1180 pfx.write(pfxVersion); 1181 1182 // -- Create AuthSafe 1183 DerOutputStream authSafe = new DerOutputStream(); 1184 1185 // -- Create ContentInfos 1186 DerOutputStream authSafeContentInfo = new DerOutputStream(); 1187 1188 // -- create safeContent Data ContentInfo 1189 if (privateKeyCount > 0 || secretKeyCount > 0) { 1190 1191 if (debug != null) { 1192 debug.println("Storing " + (privateKeyCount + secretKeyCount) + 1193 " protected key(s) in a PKCS#7 data"); 1194 } 1195 1196 byte[] safeContentData = createSafeContent(); 1197 ContentInfo dataContentInfo = new ContentInfo(safeContentData); 1198 dataContentInfo.encode(authSafeContentInfo); 1199 } 1200 1201 // -- create EncryptedContentInfo 1202 if (certificateCount > 0) { 1203 1204 if (certProtectionAlgorithm == null) { 1205 certProtectionAlgorithm = defaultCertProtectionAlgorithm(); 1206 } 1207 if (certPbeIterationCount < 0) { 1208 certPbeIterationCount = defaultCertPbeIterationCount(); 1209 } 1210 1211 if (debug != null) { 1212 debug.println("Storing " + certificateCount + 1213 " certificate(s) in a PKCS#7 encryptedData"); 1214 } 1215 1216 byte[] encrData = createEncryptedData(password); 1217 if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) { 1218 ContentInfo encrContentInfo = 1219 new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, 1220 new DerValue(encrData)); 1221 encrContentInfo.encode(authSafeContentInfo); 1222 } else { 1223 ContentInfo dataContentInfo = new ContentInfo(encrData); 1224 dataContentInfo.encode(authSafeContentInfo); 1225 } 1226 } 1227 1228 // wrap as SequenceOf ContentInfos 1229 DerOutputStream cInfo = new DerOutputStream(); 1230 cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo); 1231 byte[] authenticatedSafe = cInfo.toByteArray(); 1232 1233 // Create Encapsulated ContentInfo 1234 ContentInfo contentInfo = new ContentInfo(authenticatedSafe); 1235 contentInfo.encode(authSafe); 1236 byte[] authSafeData = authSafe.toByteArray(); 1237 pfx.write(authSafeData); 1238 1239 // -- MAC 1240 if (macAlgorithm == null) { 1241 macAlgorithm = defaultMacAlgorithm(); 1242 } 1243 if (macIterationCount < 0) { 1244 macIterationCount = defaultMacIterationCount(); 1245 } 1246 if (!macAlgorithm.equalsIgnoreCase("NONE")) { 1247 byte[] macData = calculateMac(password, authenticatedSafe); 1248 pfx.write(macData); 1249 } 1250 // write PFX to output stream 1251 DerOutputStream pfxout = new DerOutputStream(); 1252 pfxout.write(DerValue.tag_Sequence, pfx); 1253 byte[] pfxData = pfxout.toByteArray(); 1254 stream.write(pfxData); 1255 stream.flush(); 1256 } 1257 1258 /** 1259 * Gets a <code>KeyStore.Entry</code> for the specified alias 1260 * with the specified protection parameter. 1261 * 1262 * @param alias get the <code>KeyStore.Entry</code> for this alias 1263 * @param protParam the <code>ProtectionParameter</code> 1264 * used to protect the <code>Entry</code>, 1265 * which may be <code>null</code> 1266 * 1267 * @return the <code>KeyStore.Entry</code> for the specified alias, 1268 * or <code>null</code> if there is no such entry 1269 * 1270 * @exception KeyStoreException if the operation failed 1271 * @exception NoSuchAlgorithmException if the algorithm for recovering the 1272 * entry cannot be found 1273 * @exception UnrecoverableEntryException if the specified 1274 * <code>protParam</code> were insufficient or invalid 1275 * @exception UnrecoverableKeyException if the entry is a 1276 * <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code> 1277 * and the specified <code>protParam</code> does not contain 1278 * the information needed to recover the key (e.g. wrong password) 1279 * 1280 * @since 1.5 1281 */ 1282 @Override 1283 public KeyStore.Entry engineGetEntry(String alias, 1284 KeyStore.ProtectionParameter protParam) 1285 throws KeyStoreException, NoSuchAlgorithmException, 1286 UnrecoverableEntryException { 1287 1288 if (!engineContainsAlias(alias)) { 1289 return null; 1290 } 1291 1292 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1293 if (protParam == null) { 1294 if (engineIsCertificateEntry(alias)) { 1295 if (entry instanceof CertEntry && 1296 ((CertEntry) entry).trustedKeyUsage != null) { 1297 1298 if (debug != null) { 1299 debug.println("Retrieved a trusted certificate at " + 1300 "alias '" + alias + "'"); 1301 } 1302 1303 return new KeyStore.TrustedCertificateEntry( 1304 ((CertEntry)entry).cert, getAttributes(entry)); 1305 } 1306 } else { 1307 throw new UnrecoverableKeyException 1308 ("requested entry requires a password"); 1309 } 1310 } 1311 1312 if (protParam instanceof KeyStore.PasswordProtection) { 1313 if (engineIsCertificateEntry(alias)) { 1314 throw new UnsupportedOperationException 1315 ("trusted certificate entries are not password-protected"); 1316 } else if (engineIsKeyEntry(alias)) { 1317 KeyStore.PasswordProtection pp = 1318 (KeyStore.PasswordProtection)protParam; 1319 char[] password = pp.getPassword(); 1320 1321 Key key = engineGetKey(alias, password); 1322 if (key instanceof PrivateKey) { 1323 Certificate[] chain = engineGetCertificateChain(alias); 1324 1325 return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain, 1326 getAttributes(entry)); 1327 1328 } else if (key instanceof SecretKey) { 1329 1330 return new KeyStore.SecretKeyEntry((SecretKey)key, 1331 getAttributes(entry)); 1332 } 1333 } else if (!engineIsKeyEntry(alias)) { 1334 throw new UnsupportedOperationException 1335 ("untrusted certificate entries are not " + 1336 "password-protected"); 1337 } 1338 } 1339 1340 throw new UnsupportedOperationException(); 1341 } 1342 1343 /** 1344 * Saves a <code>KeyStore.Entry</code> under the specified alias. 1345 * The specified protection parameter is used to protect the 1346 * <code>Entry</code>. 1347 * 1348 * <p> If an entry already exists for the specified alias, 1349 * it is overridden. 1350 * 1351 * @param alias save the <code>KeyStore.Entry</code> under this alias 1352 * @param entry the <code>Entry</code> to save 1353 * @param protParam the <code>ProtectionParameter</code> 1354 * used to protect the <code>Entry</code>, 1355 * which may be <code>null</code> 1356 * 1357 * @exception KeyStoreException if this operation fails 1358 * 1359 * @since 1.5 1360 */ 1361 @Override 1362 public synchronized void engineSetEntry(String alias, KeyStore.Entry entry, 1363 KeyStore.ProtectionParameter protParam) throws KeyStoreException { 1364 1365 // get password 1366 if (protParam != null && 1367 !(protParam instanceof KeyStore.PasswordProtection)) { 1368 throw new KeyStoreException("unsupported protection parameter"); 1369 } 1370 KeyStore.PasswordProtection pProtect = null; 1371 if (protParam != null) { 1372 pProtect = (KeyStore.PasswordProtection)protParam; 1373 } 1374 1375 // set entry 1376 if (entry instanceof KeyStore.TrustedCertificateEntry) { 1377 if (protParam != null && pProtect.getPassword() != null) { 1378 // pre-1.5 style setCertificateEntry did not allow password 1379 throw new KeyStoreException 1380 ("trusted certificate entries are not password-protected"); 1381 } else { 1382 KeyStore.TrustedCertificateEntry tce = 1383 (KeyStore.TrustedCertificateEntry)entry; 1384 setCertEntry(alias, tce.getTrustedCertificate(), 1385 tce.getAttributes()); 1386 1387 return; 1388 } 1389 } else if (entry instanceof KeyStore.PrivateKeyEntry) { 1390 if (pProtect == null || pProtect.getPassword() == null) { 1391 // pre-1.5 style setKeyEntry required password 1392 throw new KeyStoreException 1393 ("non-null password required to create PrivateKeyEntry"); 1394 } else { 1395 KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry; 1396 setKeyEntry(alias, pke.getPrivateKey(), pProtect, 1397 pke.getCertificateChain(), pke.getAttributes()); 1398 1399 return; 1400 } 1401 } else if (entry instanceof KeyStore.SecretKeyEntry) { 1402 if (pProtect == null || pProtect.getPassword() == null) { 1403 // pre-1.5 style setKeyEntry required password 1404 throw new KeyStoreException 1405 ("non-null password required to create SecretKeyEntry"); 1406 } else { 1407 KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry; 1408 setKeyEntry(alias, ske.getSecretKey(), pProtect, 1409 (Certificate[])null, ske.getAttributes()); 1410 1411 return; 1412 } 1413 } 1414 1415 throw new KeyStoreException 1416 ("unsupported entry type: " + entry.getClass().getName()); 1417 } 1418 1419 /* 1420 * Assemble the entry attributes 1421 */ 1422 private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) { 1423 1424 if (entry.attributes == null) { 1425 entry.attributes = new HashSet<>(); 1426 } 1427 1428 // friendlyName 1429 entry.attributes.add(new PKCS12Attribute( 1430 PKCS9FriendlyName_OID.toString(), entry.alias)); 1431 1432 // localKeyID 1433 byte[] keyIdValue = entry.keyId; 1434 if (keyIdValue != null) { 1435 entry.attributes.add(new PKCS12Attribute( 1436 PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue))); 1437 } 1438 1439 // trustedKeyUsage 1440 if (entry instanceof CertEntry) { 1441 ObjectIdentifier[] trustedKeyUsageValue = 1442 ((CertEntry) entry).trustedKeyUsage; 1443 if (trustedKeyUsageValue != null) { 1444 if (trustedKeyUsageValue.length == 1) { // omit brackets 1445 entry.attributes.add(new PKCS12Attribute( 1446 TrustedKeyUsage_OID.toString(), 1447 trustedKeyUsageValue[0].toString())); 1448 } else { // multi-valued 1449 entry.attributes.add(new PKCS12Attribute( 1450 TrustedKeyUsage_OID.toString(), 1451 Arrays.toString(trustedKeyUsageValue))); 1452 } 1453 } 1454 } 1455 1456 return entry.attributes; 1457 } 1458 1459 /* 1460 * Calculate MAC using HMAC algorithm (required for password integrity) 1461 * 1462 * Hash-based MAC algorithm combines secret key with message digest to 1463 * create a message authentication code (MAC) 1464 */ 1465 private byte[] calculateMac(char[] passwd, byte[] data) 1466 throws IOException 1467 { 1468 byte[] mData = null; 1469 String algName = macAlgorithm.substring(7); 1470 1471 try { 1472 // Generate a random salt. 1473 byte[] salt = getSalt(); 1474 1475 // generate MAC (MAC key is generated within JCE) 1476 Mac m = Mac.getInstance(macAlgorithm); 1477 PBEParameterSpec params = 1478 new PBEParameterSpec(salt, macIterationCount); 1479 SecretKey key = getPBEKey(passwd); 1480 m.init(key, params); 1481 m.update(data); 1482 byte[] macResult = m.doFinal(); 1483 1484 // encode as MacData 1485 MacData macData = new MacData(algName, macResult, salt, 1486 macIterationCount); 1487 DerOutputStream bytes = new DerOutputStream(); 1488 bytes.write(macData.getEncoded()); 1489 mData = bytes.toByteArray(); 1490 } catch (Exception e) { 1491 throw new IOException("calculateMac failed: " + e, e); 1492 } 1493 return mData; 1494 } 1495 1496 1497 /* 1498 * Validate Certificate Chain 1499 */ 1500 private boolean validateChain(Certificate[] certChain) 1501 { 1502 for (int i = 0; i < certChain.length-1; i++) { 1503 X500Principal issuerDN = 1504 ((X509Certificate)certChain[i]).getIssuerX500Principal(); 1505 X500Principal subjectDN = 1506 ((X509Certificate)certChain[i+1]).getSubjectX500Principal(); 1507 if (!(issuerDN.equals(subjectDN))) 1508 return false; 1509 } 1510 1511 // Check for loops in the chain. If there are repeated certs, 1512 // the Set of certs in the chain will contain fewer certs than 1513 // the chain 1514 Set<Certificate> set = new HashSet<>(Arrays.asList(certChain)); 1515 return set.size() == certChain.length; 1516 } 1517 1518 1519 /* 1520 * Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage. 1521 * 1522 * Although attributes are optional, they could be required. 1523 * For e.g. localKeyId attribute is required to match the 1524 * private key with the associated end-entity certificate. 1525 * The trustedKeyUsage attribute is used to denote a trusted certificate. 1526 * 1527 * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName. 1528 * CertBags may or may not include attributes depending on the type 1529 * of Certificate. In end-entity certificates, localKeyID should be 1530 * unique, and the corresponding private key should have the same 1531 * localKeyID. For trusted CA certs in the cert-chain, localKeyID 1532 * attribute is not required, hence most vendors don't include it. 1533 * NSS/Netscape require it to be unique or null, where as IE/OpenSSL 1534 * ignore it. 1535 * 1536 * Here is a list of pkcs12 attribute values in CertBags. 1537 * 1538 * PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE 1539 * -------------------------------------------------------------- 1540 * LocalKeyId 1541 * (In EE cert only, 1542 * NULL in CA certs) true true true true 1543 * 1544 * friendlyName unique same/ same/ unique 1545 * unique unique/ 1546 * null 1547 * trustedKeyUsage - - - true 1548 * 1549 * Note: OpenSSL adds friendlyName for end-entity cert only, and 1550 * removes the localKeyID and friendlyName for CA certs. 1551 * If the CertBag did not have a friendlyName, most vendors will 1552 * add it, and assign it to the DN of the cert. 1553 */ 1554 private byte[] getBagAttributes(String alias, byte[] keyId, 1555 Set<KeyStore.Entry.Attribute> attributes) throws IOException { 1556 return getBagAttributes(alias, keyId, null, attributes); 1557 } 1558 1559 private byte[] getBagAttributes(String alias, byte[] keyId, 1560 ObjectIdentifier[] trustedUsage, 1561 Set<KeyStore.Entry.Attribute> attributes) throws IOException { 1562 1563 byte[] localKeyID = null; 1564 byte[] friendlyName = null; 1565 byte[] trustedKeyUsage = null; 1566 1567 // return null if all three attributes are null 1568 if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) { 1569 return null; 1570 } 1571 1572 // SafeBag Attributes 1573 DerOutputStream bagAttrs = new DerOutputStream(); 1574 1575 // Encode the friendlyname oid. 1576 if (alias != null) { 1577 DerOutputStream bagAttr1 = new DerOutputStream(); 1578 bagAttr1.putOID(PKCS9FriendlyName_OID); 1579 DerOutputStream bagAttrContent1 = new DerOutputStream(); 1580 DerOutputStream bagAttrValue1 = new DerOutputStream(); 1581 bagAttrContent1.putBMPString(alias); 1582 bagAttr1.write(DerValue.tag_Set, bagAttrContent1); 1583 bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1); 1584 friendlyName = bagAttrValue1.toByteArray(); 1585 } 1586 1587 // Encode the localkeyId oid. 1588 if (keyId != null) { 1589 DerOutputStream bagAttr2 = new DerOutputStream(); 1590 bagAttr2.putOID(PKCS9LocalKeyId_OID); 1591 DerOutputStream bagAttrContent2 = new DerOutputStream(); 1592 DerOutputStream bagAttrValue2 = new DerOutputStream(); 1593 bagAttrContent2.putOctetString(keyId); 1594 bagAttr2.write(DerValue.tag_Set, bagAttrContent2); 1595 bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2); 1596 localKeyID = bagAttrValue2.toByteArray(); 1597 } 1598 1599 // Encode the trustedKeyUsage oid. 1600 if (trustedUsage != null) { 1601 DerOutputStream bagAttr3 = new DerOutputStream(); 1602 bagAttr3.putOID(TrustedKeyUsage_OID); 1603 DerOutputStream bagAttrContent3 = new DerOutputStream(); 1604 DerOutputStream bagAttrValue3 = new DerOutputStream(); 1605 for (ObjectIdentifier usage : trustedUsage) { 1606 bagAttrContent3.putOID(usage); 1607 } 1608 bagAttr3.write(DerValue.tag_Set, bagAttrContent3); 1609 bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3); 1610 trustedKeyUsage = bagAttrValue3.toByteArray(); 1611 } 1612 1613 DerOutputStream attrs = new DerOutputStream(); 1614 if (friendlyName != null) { 1615 attrs.write(friendlyName); 1616 } 1617 if (localKeyID != null) { 1618 attrs.write(localKeyID); 1619 } 1620 if (trustedKeyUsage != null) { 1621 attrs.write(trustedKeyUsage); 1622 } 1623 1624 if (attributes != null) { 1625 for (KeyStore.Entry.Attribute attribute : attributes) { 1626 String attributeName = attribute.getName(); 1627 // skip friendlyName, localKeyId and trustedKeyUsage 1628 if (CORE_ATTRIBUTES[0].equals(attributeName) || 1629 CORE_ATTRIBUTES[1].equals(attributeName) || 1630 CORE_ATTRIBUTES[2].equals(attributeName)) { 1631 continue; 1632 } 1633 attrs.write(((PKCS12Attribute) attribute).getEncoded()); 1634 } 1635 } 1636 1637 bagAttrs.write(DerValue.tag_Set, attrs); 1638 return bagAttrs.toByteArray(); 1639 } 1640 1641 /* 1642 * Create EncryptedData content type, that contains EncryptedContentInfo. 1643 * Includes certificates in individual SafeBags of type CertBag. 1644 * Each CertBag may include pkcs12 attributes 1645 * (see comments in getBagAttributes) 1646 */ 1647 private byte[] createEncryptedData(char[] password) 1648 throws CertificateException, IOException 1649 { 1650 DerOutputStream out = new DerOutputStream(); 1651 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1652 1653 String alias = e.nextElement(); 1654 Entry entry = entries.get(alias); 1655 1656 // certificate chain 1657 Certificate[] certs; 1658 1659 if (entry instanceof PrivateKeyEntry) { 1660 PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; 1661 if (keyEntry.chain != null) { 1662 certs = keyEntry.chain; 1663 } else { 1664 certs = new Certificate[0]; 1665 } 1666 } else if (entry instanceof CertEntry) { 1667 certs = new Certificate[]{((CertEntry) entry).cert}; 1668 } else { 1669 certs = new Certificate[0]; 1670 } 1671 1672 for (int i = 0; i < certs.length; i++) { 1673 // create SafeBag of Type CertBag 1674 DerOutputStream safeBag = new DerOutputStream(); 1675 safeBag.putOID(CertBag_OID); 1676 1677 // create a CertBag 1678 DerOutputStream certBag = new DerOutputStream(); 1679 certBag.putOID(PKCS9CertType_OID); 1680 1681 // write encoded certs in a context-specific tag 1682 DerOutputStream certValue = new DerOutputStream(); 1683 X509Certificate cert = (X509Certificate) certs[i]; 1684 certValue.putOctetString(cert.getEncoded()); 1685 certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1686 true, (byte) 0), certValue); 1687 1688 // wrap CertBag in a Sequence 1689 DerOutputStream certout = new DerOutputStream(); 1690 certout.write(DerValue.tag_Sequence, certBag); 1691 byte[] certBagValue = certout.toByteArray(); 1692 1693 // Wrap the CertBag encoding in a context-specific tag. 1694 DerOutputStream bagValue = new DerOutputStream(); 1695 bagValue.write(certBagValue); 1696 // write SafeBag Value 1697 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1698 true, (byte) 0), bagValue); 1699 1700 // write SafeBag Attributes 1701 // All Certs should have a unique friendlyName. 1702 // This change is made to meet NSS requirements. 1703 byte[] bagAttrs = null; 1704 if (i == 0) { 1705 // Only End-Entity Cert should have a localKeyId. 1706 if (entry instanceof KeyEntry) { 1707 KeyEntry keyEntry = (KeyEntry) entry; 1708 bagAttrs = 1709 getBagAttributes(keyEntry.alias, keyEntry.keyId, 1710 keyEntry.attributes); 1711 } else { 1712 CertEntry certEntry = (CertEntry) entry; 1713 bagAttrs = 1714 getBagAttributes(certEntry.alias, certEntry.keyId, 1715 certEntry.trustedKeyUsage, 1716 certEntry.attributes); 1717 } 1718 } else { 1719 // Trusted root CA certs and Intermediate CA certs do not 1720 // need to have a localKeyId, and hence localKeyId is null 1721 // This change is made to meet NSS/Netscape requirements. 1722 // NSS pkcs12 library requires trusted CA certs in the 1723 // certificate chain to have unique or null localKeyID. 1724 // However, IE/OpenSSL do not impose this restriction. 1725 bagAttrs = getBagAttributes( 1726 cert.getSubjectX500Principal().getName(), null, 1727 entry.attributes); 1728 } 1729 if (bagAttrs != null) { 1730 safeBag.write(bagAttrs); 1731 } 1732 1733 // wrap as Sequence 1734 out.write(DerValue.tag_Sequence, safeBag); 1735 } // for cert-chain 1736 } 1737 1738 // wrap as SequenceOf SafeBag 1739 DerOutputStream safeBagValue = new DerOutputStream(); 1740 safeBagValue.write(DerValue.tag_SequenceOf, out); 1741 byte[] safeBagData = safeBagValue.toByteArray(); 1742 1743 // encrypt the content (EncryptedContentInfo) 1744 if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) { 1745 byte[] encrContentInfo = encryptContent(safeBagData, password); 1746 1747 // -- SEQUENCE of EncryptedData 1748 DerOutputStream encrData = new DerOutputStream(); 1749 DerOutputStream encrDataContent = new DerOutputStream(); 1750 encrData.putInteger(0); 1751 encrData.write(encrContentInfo); 1752 encrDataContent.write(DerValue.tag_Sequence, encrData); 1753 return encrDataContent.toByteArray(); 1754 } else { 1755 return safeBagData; 1756 } 1757 } 1758 1759 /* 1760 * Create SafeContent Data content type. 1761 * Includes encrypted secret key in a SafeBag of type SecretBag. 1762 * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag. 1763 * Each PKCS8ShroudedKeyBag includes pkcs12 attributes 1764 * (see comments in getBagAttributes) 1765 */ 1766 private byte[] createSafeContent() 1767 throws CertificateException, IOException { 1768 1769 DerOutputStream out = new DerOutputStream(); 1770 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1771 1772 String alias = e.nextElement(); 1773 Entry entry = entries.get(alias); 1774 if (entry == null || (!(entry instanceof KeyEntry))) { 1775 continue; 1776 } 1777 DerOutputStream safeBag = new DerOutputStream(); 1778 KeyEntry keyEntry = (KeyEntry) entry; 1779 1780 // DER encode the private key 1781 if (keyEntry instanceof PrivateKeyEntry) { 1782 // Create SafeBag of type pkcs8ShroudedKeyBag 1783 safeBag.putOID(PKCS8ShroudedKeyBag_OID); 1784 1785 // get the encrypted private key 1786 byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey; 1787 EncryptedPrivateKeyInfo encrInfo = null; 1788 try { 1789 encrInfo = new EncryptedPrivateKeyInfo(encrBytes); 1790 1791 } catch (IOException ioe) { 1792 throw new IOException("Private key not stored as " 1793 + "PKCS#8 EncryptedPrivateKeyInfo" 1794 + ioe.getMessage()); 1795 } 1796 1797 // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. 1798 DerOutputStream bagValue = new DerOutputStream(); 1799 bagValue.write(encrInfo.getEncoded()); 1800 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1801 true, (byte) 0), bagValue); 1802 1803 // DER encode the secret key 1804 } else if (keyEntry instanceof SecretKeyEntry) { 1805 // Create SafeBag of type SecretBag 1806 safeBag.putOID(SecretBag_OID); 1807 1808 // Create a SecretBag 1809 DerOutputStream secretBag = new DerOutputStream(); 1810 secretBag.putOID(PKCS8ShroudedKeyBag_OID); 1811 1812 // Write secret key in a context-specific tag 1813 DerOutputStream secretKeyValue = new DerOutputStream(); 1814 secretKeyValue.putOctetString( 1815 ((SecretKeyEntry) keyEntry).protectedSecretKey); 1816 secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1817 true, (byte) 0), secretKeyValue); 1818 1819 // Wrap SecretBag in a Sequence 1820 DerOutputStream secretBagSeq = new DerOutputStream(); 1821 secretBagSeq.write(DerValue.tag_Sequence, secretBag); 1822 byte[] secretBagValue = secretBagSeq.toByteArray(); 1823 1824 // Wrap the secret bag in a context-specific tag. 1825 DerOutputStream bagValue = new DerOutputStream(); 1826 bagValue.write(secretBagValue); 1827 1828 // Write SafeBag value 1829 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1830 true, (byte) 0), bagValue); 1831 } else { 1832 continue; // skip this entry 1833 } 1834 1835 // write SafeBag Attributes 1836 byte[] bagAttrs = 1837 getBagAttributes(alias, entry.keyId, entry.attributes); 1838 safeBag.write(bagAttrs); 1839 1840 // wrap as Sequence 1841 out.write(DerValue.tag_Sequence, safeBag); 1842 } 1843 1844 // wrap as Sequence 1845 DerOutputStream safeBagValue = new DerOutputStream(); 1846 safeBagValue.write(DerValue.tag_Sequence, out); 1847 return safeBagValue.toByteArray(); 1848 } 1849 1850 1851 /* 1852 * Encrypt the contents using Password-based (PBE) encryption 1853 * as defined in PKCS #5. 1854 * 1855 * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used 1856 * to derive the key and IV. 1857 * 1858 * @return encrypted contents encoded as EncryptedContentInfo 1859 */ 1860 private byte[] encryptContent(byte[] data, char[] password) 1861 throws IOException { 1862 1863 byte[] encryptedData = null; 1864 1865 1866 try { 1867 // create AlgorithmParameters 1868 AlgorithmParameters algParams = getPBEAlgorithmParameters( 1869 certProtectionAlgorithm, certPbeIterationCount); 1870 DerOutputStream bytes = new DerOutputStream(); 1871 1872 // Use JCE 1873 SecretKey skey = getPBEKey(password); 1874 Cipher cipher = Cipher.getInstance(certProtectionAlgorithm); 1875 cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); 1876 encryptedData = cipher.doFinal(data); 1877 1878 AlgorithmId algId = new AlgorithmId( 1879 mapPBEAlgorithmToOID(certProtectionAlgorithm), 1880 cipher.getParameters()); 1881 // cipher.getParameters() now has IV 1882 algId.encode(bytes); 1883 byte[] encodedAlgId = bytes.toByteArray(); 1884 1885 if (debug != null) { 1886 debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + 1887 ")"); 1888 } 1889 1890 // create EncryptedContentInfo 1891 DerOutputStream bytes2 = new DerOutputStream(); 1892 bytes2.putOID(ContentInfo.DATA_OID); 1893 bytes2.write(encodedAlgId); 1894 1895 // Wrap encrypted data in a context-specific tag. 1896 DerOutputStream tmpout2 = new DerOutputStream(); 1897 tmpout2.putOctetString(encryptedData); 1898 bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, 1899 false, (byte) 0), tmpout2); 1900 1901 // wrap EncryptedContentInfo in a Sequence 1902 DerOutputStream out = new DerOutputStream(); 1903 out.write(DerValue.tag_Sequence, bytes2); 1904 return out.toByteArray(); 1905 } catch (IOException ioe) { 1906 throw ioe; 1907 } catch (Exception e) { 1908 throw new IOException("Failed to encrypt" + 1909 " safe contents entry: " + e, e); 1910 } 1911 } 1912 1913 /** 1914 * Loads the keystore from the given input stream. 1915 * 1916 * <p>If a password is given, it is used to check the integrity of the 1917 * keystore data. Otherwise, the integrity of the keystore is not checked. 1918 * 1919 * @param stream the input stream from which the keystore is loaded 1920 * @param password the (optional) password used to check the integrity of 1921 * the keystore. 1922 * 1923 * @exception IOException if there is an I/O or format problem with the 1924 * keystore data 1925 * @exception NoSuchAlgorithmException if the algorithm used to check 1926 * the integrity of the keystore cannot be found 1927 * @exception CertificateException if any of the certificates in the 1928 * keystore could not be loaded 1929 */ 1930 public synchronized void engineLoad(InputStream stream, char[] password) 1931 throws IOException, NoSuchAlgorithmException, CertificateException 1932 { 1933 1934 // Reset config when loading a different keystore. 1935 certProtectionAlgorithm = null; 1936 certPbeIterationCount = -1; 1937 macAlgorithm = null; 1938 macIterationCount = -1; 1939 1940 if (stream == null) 1941 return; 1942 1943 // reset the counter 1944 counter = 0; 1945 1946 DerValue val = new DerValue(stream); 1947 DerInputStream s = val.toDerInputStream(); 1948 int version = s.getInteger(); 1949 1950 if (version != VERSION_3) { 1951 throw new IOException("PKCS12 keystore not in version 3 format"); 1952 } 1953 1954 entries.clear(); 1955 1956 /* 1957 * Read the authSafe. 1958 */ 1959 byte[] authSafeData; 1960 ContentInfo authSafe = new ContentInfo(s); 1961 ObjectIdentifier contentType = authSafe.getContentType(); 1962 1963 if (contentType.equals((Object)ContentInfo.DATA_OID)) { 1964 authSafeData = authSafe.getData(); 1965 } else /* signed data */ { 1966 throw new IOException("public key protected PKCS12 not supported"); 1967 } 1968 1969 DerInputStream as = new DerInputStream(authSafeData); 1970 DerValue[] safeContentsArray = as.getSequence(2); 1971 int count = safeContentsArray.length; 1972 1973 // reset the counters at the start 1974 privateKeyCount = 0; 1975 secretKeyCount = 0; 1976 certificateCount = 0; 1977 1978 boolean seeEncBag = false; 1979 1980 /* 1981 * Spin over the ContentInfos. 1982 */ 1983 for (int i = 0; i < count; i++) { 1984 ContentInfo safeContents; 1985 DerInputStream sci; 1986 byte[] eAlgId = null; 1987 1988 sci = new DerInputStream(safeContentsArray[i].toByteArray()); 1989 safeContents = new ContentInfo(sci); 1990 contentType = safeContents.getContentType(); 1991 if (contentType.equals((Object)ContentInfo.DATA_OID)) { 1992 1993 if (debug != null) { 1994 debug.println("Loading PKCS#7 data"); 1995 } 1996 1997 loadSafeContents(new DerInputStream(safeContents.getData())); 1998 } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) { 1999 if (password == null) { 2000 2001 if (debug != null) { 2002 debug.println("Warning: skipping PKCS#7 encryptedData" + 2003 " - no password was supplied"); 2004 } 2005 // No password to decrypt ENCRYPTED_DATA_OID. *Skip it*. 2006 // This means user will see a PrivateKeyEntry without 2007 // certificates and a whole TrustedCertificateEntry will 2008 // be lost. This is not a perfect solution but alternative 2009 // solutions are more disruptive: 2010 // 2011 // We cannot just fail, since KeyStore.load(is, null) 2012 // has been known to never fail because of a null password. 2013 // 2014 // We cannot just throw away the whole PrivateKeyEntry, 2015 // this is too silent and no one will notice anything. 2016 // 2017 // We also cannot fail when getCertificate() on such a 2018 // PrivateKeyEntry is called, since the method has not 2019 // specified this behavior. 2020 continue; 2021 } 2022 2023 DerInputStream edi = 2024 safeContents.getContent().toDerInputStream(); 2025 int edVersion = edi.getInteger(); 2026 DerValue[] seq = edi.getSequence(3); 2027 if (seq.length != 3) { 2028 // We require the encryptedContent field, even though 2029 // it is optional 2030 throw new IOException("Invalid length for EncryptedContentInfo"); 2031 } 2032 ObjectIdentifier edContentType = seq[0].getOID(); 2033 eAlgId = seq[1].toByteArray(); 2034 if (!seq[2].isContextSpecific((byte)0)) { 2035 throw new IOException("unsupported encrypted content type " 2036 + seq[2].tag); 2037 } 2038 byte newTag = DerValue.tag_OctetString; 2039 if (seq[2].isConstructed()) 2040 newTag |= 0x20; 2041 seq[2].resetTag(newTag); 2042 byte[] rawData = seq[2].getOctetString(); 2043 2044 // parse Algorithm parameters 2045 DerInputStream in = seq[1].toDerInputStream(); 2046 ObjectIdentifier algOid = in.getOID(); 2047 AlgorithmParameters algParams = parseAlgParameters(algOid, in); 2048 2049 PBEParameterSpec pbeSpec; 2050 int ic = 0; 2051 2052 if (algParams != null) { 2053 try { 2054 pbeSpec = 2055 algParams.getParameterSpec(PBEParameterSpec.class); 2056 } catch (InvalidParameterSpecException ipse) { 2057 throw new IOException( 2058 "Invalid PBE algorithm parameters"); 2059 } 2060 ic = pbeSpec.getIterationCount(); 2061 2062 if (ic > MAX_ITERATION_COUNT) { 2063 throw new IOException("cert PBE iteration count too large"); 2064 } 2065 2066 certProtectionAlgorithm 2067 = mapPBEParamsToAlgorithm(algOid, algParams); 2068 certPbeIterationCount = ic; 2069 seeEncBag = true; 2070 } 2071 2072 if (debug != null) { 2073 debug.println("Loading PKCS#7 encryptedData " + 2074 "(" + mapPBEParamsToAlgorithm(algOid, algParams) + 2075 " iterations: " + ic + ")"); 2076 } 2077 2078 try { 2079 RetryWithZero.run(pass -> { 2080 // Use JCE 2081 SecretKey skey = getPBEKey(pass); 2082 Cipher cipher = Cipher.getInstance( 2083 mapPBEParamsToAlgorithm(algOid, algParams)); 2084 cipher.init(Cipher.DECRYPT_MODE, skey, algParams); 2085 loadSafeContents(new DerInputStream(cipher.doFinal(rawData))); 2086 return null; 2087 }, password); 2088 } catch (Exception e) { 2089 throw new IOException("keystore password was incorrect", 2090 new UnrecoverableKeyException( 2091 "failed to decrypt safe contents entry: " + e)); 2092 } 2093 } else { 2094 throw new IOException("public key protected PKCS12" + 2095 " not supported"); 2096 } 2097 } 2098 2099 // No ENCRYPTED_DATA_OID but see certificate. Must be passwordless. 2100 if (!seeEncBag && certificateCount > 0) { 2101 certProtectionAlgorithm = "NONE"; 2102 } 2103 2104 // The MacData is optional. 2105 if (s.available() > 0) { 2106 // If there is no password, we cannot fail. KeyStore.load(is, null) 2107 // has been known to never fail because of a null password. 2108 if (password != null) { 2109 MacData macData = new MacData(s); 2110 int ic = macData.getIterations(); 2111 2112 try { 2113 if (ic > MAX_ITERATION_COUNT) { 2114 throw new InvalidAlgorithmParameterException( 2115 "MAC iteration count too large: " + ic); 2116 } 2117 2118 String algName = 2119 macData.getDigestAlgName().toUpperCase(Locale.ENGLISH); 2120 2121 // Change SHA-1 to SHA1 2122 algName = algName.replace("-", ""); 2123 2124 macAlgorithm = "HmacPBE" + algName; 2125 macIterationCount = ic; 2126 2127 // generate MAC (MAC key is created within JCE) 2128 Mac m = Mac.getInstance(macAlgorithm); 2129 PBEParameterSpec params = 2130 new PBEParameterSpec(macData.getSalt(), ic); 2131 2132 RetryWithZero.run(pass -> { 2133 SecretKey key = getPBEKey(pass); 2134 m.init(key, params); 2135 m.update(authSafeData); 2136 byte[] macResult = m.doFinal(); 2137 2138 if (debug != null) { 2139 debug.println("Checking keystore integrity " + 2140 "(" + m.getAlgorithm() + " iterations: " + ic + ")"); 2141 } 2142 2143 if (!MessageDigest.isEqual(macData.getDigest(), macResult)) { 2144 throw new UnrecoverableKeyException("Failed PKCS12" + 2145 " integrity checking"); 2146 } 2147 return (Void) null; 2148 }, password); 2149 } catch (Exception e) { 2150 throw new IOException("Integrity check failed: " + e, e); 2151 } 2152 } 2153 } else { 2154 macAlgorithm = "NONE"; 2155 } 2156 2157 /* 2158 * Match up private keys with certificate chains. 2159 */ 2160 PrivateKeyEntry[] list = 2161 keyList.toArray(new PrivateKeyEntry[keyList.size()]); 2162 for (int m = 0; m < list.length; m++) { 2163 PrivateKeyEntry entry = list[m]; 2164 if (entry.keyId != null) { 2165 ArrayList<X509Certificate> chain = 2166 new ArrayList<X509Certificate>(); 2167 X509Certificate cert = findMatchedCertificate(entry); 2168 2169 mainloop: 2170 while (cert != null) { 2171 // Check for loops in the certificate chain 2172 if (!chain.isEmpty()) { 2173 for (X509Certificate chainCert : chain) { 2174 if (cert.equals(chainCert)) { 2175 if (debug != null) { 2176 debug.println("Loop detected in " + 2177 "certificate chain. Skip adding " + 2178 "repeated cert to chain. Subject: " + 2179 cert.getSubjectX500Principal() 2180 .toString()); 2181 } 2182 break mainloop; 2183 } 2184 } 2185 } 2186 chain.add(cert); 2187 X500Principal issuerDN = cert.getIssuerX500Principal(); 2188 if (issuerDN.equals(cert.getSubjectX500Principal())) { 2189 break; 2190 } 2191 cert = certsMap.get(issuerDN); 2192 } 2193 /* Update existing KeyEntry in entries table */ 2194 if (chain.size() > 0) { 2195 entry.chain = chain.toArray(new Certificate[chain.size()]); 2196 } else { 2197 // Remove private key entries where there is no associated 2198 // certs. Most likely the keystore is loaded with a null 2199 // password. 2200 entries.remove(entry); 2201 } 2202 } 2203 } 2204 2205 if (debug != null) { 2206 debug.println("PKCS12KeyStore load: private key count: " + 2207 privateKeyCount + ". secret key count: " + secretKeyCount + 2208 ". certificate count: " + certificateCount); 2209 } 2210 2211 certEntries.clear(); 2212 certsMap.clear(); 2213 keyList.clear(); 2214 } 2215 2216 /** 2217 * Returns if a pkcs12 file is password-less. This means no cert is 2218 * encrypted and there is no Mac. Please note that the private key 2219 * can be encrypted. 2220 * 2221 * This is a simplified version of {@link #engineLoad} that only looks 2222 * at the ContentInfo types. 2223 * 2224 * @param f the pkcs12 file 2225 * @return if it's password-less 2226 * @throws IOException 2227 */ 2228 public static boolean isPasswordless(File f) throws IOException { 2229 2230 try (FileInputStream stream = new FileInputStream(f)) { 2231 DerValue val = new DerValue(stream); 2232 DerInputStream s = val.toDerInputStream(); 2233 2234 s.getInteger(); // skip version 2235 2236 ContentInfo authSafe = new ContentInfo(s); 2237 DerInputStream as = new DerInputStream(authSafe.getData()); 2238 for (DerValue seq : as.getSequence(2)) { 2239 DerInputStream sci = new DerInputStream(seq.toByteArray()); 2240 ContentInfo safeContents = new ContentInfo(sci); 2241 if (safeContents.getContentType() 2242 .equals(ContentInfo.ENCRYPTED_DATA_OID)) { 2243 // Certificate encrypted 2244 return false; 2245 } 2246 } 2247 2248 if (s.available() > 0) { 2249 // The MacData exists. 2250 return false; 2251 } 2252 } 2253 return true; 2254 } 2255 2256 /** 2257 * Locates a matched CertEntry from certEntries, and returns its cert. 2258 * @param entry the KeyEntry to match 2259 * @return a certificate, null if not found 2260 */ 2261 private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) { 2262 CertEntry keyIdMatch = null; 2263 CertEntry aliasMatch = null; 2264 for (CertEntry ce: certEntries) { 2265 if (Arrays.equals(entry.keyId, ce.keyId)) { 2266 keyIdMatch = ce; 2267 if (entry.alias.equalsIgnoreCase(ce.alias)) { 2268 // Full match! 2269 return ce.cert; 2270 } 2271 } else if (entry.alias.equalsIgnoreCase(ce.alias)) { 2272 aliasMatch = ce; 2273 } 2274 } 2275 // keyId match first, for compatibility 2276 if (keyIdMatch != null) return keyIdMatch.cert; 2277 else if (aliasMatch != null) return aliasMatch.cert; 2278 else return null; 2279 } 2280 2281 private void loadSafeContents(DerInputStream stream) 2282 throws IOException, NoSuchAlgorithmException, CertificateException 2283 { 2284 DerValue[] safeBags = stream.getSequence(2); 2285 int count = safeBags.length; 2286 2287 /* 2288 * Spin over the SafeBags. 2289 */ 2290 for (int i = 0; i < count; i++) { 2291 ObjectIdentifier bagId; 2292 DerInputStream sbi; 2293 DerValue bagValue; 2294 Object bagItem = null; 2295 2296 sbi = safeBags[i].toDerInputStream(); 2297 bagId = sbi.getOID(); 2298 bagValue = sbi.getDerValue(); 2299 if (!bagValue.isContextSpecific((byte)0)) { 2300 throw new IOException("unsupported PKCS12 bag value type " 2301 + bagValue.tag); 2302 } 2303 bagValue = bagValue.data.getDerValue(); 2304 if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) { 2305 PrivateKeyEntry kEntry = new PrivateKeyEntry(); 2306 kEntry.protectedPrivKey = bagValue.toByteArray(); 2307 bagItem = kEntry; 2308 privateKeyCount++; 2309 } else if (bagId.equals((Object)CertBag_OID)) { 2310 DerInputStream cs = new DerInputStream(bagValue.toByteArray()); 2311 DerValue[] certValues = cs.getSequence(2); 2312 if (certValues.length != 2) { 2313 throw new IOException("Invalid length for CertBag"); 2314 } 2315 ObjectIdentifier certId = certValues[0].getOID(); 2316 if (!certValues[1].isContextSpecific((byte)0)) { 2317 throw new IOException("unsupported PKCS12 cert value type " 2318 + certValues[1].tag); 2319 } 2320 DerValue certValue = certValues[1].data.getDerValue(); 2321 CertificateFactory cf = CertificateFactory.getInstance("X509"); 2322 X509Certificate cert; 2323 cert = (X509Certificate)cf.generateCertificate 2324 (new ByteArrayInputStream(certValue.getOctetString())); 2325 bagItem = cert; 2326 certificateCount++; 2327 } else if (bagId.equals((Object)SecretBag_OID)) { 2328 DerInputStream ss = new DerInputStream(bagValue.toByteArray()); 2329 DerValue[] secretValues = ss.getSequence(2); 2330 if (secretValues.length != 2) { 2331 throw new IOException("Invalid length for SecretBag"); 2332 } 2333 ObjectIdentifier secretId = secretValues[0].getOID(); 2334 if (!secretValues[1].isContextSpecific((byte)0)) { 2335 throw new IOException( 2336 "unsupported PKCS12 secret value type " 2337 + secretValues[1].tag); 2338 } 2339 DerValue secretValue = secretValues[1].data.getDerValue(); 2340 SecretKeyEntry kEntry = new SecretKeyEntry(); 2341 kEntry.protectedSecretKey = secretValue.getOctetString(); 2342 bagItem = kEntry; 2343 secretKeyCount++; 2344 } else { 2345 2346 if (debug != null) { 2347 debug.println("Unsupported PKCS12 bag type: " + bagId); 2348 } 2349 } 2350 2351 DerValue[] attrSet; 2352 try { 2353 attrSet = sbi.getSet(3); 2354 } catch (IOException e) { 2355 // entry does not have attributes 2356 // Note: CA certs can have no attributes 2357 // OpenSSL generates pkcs12 with no attr for CA certs. 2358 attrSet = null; 2359 } 2360 2361 String alias = null; 2362 byte[] keyId = null; 2363 ObjectIdentifier[] trustedKeyUsage = null; 2364 Set<PKCS12Attribute> attributes = new HashSet<>(); 2365 2366 if (attrSet != null) { 2367 for (int j = 0; j < attrSet.length; j++) { 2368 byte[] encoded = attrSet[j].toByteArray(); 2369 DerInputStream as = new DerInputStream(encoded); 2370 DerValue[] attrSeq = as.getSequence(2); 2371 if (attrSeq.length != 2) { 2372 throw new IOException("Invalid length for Attribute"); 2373 } 2374 ObjectIdentifier attrId = attrSeq[0].getOID(); 2375 DerInputStream vs = 2376 new DerInputStream(attrSeq[1].toByteArray()); 2377 DerValue[] valSet; 2378 try { 2379 valSet = vs.getSet(1); 2380 } catch (IOException e) { 2381 throw new IOException("Attribute " + attrId + 2382 " should have a value " + e.getMessage()); 2383 } 2384 if (attrId.equals((Object)PKCS9FriendlyName_OID)) { 2385 alias = valSet[0].getBMPString(); 2386 } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) { 2387 keyId = valSet[0].getOctetString(); 2388 } else if 2389 (attrId.equals((Object)TrustedKeyUsage_OID)) { 2390 trustedKeyUsage = new ObjectIdentifier[valSet.length]; 2391 for (int k = 0; k < valSet.length; k++) { 2392 trustedKeyUsage[k] = valSet[k].getOID(); 2393 } 2394 } else { 2395 attributes.add(new PKCS12Attribute(encoded)); 2396 } 2397 } 2398 } 2399 2400 /* 2401 * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId) 2402 * are optional PKCS12 bagAttributes. But entries in the keyStore 2403 * are identified by their alias. Hence we need to have an 2404 * Unfriendlyname in the alias, if alias is null. The keyId 2405 * attribute is required to match the private key with the 2406 * certificate. If we get a bagItem of type KeyEntry with a 2407 * null keyId, we should skip it entirely. 2408 */ 2409 if (bagItem instanceof KeyEntry) { 2410 KeyEntry entry = (KeyEntry)bagItem; 2411 2412 if (keyId == null) { 2413 if (bagItem instanceof PrivateKeyEntry) { 2414 // Insert a localKeyID for the privateKey 2415 // Note: This is a workaround to allow null localKeyID 2416 // attribute in pkcs12 with one private key entry and 2417 // associated cert-chain 2418 if (privateKeyCount == 1) { 2419 keyId = "01".getBytes("UTF8"); 2420 } else { 2421 continue; 2422 } 2423 } else { 2424 // keyId in a SecretKeyEntry is not significant 2425 keyId = "00".getBytes("UTF8"); 2426 } 2427 } 2428 entry.keyId = keyId; 2429 // restore date if it exists 2430 String keyIdStr = new String(keyId, "UTF8"); 2431 Date date = null; 2432 if (keyIdStr.startsWith("Time ")) { 2433 try { 2434 date = new Date( 2435 Long.parseLong(keyIdStr.substring(5))); 2436 } catch (Exception e) { 2437 date = null; 2438 } 2439 } 2440 if (date == null) { 2441 date = new Date(); 2442 } 2443 entry.date = date; 2444 2445 if (bagItem instanceof PrivateKeyEntry) { 2446 keyList.add((PrivateKeyEntry) entry); 2447 } 2448 if (entry.attributes == null) { 2449 entry.attributes = new HashSet<>(); 2450 } 2451 entry.attributes.addAll(attributes); 2452 if (alias == null) { 2453 alias = getUnfriendlyName(); 2454 } 2455 entry.alias = alias; 2456 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 2457 2458 } else if (bagItem instanceof X509Certificate) { 2459 X509Certificate cert = (X509Certificate)bagItem; 2460 // Insert a localKeyID for the corresponding cert 2461 // Note: This is a workaround to allow null localKeyID 2462 // attribute in pkcs12 with one private key entry and 2463 // associated cert-chain 2464 if ((keyId == null) && (privateKeyCount == 1)) { 2465 // insert localKeyID only for EE cert or self-signed cert 2466 if (i == 0) { 2467 keyId = "01".getBytes("UTF8"); 2468 } 2469 } 2470 // Trusted certificate 2471 if (trustedKeyUsage != null) { 2472 if (alias == null) { 2473 alias = getUnfriendlyName(); 2474 } 2475 CertEntry certEntry = 2476 new CertEntry(cert, keyId, alias, trustedKeyUsage, 2477 attributes); 2478 entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry); 2479 } else { 2480 certEntries.add(new CertEntry(cert, keyId, alias)); 2481 } 2482 X500Principal subjectDN = cert.getSubjectX500Principal(); 2483 if (subjectDN != null) { 2484 if (!certsMap.containsKey(subjectDN)) { 2485 certsMap.put(subjectDN, cert); 2486 } 2487 } 2488 } 2489 } 2490 } 2491 2492 private String getUnfriendlyName() { 2493 counter++; 2494 return (String.valueOf(counter)); 2495 } 2496 2497 // 8076190: Customizing the generation of a PKCS12 keystore 2498 2499 private static String defaultCertProtectionAlgorithm() { 2500 String result = SecurityProperties.privilegedGetOverridable( 2501 "keystore.pkcs12.certProtectionAlgorithm"); 2502 return (result != null && !result.isEmpty()) 2503 ? result : "PBEWithSHA1AndRC2_40"; 2504 } 2505 2506 private static int defaultCertPbeIterationCount() { 2507 String result = SecurityProperties.privilegedGetOverridable( 2508 "keystore.pkcs12.certPbeIterationCount"); 2509 return (result != null && !result.isEmpty()) 2510 ? string2IC("certPbeIterationCount", result) : 50000; 2511 } 2512 2513 // Read both "keystore.pkcs12.keyProtectionAlgorithm" and 2514 // "keystore.PKCS12.keyProtectionAlgorithm" for compatibility. 2515 private static String defaultKeyProtectionAlgorithm() { 2516 String result = AccessController.doPrivileged(new PrivilegedAction<String>() { 2517 public String run() { 2518 String result; 2519 String name1 = "keystore.pkcs12.keyProtectionAlgorithm"; 2520 String name2 = "keystore.PKCS12.keyProtectionAlgorithm"; 2521 result = System.getProperty(name1); 2522 if (result != null) { 2523 return result; 2524 } 2525 result = System.getProperty(name2); 2526 if (result != null) { 2527 return result; 2528 } 2529 result = Security.getProperty(name1); 2530 if (result != null) { 2531 return result; 2532 } 2533 return Security.getProperty(name2); 2534 } 2535 }); 2536 return (result != null && !result.isEmpty()) 2537 ? result : "PBEWithSHA1AndDESede"; 2538 } 2539 2540 private static int defaultKeyPbeIterationCount() { 2541 String result = SecurityProperties.privilegedGetOverridable( 2542 "keystore.pkcs12.keyPbeIterationCount"); 2543 return (result != null && !result.isEmpty()) 2544 ? string2IC("keyPbeIterationCount", result) : 50000; 2545 } 2546 2547 private static String defaultMacAlgorithm() { 2548 String result = SecurityProperties.privilegedGetOverridable( 2549 "keystore.pkcs12.macAlgorithm"); 2550 return (result != null && !result.isEmpty()) 2551 ? result : "HmacPBESHA1"; 2552 } 2553 2554 private static int defaultMacIterationCount() { 2555 String result = SecurityProperties.privilegedGetOverridable( 2556 "keystore.pkcs12.macIterationCount"); 2557 return (result != null && !result.isEmpty()) 2558 ? string2IC("macIterationCount", result) : 100000; 2559 } 2560 2561 private static int string2IC(String type, String value) { 2562 int number; 2563 try { 2564 number = Integer.parseInt(value); 2565 } catch (NumberFormatException e) { 2566 throw new IllegalArgumentException("keystore.pkcs12." + type 2567 + " is not a number: " + value); 2568 } 2569 if (number <= 0 || number > MAX_ITERATION_COUNT) { 2570 throw new IllegalArgumentException("Invalid keystore.pkcs12." 2571 + type + ": " + value); 2572 } 2573 return number; 2574 } 2575 }