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