1 /* 2 * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 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 * Private keys and certificates are stored in a map. 276 * Map entries are keyed by alias names. 277 */ 278 private Map<String, Entry> entries = 279 Collections.synchronizedMap(new LinkedHashMap<String, Entry>()); 280 281 private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>(); 282 private LinkedHashMap<X500Principal, X509Certificate> certsMap = 283 new LinkedHashMap<X500Principal, X509Certificate>(); 284 private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>(); 285 286 /** 287 * Returns the key associated with the given alias, using the given 288 * password to recover it. 289 * 290 * @param alias the alias name 291 * @param password the password for recovering the key 292 * 293 * @return the requested key, or null if the given alias does not exist 294 * or does not identify a <i>key entry</i>. 295 * 296 * @exception NoSuchAlgorithmException if the algorithm for recovering the 297 * key cannot be found 298 * @exception UnrecoverableKeyException if the key cannot be recovered 299 * (e.g., the given password is wrong). 300 */ 301 public Key engineGetKey(String alias, char[] password) 302 throws NoSuchAlgorithmException, UnrecoverableKeyException 303 { 304 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 305 Key key = null; 306 307 if (entry == null || (!(entry instanceof KeyEntry))) { 308 return null; 309 } 310 311 // get the encoded private key or secret key 312 byte[] encrBytes = null; 313 if (entry instanceof PrivateKeyEntry) { 314 encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey; 315 } else if (entry instanceof SecretKeyEntry) { 316 encrBytes = ((SecretKeyEntry) entry).protectedSecretKey; 317 } else { 318 throw new UnrecoverableKeyException("Error locating key"); 319 } 320 321 byte[] encryptedKey; 322 AlgorithmParameters algParams; 323 ObjectIdentifier algOid; 324 325 try { 326 // get the encrypted private key 327 EncryptedPrivateKeyInfo encrInfo = 328 new EncryptedPrivateKeyInfo(encrBytes); 329 encryptedKey = encrInfo.getEncryptedData(); 330 331 // parse Algorithm parameters 332 DerValue val = new DerValue(encrInfo.getAlgorithm().encode()); 333 DerInputStream in = val.toDerInputStream(); 334 algOid = in.getOID(); 335 algParams = parseAlgParameters(algOid, in); 336 337 } catch (IOException ioe) { 338 UnrecoverableKeyException uke = 339 new UnrecoverableKeyException("Private key not stored as " 340 + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe); 341 uke.initCause(ioe); 342 throw uke; 343 } 344 345 try { 346 PBEParameterSpec pbeSpec; 347 int ic = 0; 348 349 if (algParams != null) { 350 try { 351 pbeSpec = 352 algParams.getParameterSpec(PBEParameterSpec.class); 353 } catch (InvalidParameterSpecException ipse) { 354 throw new IOException("Invalid PBE algorithm parameters"); 355 } 356 ic = pbeSpec.getIterationCount(); 357 358 if (ic > MAX_ITERATION_COUNT) { 359 throw new IOException("PBE iteration count too large"); 360 } 361 } 362 363 byte[] keyInfo; 364 while (true) { 365 try { 366 // Use JCE 367 SecretKey skey = getPBEKey(password); 368 Cipher cipher = Cipher.getInstance( 369 mapPBEParamsToAlgorithm(algOid, algParams)); 370 cipher.init(Cipher.DECRYPT_MODE, skey, algParams); 371 keyInfo = cipher.doFinal(encryptedKey); 372 break; 373 } catch (Exception e) { 374 if (password.length == 0) { 375 // Retry using an empty password 376 // without a NULL terminator. 377 password = new char[1]; 378 continue; 379 } 380 throw e; 381 } 382 } 383 384 /* 385 * Parse the key algorithm and then use a JCA key factory 386 * to re-create the key. 387 */ 388 DerValue val = new DerValue(keyInfo); 389 DerInputStream in = val.toDerInputStream(); 390 int i = in.getInteger(); 391 DerValue[] value = in.getSequence(2); 392 AlgorithmId algId = new AlgorithmId(value[0].getOID()); 393 String keyAlgo = algId.getName(); 394 395 // decode private key 396 if (entry instanceof PrivateKeyEntry) { 397 KeyFactory kfac = KeyFactory.getInstance(keyAlgo); 398 PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo); 399 key = kfac.generatePrivate(kspec); 400 401 if (debug != null) { 402 debug.println("Retrieved a protected private key at alias" + 403 " '" + alias + "' (" + 404 new AlgorithmId(algOid).getName() + 405 " iterations: " + ic + ")"); 406 } 407 408 // decode secret key 409 } else { 410 byte[] keyBytes = in.getOctetString(); 411 SecretKeySpec secretKeySpec = 412 new SecretKeySpec(keyBytes, keyAlgo); 413 414 // Special handling required for PBE: needs a PBEKeySpec 415 if (keyAlgo.startsWith("PBE")) { 416 SecretKeyFactory sKeyFactory = 417 SecretKeyFactory.getInstance(keyAlgo); 418 KeySpec pbeKeySpec = 419 sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class); 420 key = sKeyFactory.generateSecret(pbeKeySpec); 421 } else { 422 key = secretKeySpec; 423 } 424 425 if (debug != null) { 426 debug.println("Retrieved a protected secret key at alias " + 427 "'" + alias + "' (" + 428 new AlgorithmId(algOid).getName() + 429 " iterations: " + ic + ")"); 430 } 431 } 432 } catch (Exception e) { 433 UnrecoverableKeyException uke = 434 new UnrecoverableKeyException("Get Key failed: " + 435 e.getMessage()); 436 uke.initCause(e); 437 throw uke; 438 } 439 return key; 440 } 441 442 /** 443 * Returns the certificate chain associated with the given alias. 444 * 445 * @param alias the alias name 446 * 447 * @return the certificate chain (ordered with the user's certificate first 448 * and the root certificate authority last), or null if the given alias 449 * does not exist or does not contain a certificate chain (i.e., the given 450 * alias identifies either a <i>trusted certificate entry</i> or a 451 * <i>key entry</i> without a certificate chain). 452 */ 453 public Certificate[] engineGetCertificateChain(String alias) { 454 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 455 if (entry != null && entry instanceof PrivateKeyEntry) { 456 if (((PrivateKeyEntry) entry).chain == null) { 457 return null; 458 } else { 459 460 if (debug != null) { 461 debug.println("Retrieved a " + 462 ((PrivateKeyEntry) entry).chain.length + 463 "-certificate chain at alias '" + alias + "'"); 464 } 465 466 return ((PrivateKeyEntry) entry).chain.clone(); 467 } 468 } else { 469 return null; 470 } 471 } 472 473 /** 474 * Returns the certificate associated with the given alias. 475 * 476 * <p>If the given alias name identifies a 477 * <i>trusted certificate entry</i>, the certificate associated with that 478 * entry is returned. If the given alias name identifies a 479 * <i>key entry</i>, the first element of the certificate chain of that 480 * entry is returned, or null if that entry does not have a certificate 481 * chain. 482 * 483 * @param alias the alias name 484 * 485 * @return the certificate, or null if the given alias does not exist or 486 * does not contain a certificate. 487 */ 488 public Certificate engineGetCertificate(String alias) { 489 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 490 if (entry == null) { 491 return null; 492 } 493 if (entry instanceof CertEntry && 494 ((CertEntry) entry).trustedKeyUsage != null) { 495 496 if (debug != null) { 497 if (Arrays.equals(AnyUsage, 498 ((CertEntry) entry).trustedKeyUsage)) { 499 debug.println("Retrieved a certificate at alias '" + alias + 500 "' (trusted for any purpose)"); 501 } else { 502 debug.println("Retrieved a certificate at alias '" + alias + 503 "' (trusted for limited purposes)"); 504 } 505 } 506 507 return ((CertEntry) entry).cert; 508 509 } else if (entry instanceof PrivateKeyEntry) { 510 if (((PrivateKeyEntry) entry).chain == null) { 511 return null; 512 } else { 513 514 if (debug != null) { 515 debug.println("Retrieved a certificate at alias '" + alias + 516 "'"); 517 } 518 519 return ((PrivateKeyEntry) entry).chain[0]; 520 } 521 522 } else { 523 return null; 524 } 525 } 526 527 /** 528 * Returns the creation date of the entry identified by the given alias. 529 * 530 * @param alias the alias name 531 * 532 * @return the creation date of this entry, or null if the given alias does 533 * not exist 534 */ 535 public Date engineGetCreationDate(String alias) { 536 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 537 if (entry != null) { 538 return new Date(entry.date.getTime()); 539 } else { 540 return null; 541 } 542 } 543 544 /** 545 * Assigns the given key to the given alias, protecting it with the given 546 * password. 547 * 548 * <p>If the given key is of type <code>java.security.PrivateKey</code>, 549 * it must be accompanied by a certificate chain certifying the 550 * corresponding public key. 551 * 552 * <p>If the given alias already exists, the keystore information 553 * associated with it is overridden by the given key (and possibly 554 * certificate chain). 555 * 556 * @param alias the alias name 557 * @param key the key to be associated with the alias 558 * @param password the password to protect the key 559 * @param chain the certificate chain for the corresponding public 560 * key (only required if the given key is of type 561 * <code>java.security.PrivateKey</code>). 562 * 563 * @exception KeyStoreException if the given key cannot be protected, or 564 * this operation fails for some other reason 565 */ 566 public synchronized void engineSetKeyEntry(String alias, Key key, 567 char[] password, Certificate[] chain) 568 throws KeyStoreException 569 { 570 KeyStore.PasswordProtection passwordProtection = 571 new KeyStore.PasswordProtection(password); 572 573 try { 574 setKeyEntry(alias, key, passwordProtection, chain, null); 575 576 } finally { 577 try { 578 passwordProtection.destroy(); 579 } catch (DestroyFailedException dfe) { 580 // ignore 581 } 582 } 583 } 584 585 /* 586 * Sets a key entry (with attributes, when present) 587 */ 588 private void setKeyEntry(String alias, Key key, 589 KeyStore.PasswordProtection passwordProtection, Certificate[] chain, 590 Set<KeyStore.Entry.Attribute> attributes) 591 throws KeyStoreException 592 { 593 try { 594 Entry entry; 595 596 if (key instanceof PrivateKey) { 597 PrivateKeyEntry keyEntry = new PrivateKeyEntry(); 598 keyEntry.date = new Date(); 599 600 if ((key.getFormat().equals("PKCS#8")) || 601 (key.getFormat().equals("PKCS8"))) { 602 603 if (debug != null) { 604 debug.println( 605 "Setting a protected private key at alias '" + 606 alias + "'"); 607 } 608 609 // Encrypt the private key 610 keyEntry.protectedPrivKey = 611 encryptPrivateKey(key.getEncoded(), passwordProtection); 612 } else { 613 throw new KeyStoreException("Private key is not encoded" + 614 "as PKCS#8"); 615 } 616 617 // clone the chain 618 if (chain != null) { 619 // validate cert-chain 620 if ((chain.length > 1) && (!validateChain(chain))) 621 throw new KeyStoreException("Certificate chain is " + 622 "not valid"); 623 keyEntry.chain = chain.clone(); 624 certificateCount += chain.length; 625 626 if (debug != null) { 627 debug.println("Setting a " + chain.length + 628 "-certificate chain at alias '" + alias + "'"); 629 } 630 } 631 privateKeyCount++; 632 entry = keyEntry; 633 634 } else if (key instanceof SecretKey) { 635 SecretKeyEntry keyEntry = new SecretKeyEntry(); 636 keyEntry.date = new Date(); 637 638 // Encode secret key in a PKCS#8 639 DerOutputStream pkcs8 = new DerOutputStream(); 640 DerOutputStream secretKeyInfo = new DerOutputStream(); 641 secretKeyInfo.putInteger(0); 642 AlgorithmId algId = AlgorithmId.get(key.getAlgorithm()); 643 algId.encode(secretKeyInfo); 644 secretKeyInfo.putOctetString(key.getEncoded()); 645 pkcs8.write(DerValue.tag_Sequence, secretKeyInfo); 646 647 // Encrypt the secret key (using same PBE as for private keys) 648 keyEntry.protectedSecretKey = 649 encryptPrivateKey(pkcs8.toByteArray(), passwordProtection); 650 651 if (debug != null) { 652 debug.println("Setting a protected secret key at alias '" + 653 alias + "'"); 654 } 655 secretKeyCount++; 656 entry = keyEntry; 657 658 } else { 659 throw new KeyStoreException("Unsupported Key type"); 660 } 661 662 entry.attributes = new HashSet<>(); 663 if (attributes != null) { 664 entry.attributes.addAll(attributes); 665 } 666 // set the keyId to current date 667 entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); 668 // set the alias 669 entry.alias = alias.toLowerCase(Locale.ENGLISH); 670 // add the entry 671 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 672 673 } catch (Exception nsae) { 674 throw new KeyStoreException("Key protection " + 675 " algorithm not found: " + nsae, nsae); 676 } 677 } 678 679 /** 680 * Assigns the given key (that has already been protected) to the given 681 * alias. 682 * 683 * <p>If the protected key is of type 684 * <code>java.security.PrivateKey</code>, it must be accompanied by a 685 * certificate chain certifying the corresponding public key. If the 686 * underlying keystore implementation is of type <code>jks</code>, 687 * <code>key</code> must be encoded as an 688 * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard. 689 * 690 * <p>If the given alias already exists, the keystore information 691 * associated with it is overridden by the given key (and possibly 692 * certificate chain). 693 * 694 * @param alias the alias name 695 * @param key the key (in protected format) to be associated with the alias 696 * @param chain the certificate chain for the corresponding public 697 * key (only useful if the protected key is of type 698 * <code>java.security.PrivateKey</code>). 699 * 700 * @exception KeyStoreException if this operation fails. 701 */ 702 public synchronized void engineSetKeyEntry(String alias, byte[] key, 703 Certificate[] chain) 704 throws KeyStoreException 705 { 706 // Private key must be encoded as EncryptedPrivateKeyInfo 707 // as defined in PKCS#8 708 try { 709 new EncryptedPrivateKeyInfo(key); 710 } catch (IOException ioe) { 711 throw new KeyStoreException("Private key is not stored" 712 + " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe); 713 } 714 715 PrivateKeyEntry entry = new PrivateKeyEntry(); 716 entry.date = new Date(); 717 718 if (debug != null) { 719 debug.println("Setting a protected private key at alias '" + 720 alias + "'"); 721 } 722 723 try { 724 // set the keyId to current date 725 entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); 726 } catch (UnsupportedEncodingException ex) { 727 // Won't happen 728 } 729 // set the alias 730 entry.alias = alias.toLowerCase(Locale.ENGLISH); 731 732 entry.protectedPrivKey = key.clone(); 733 if (chain != null) { 734 // validate cert-chain 735 if ((chain.length > 1) && (!validateChain(chain))) { 736 throw new KeyStoreException("Certificate chain is " 737 + "not valid"); 738 } 739 entry.chain = chain.clone(); 740 certificateCount += chain.length; 741 742 if (debug != null) { 743 debug.println("Setting a " + entry.chain.length + 744 "-certificate chain at alias '" + alias + "'"); 745 } 746 } 747 748 // add the entry 749 privateKeyCount++; 750 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 751 } 752 753 754 /* 755 * Generate random salt 756 */ 757 private byte[] getSalt() 758 { 759 // Generate a random salt. 760 byte[] salt = new byte[SALT_LEN]; 761 if (random == null) { 762 random = new SecureRandom(); 763 } 764 random.nextBytes(salt); 765 return salt; 766 } 767 768 /* 769 * Generate PBE Algorithm Parameters 770 */ 771 private AlgorithmParameters getPBEAlgorithmParameters(String algorithm) 772 throws IOException 773 { 774 AlgorithmParameters algParams = null; 775 776 // create PBE parameters from salt and iteration count 777 PBEParameterSpec paramSpec = 778 new PBEParameterSpec(getSalt(), PBE_ITERATION_COUNT); 779 try { 780 algParams = AlgorithmParameters.getInstance(algorithm); 781 algParams.init(paramSpec); 782 } catch (Exception e) { 783 throw new IOException("getPBEAlgorithmParameters failed: " + 784 e.getMessage(), e); 785 } 786 return algParams; 787 } 788 789 /* 790 * parse Algorithm Parameters 791 */ 792 private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm, 793 DerInputStream in) throws IOException 794 { 795 AlgorithmParameters algParams = null; 796 try { 797 DerValue params; 798 if (in.available() == 0) { 799 params = null; 800 } else { 801 params = in.getDerValue(); 802 if (params.tag == DerValue.tag_Null) { 803 params = null; 804 } 805 } 806 if (params != null) { 807 if (algorithm.equals((Object)pbes2_OID)) { 808 algParams = AlgorithmParameters.getInstance("PBES2"); 809 } else { 810 algParams = AlgorithmParameters.getInstance("PBE"); 811 } 812 algParams.init(params.toByteArray()); 813 } 814 } catch (Exception e) { 815 throw new IOException("parseAlgParameters failed: " + 816 e.getMessage(), e); 817 } 818 return algParams; 819 } 820 821 /* 822 * Generate PBE key 823 */ 824 private SecretKey getPBEKey(char[] password) throws IOException 825 { 826 SecretKey skey = null; 827 828 try { 829 PBEKeySpec keySpec = new PBEKeySpec(password); 830 SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); 831 skey = skFac.generateSecret(keySpec); 832 keySpec.clearPassword(); 833 } catch (Exception e) { 834 throw new IOException("getSecretKey failed: " + 835 e.getMessage(), e); 836 } 837 return skey; 838 } 839 840 /* 841 * Encrypt private key using Password-based encryption (PBE) 842 * as defined in PKCS#5. 843 * 844 * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is 845 * used to derive the key and IV. 846 * 847 * @return encrypted private key encoded as EncryptedPrivateKeyInfo 848 */ 849 private byte[] encryptPrivateKey(byte[] data, 850 KeyStore.PasswordProtection passwordProtection) 851 throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException 852 { 853 byte[] key = null; 854 855 try { 856 String algorithm; 857 AlgorithmParameters algParams; 858 AlgorithmId algid; 859 860 // Initialize PBE algorithm and parameters 861 algorithm = passwordProtection.getProtectionAlgorithm(); 862 if (algorithm != null) { 863 AlgorithmParameterSpec algParamSpec = 864 passwordProtection.getProtectionParameters(); 865 if (algParamSpec != null) { 866 algParams = AlgorithmParameters.getInstance(algorithm); 867 algParams.init(algParamSpec); 868 } else { 869 algParams = getPBEAlgorithmParameters(algorithm); 870 } 871 } else { 872 // Check default key protection algorithm for PKCS12 keystores 873 algorithm = AccessController.doPrivileged( 874 new PrivilegedAction<String>() { 875 public String run() { 876 String prop = 877 Security.getProperty( 878 KEY_PROTECTION_ALGORITHM[0]); 879 if (prop == null) { 880 prop = Security.getProperty( 881 KEY_PROTECTION_ALGORITHM[1]); 882 } 883 return prop; 884 } 885 }); 886 if (algorithm == null || algorithm.isEmpty()) { 887 algorithm = "PBEWithSHA1AndDESede"; 888 } 889 algParams = getPBEAlgorithmParameters(algorithm); 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 algorithm.toString(); 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, 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 // password is mandatory when storing 1173 if (password == null) { 1174 throw new IllegalArgumentException("password can't be null"); 1175 } 1176 1177 // -- Create PFX 1178 DerOutputStream pfx = new DerOutputStream(); 1179 1180 // PFX version (always write the latest version) 1181 DerOutputStream version = new DerOutputStream(); 1182 version.putInteger(VERSION_3); 1183 byte[] pfxVersion = version.toByteArray(); 1184 pfx.write(pfxVersion); 1185 1186 // -- Create AuthSafe 1187 DerOutputStream authSafe = new DerOutputStream(); 1188 1189 // -- Create ContentInfos 1190 DerOutputStream authSafeContentInfo = new DerOutputStream(); 1191 1192 // -- create safeContent Data ContentInfo 1193 if (privateKeyCount > 0 || secretKeyCount > 0) { 1194 1195 if (debug != null) { 1196 debug.println("Storing " + (privateKeyCount + secretKeyCount) + 1197 " protected key(s) in a PKCS#7 data"); 1198 } 1199 1200 byte[] safeContentData = createSafeContent(); 1201 ContentInfo dataContentInfo = new ContentInfo(safeContentData); 1202 dataContentInfo.encode(authSafeContentInfo); 1203 } 1204 1205 // -- create EncryptedContentInfo 1206 if (certificateCount > 0) { 1207 1208 if (debug != null) { 1209 debug.println("Storing " + certificateCount + 1210 " certificate(s) in a PKCS#7 encryptedData"); 1211 } 1212 1213 byte[] encrData = createEncryptedData(password); 1214 ContentInfo encrContentInfo = 1215 new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, 1216 new DerValue(encrData)); 1217 encrContentInfo.encode(authSafeContentInfo); 1218 } 1219 1220 // wrap as SequenceOf ContentInfos 1221 DerOutputStream cInfo = new DerOutputStream(); 1222 cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo); 1223 byte[] authenticatedSafe = cInfo.toByteArray(); 1224 1225 // Create Encapsulated ContentInfo 1226 ContentInfo contentInfo = new ContentInfo(authenticatedSafe); 1227 contentInfo.encode(authSafe); 1228 byte[] authSafeData = authSafe.toByteArray(); 1229 pfx.write(authSafeData); 1230 1231 // -- MAC 1232 byte[] macData = calculateMac(password, authenticatedSafe); 1233 pfx.write(macData); 1234 1235 // write PFX to output stream 1236 DerOutputStream pfxout = new DerOutputStream(); 1237 pfxout.write(DerValue.tag_Sequence, pfx); 1238 byte[] pfxData = pfxout.toByteArray(); 1239 stream.write(pfxData); 1240 stream.flush(); 1241 } 1242 1243 /** 1244 * Gets a <code>KeyStore.Entry</code> for the specified alias 1245 * with the specified protection parameter. 1246 * 1247 * @param alias get the <code>KeyStore.Entry</code> for this alias 1248 * @param protParam the <code>ProtectionParameter</code> 1249 * used to protect the <code>Entry</code>, 1250 * which may be <code>null</code> 1251 * 1252 * @return the <code>KeyStore.Entry</code> for the specified alias, 1253 * or <code>null</code> if there is no such entry 1254 * 1255 * @exception KeyStoreException if the operation failed 1256 * @exception NoSuchAlgorithmException if the algorithm for recovering the 1257 * entry cannot be found 1258 * @exception UnrecoverableEntryException if the specified 1259 * <code>protParam</code> were insufficient or invalid 1260 * @exception UnrecoverableKeyException if the entry is a 1261 * <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code> 1262 * and the specified <code>protParam</code> does not contain 1263 * the information needed to recover the key (e.g. wrong password) 1264 * 1265 * @since 1.5 1266 */ 1267 @Override 1268 public KeyStore.Entry engineGetEntry(String alias, 1269 KeyStore.ProtectionParameter protParam) 1270 throws KeyStoreException, NoSuchAlgorithmException, 1271 UnrecoverableEntryException { 1272 1273 if (!engineContainsAlias(alias)) { 1274 return null; 1275 } 1276 1277 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1278 if (protParam == null) { 1279 if (engineIsCertificateEntry(alias)) { 1280 if (entry instanceof CertEntry && 1281 ((CertEntry) entry).trustedKeyUsage != null) { 1282 1283 if (debug != null) { 1284 debug.println("Retrieved a trusted certificate at " + 1285 "alias '" + alias + "'"); 1286 } 1287 1288 return new KeyStore.TrustedCertificateEntry( 1289 ((CertEntry)entry).cert, getAttributes(entry)); 1290 } 1291 } else { 1292 throw new UnrecoverableKeyException 1293 ("requested entry requires a password"); 1294 } 1295 } 1296 1297 if (protParam instanceof KeyStore.PasswordProtection) { 1298 if (engineIsCertificateEntry(alias)) { 1299 throw new UnsupportedOperationException 1300 ("trusted certificate entries are not password-protected"); 1301 } else if (engineIsKeyEntry(alias)) { 1302 KeyStore.PasswordProtection pp = 1303 (KeyStore.PasswordProtection)protParam; 1304 char[] password = pp.getPassword(); 1305 1306 Key key = engineGetKey(alias, password); 1307 if (key instanceof PrivateKey) { 1308 Certificate[] chain = engineGetCertificateChain(alias); 1309 1310 return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain, 1311 getAttributes(entry)); 1312 1313 } else if (key instanceof SecretKey) { 1314 1315 return new KeyStore.SecretKeyEntry((SecretKey)key, 1316 getAttributes(entry)); 1317 } 1318 } else if (!engineIsKeyEntry(alias)) { 1319 throw new UnsupportedOperationException 1320 ("untrusted certificate entries are not " + 1321 "password-protected"); 1322 } 1323 } 1324 1325 throw new UnsupportedOperationException(); 1326 } 1327 1328 /** 1329 * Saves a <code>KeyStore.Entry</code> under the specified alias. 1330 * The specified protection parameter is used to protect the 1331 * <code>Entry</code>. 1332 * 1333 * <p> If an entry already exists for the specified alias, 1334 * it is overridden. 1335 * 1336 * @param alias save the <code>KeyStore.Entry</code> under this alias 1337 * @param entry the <code>Entry</code> to save 1338 * @param protParam the <code>ProtectionParameter</code> 1339 * used to protect the <code>Entry</code>, 1340 * which may be <code>null</code> 1341 * 1342 * @exception KeyStoreException if this operation fails 1343 * 1344 * @since 1.5 1345 */ 1346 @Override 1347 public synchronized void engineSetEntry(String alias, KeyStore.Entry entry, 1348 KeyStore.ProtectionParameter protParam) throws KeyStoreException { 1349 1350 // get password 1351 if (protParam != null && 1352 !(protParam instanceof KeyStore.PasswordProtection)) { 1353 throw new KeyStoreException("unsupported protection parameter"); 1354 } 1355 KeyStore.PasswordProtection pProtect = null; 1356 if (protParam != null) { 1357 pProtect = (KeyStore.PasswordProtection)protParam; 1358 } 1359 1360 // set entry 1361 if (entry instanceof KeyStore.TrustedCertificateEntry) { 1362 if (protParam != null && pProtect.getPassword() != null) { 1363 // pre-1.5 style setCertificateEntry did not allow password 1364 throw new KeyStoreException 1365 ("trusted certificate entries are not password-protected"); 1366 } else { 1367 KeyStore.TrustedCertificateEntry tce = 1368 (KeyStore.TrustedCertificateEntry)entry; 1369 setCertEntry(alias, tce.getTrustedCertificate(), 1370 tce.getAttributes()); 1371 1372 return; 1373 } 1374 } else if (entry instanceof KeyStore.PrivateKeyEntry) { 1375 if (pProtect == null || pProtect.getPassword() == null) { 1376 // pre-1.5 style setKeyEntry required password 1377 throw new KeyStoreException 1378 ("non-null password required to create PrivateKeyEntry"); 1379 } else { 1380 KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry; 1381 setKeyEntry(alias, pke.getPrivateKey(), pProtect, 1382 pke.getCertificateChain(), pke.getAttributes()); 1383 1384 return; 1385 } 1386 } else if (entry instanceof KeyStore.SecretKeyEntry) { 1387 if (pProtect == null || pProtect.getPassword() == null) { 1388 // pre-1.5 style setKeyEntry required password 1389 throw new KeyStoreException 1390 ("non-null password required to create SecretKeyEntry"); 1391 } else { 1392 KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry; 1393 setKeyEntry(alias, ske.getSecretKey(), pProtect, 1394 (Certificate[])null, ske.getAttributes()); 1395 1396 return; 1397 } 1398 } 1399 1400 throw new KeyStoreException 1401 ("unsupported entry type: " + entry.getClass().getName()); 1402 } 1403 1404 /* 1405 * Assemble the entry attributes 1406 */ 1407 private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) { 1408 1409 if (entry.attributes == null) { 1410 entry.attributes = new HashSet<>(); 1411 } 1412 1413 // friendlyName 1414 entry.attributes.add(new PKCS12Attribute( 1415 PKCS9FriendlyName_OID.toString(), entry.alias)); 1416 1417 // localKeyID 1418 byte[] keyIdValue = entry.keyId; 1419 if (keyIdValue != null) { 1420 entry.attributes.add(new PKCS12Attribute( 1421 PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue))); 1422 } 1423 1424 // trustedKeyUsage 1425 if (entry instanceof CertEntry) { 1426 ObjectIdentifier[] trustedKeyUsageValue = 1427 ((CertEntry) entry).trustedKeyUsage; 1428 if (trustedKeyUsageValue != null) { 1429 if (trustedKeyUsageValue.length == 1) { // omit brackets 1430 entry.attributes.add(new PKCS12Attribute( 1431 TrustedKeyUsage_OID.toString(), 1432 trustedKeyUsageValue[0].toString())); 1433 } else { // multi-valued 1434 entry.attributes.add(new PKCS12Attribute( 1435 TrustedKeyUsage_OID.toString(), 1436 Arrays.toString(trustedKeyUsageValue))); 1437 } 1438 } 1439 } 1440 1441 return entry.attributes; 1442 } 1443 1444 /* 1445 * Generate Hash. 1446 */ 1447 private byte[] generateHash(byte[] data) throws IOException 1448 { 1449 byte[] digest = null; 1450 1451 try { 1452 MessageDigest md = MessageDigest.getInstance("SHA1"); 1453 md.update(data); 1454 digest = md.digest(); 1455 } catch (Exception e) { 1456 throw new IOException("generateHash failed: " + e, e); 1457 } 1458 return digest; 1459 } 1460 1461 1462 /* 1463 * Calculate MAC using HMAC algorithm (required for password integrity) 1464 * 1465 * Hash-based MAC algorithm combines secret key with message digest to 1466 * create a message authentication code (MAC) 1467 */ 1468 private byte[] calculateMac(char[] passwd, byte[] data) 1469 throws IOException 1470 { 1471 byte[] mData = null; 1472 String algName = "SHA1"; 1473 1474 try { 1475 // Generate a random salt. 1476 byte[] salt = getSalt(); 1477 1478 // generate MAC (MAC key is generated within JCE) 1479 Mac m = Mac.getInstance("HmacPBESHA1"); 1480 PBEParameterSpec params = 1481 new PBEParameterSpec(salt, MAC_ITERATION_COUNT); 1482 SecretKey key = getPBEKey(passwd); 1483 m.init(key, params); 1484 m.update(data); 1485 byte[] macResult = m.doFinal(); 1486 1487 // encode as MacData 1488 MacData macData = new MacData(algName, macResult, salt, 1489 MAC_ITERATION_COUNT); 1490 DerOutputStream bytes = new DerOutputStream(); 1491 bytes.write(macData.getEncoded()); 1492 mData = bytes.toByteArray(); 1493 } catch (Exception e) { 1494 throw new IOException("calculateMac failed: " + e, e); 1495 } 1496 return mData; 1497 } 1498 1499 1500 /* 1501 * Validate Certificate Chain 1502 */ 1503 private boolean validateChain(Certificate[] certChain) 1504 { 1505 for (int i = 0; i < certChain.length-1; i++) { 1506 X500Principal issuerDN = 1507 ((X509Certificate)certChain[i]).getIssuerX500Principal(); 1508 X500Principal subjectDN = 1509 ((X509Certificate)certChain[i+1]).getSubjectX500Principal(); 1510 if (!(issuerDN.equals(subjectDN))) 1511 return false; 1512 } 1513 1514 // Check for loops in the chain. If there are repeated certs, 1515 // the Set of certs in the chain will contain fewer certs than 1516 // the chain 1517 Set<Certificate> set = new HashSet<>(Arrays.asList(certChain)); 1518 return set.size() == certChain.length; 1519 } 1520 1521 1522 /* 1523 * Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage. 1524 * 1525 * Although attributes are optional, they could be required. 1526 * For e.g. localKeyId attribute is required to match the 1527 * private key with the associated end-entity certificate. 1528 * The trustedKeyUsage attribute is used to denote a trusted certificate. 1529 * 1530 * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName. 1531 * CertBags may or may not include attributes depending on the type 1532 * of Certificate. In end-entity certificates, localKeyID should be 1533 * unique, and the corresponding private key should have the same 1534 * localKeyID. For trusted CA certs in the cert-chain, localKeyID 1535 * attribute is not required, hence most vendors don't include it. 1536 * NSS/Netscape require it to be unique or null, where as IE/OpenSSL 1537 * ignore it. 1538 * 1539 * Here is a list of pkcs12 attribute values in CertBags. 1540 * 1541 * PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE 1542 * -------------------------------------------------------------- 1543 * LocalKeyId 1544 * (In EE cert only, 1545 * NULL in CA certs) true true true true 1546 * 1547 * friendlyName unique same/ same/ unique 1548 * unique unique/ 1549 * null 1550 * trustedKeyUsage - - - true 1551 * 1552 * Note: OpenSSL adds friendlyName for end-entity cert only, and 1553 * removes the localKeyID and friendlyName for CA certs. 1554 * If the CertBag did not have a friendlyName, most vendors will 1555 * add it, and assign it to the DN of the cert. 1556 */ 1557 private byte[] getBagAttributes(String alias, byte[] keyId, 1558 Set<KeyStore.Entry.Attribute> attributes) throws IOException { 1559 return getBagAttributes(alias, keyId, null, attributes); 1560 } 1561 1562 private byte[] getBagAttributes(String alias, byte[] keyId, 1563 ObjectIdentifier[] trustedUsage, 1564 Set<KeyStore.Entry.Attribute> attributes) throws IOException { 1565 1566 byte[] localKeyID = null; 1567 byte[] friendlyName = null; 1568 byte[] trustedKeyUsage = null; 1569 1570 // return null if all three attributes are null 1571 if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) { 1572 return null; 1573 } 1574 1575 // SafeBag Attributes 1576 DerOutputStream bagAttrs = new DerOutputStream(); 1577 1578 // Encode the friendlyname oid. 1579 if (alias != null) { 1580 DerOutputStream bagAttr1 = new DerOutputStream(); 1581 bagAttr1.putOID(PKCS9FriendlyName_OID); 1582 DerOutputStream bagAttrContent1 = new DerOutputStream(); 1583 DerOutputStream bagAttrValue1 = new DerOutputStream(); 1584 bagAttrContent1.putBMPString(alias); 1585 bagAttr1.write(DerValue.tag_Set, bagAttrContent1); 1586 bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1); 1587 friendlyName = bagAttrValue1.toByteArray(); 1588 } 1589 1590 // Encode the localkeyId oid. 1591 if (keyId != null) { 1592 DerOutputStream bagAttr2 = new DerOutputStream(); 1593 bagAttr2.putOID(PKCS9LocalKeyId_OID); 1594 DerOutputStream bagAttrContent2 = new DerOutputStream(); 1595 DerOutputStream bagAttrValue2 = new DerOutputStream(); 1596 bagAttrContent2.putOctetString(keyId); 1597 bagAttr2.write(DerValue.tag_Set, bagAttrContent2); 1598 bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2); 1599 localKeyID = bagAttrValue2.toByteArray(); 1600 } 1601 1602 // Encode the trustedKeyUsage oid. 1603 if (trustedUsage != null) { 1604 DerOutputStream bagAttr3 = new DerOutputStream(); 1605 bagAttr3.putOID(TrustedKeyUsage_OID); 1606 DerOutputStream bagAttrContent3 = new DerOutputStream(); 1607 DerOutputStream bagAttrValue3 = new DerOutputStream(); 1608 for (ObjectIdentifier usage : trustedUsage) { 1609 bagAttrContent3.putOID(usage); 1610 } 1611 bagAttr3.write(DerValue.tag_Set, bagAttrContent3); 1612 bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3); 1613 trustedKeyUsage = bagAttrValue3.toByteArray(); 1614 } 1615 1616 DerOutputStream attrs = new DerOutputStream(); 1617 if (friendlyName != null) { 1618 attrs.write(friendlyName); 1619 } 1620 if (localKeyID != null) { 1621 attrs.write(localKeyID); 1622 } 1623 if (trustedKeyUsage != null) { 1624 attrs.write(trustedKeyUsage); 1625 } 1626 1627 if (attributes != null) { 1628 for (KeyStore.Entry.Attribute attribute : attributes) { 1629 String attributeName = attribute.getName(); 1630 // skip friendlyName, localKeyId and trustedKeyUsage 1631 if (CORE_ATTRIBUTES[0].equals(attributeName) || 1632 CORE_ATTRIBUTES[1].equals(attributeName) || 1633 CORE_ATTRIBUTES[2].equals(attributeName)) { 1634 continue; 1635 } 1636 attrs.write(((PKCS12Attribute) attribute).getEncoded()); 1637 } 1638 } 1639 1640 bagAttrs.write(DerValue.tag_Set, attrs); 1641 return bagAttrs.toByteArray(); 1642 } 1643 1644 /* 1645 * Create EncryptedData content type, that contains EncryptedContentInfo. 1646 * Includes certificates in individual SafeBags of type CertBag. 1647 * Each CertBag may include pkcs12 attributes 1648 * (see comments in getBagAttributes) 1649 */ 1650 private byte[] createEncryptedData(char[] password) 1651 throws CertificateException, IOException 1652 { 1653 DerOutputStream out = new DerOutputStream(); 1654 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1655 1656 String alias = e.nextElement(); 1657 Entry entry = entries.get(alias); 1658 1659 // certificate chain 1660 Certificate[] certs; 1661 1662 if (entry instanceof PrivateKeyEntry) { 1663 PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; 1664 if (keyEntry.chain != null) { 1665 certs = keyEntry.chain; 1666 } else { 1667 certs = new Certificate[0]; 1668 } 1669 } else if (entry instanceof CertEntry) { 1670 certs = new Certificate[]{((CertEntry) entry).cert}; 1671 } else { 1672 certs = new Certificate[0]; 1673 } 1674 1675 for (int i = 0; i < certs.length; i++) { 1676 // create SafeBag of Type CertBag 1677 DerOutputStream safeBag = new DerOutputStream(); 1678 safeBag.putOID(CertBag_OID); 1679 1680 // create a CertBag 1681 DerOutputStream certBag = new DerOutputStream(); 1682 certBag.putOID(PKCS9CertType_OID); 1683 1684 // write encoded certs in a context-specific tag 1685 DerOutputStream certValue = new DerOutputStream(); 1686 X509Certificate cert = (X509Certificate) certs[i]; 1687 certValue.putOctetString(cert.getEncoded()); 1688 certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1689 true, (byte) 0), certValue); 1690 1691 // wrap CertBag in a Sequence 1692 DerOutputStream certout = new DerOutputStream(); 1693 certout.write(DerValue.tag_Sequence, certBag); 1694 byte[] certBagValue = certout.toByteArray(); 1695 1696 // Wrap the CertBag encoding in a context-specific tag. 1697 DerOutputStream bagValue = new DerOutputStream(); 1698 bagValue.write(certBagValue); 1699 // write SafeBag Value 1700 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1701 true, (byte) 0), bagValue); 1702 1703 // write SafeBag Attributes 1704 // All Certs should have a unique friendlyName. 1705 // This change is made to meet NSS requirements. 1706 byte[] bagAttrs = null; 1707 if (i == 0) { 1708 // Only End-Entity Cert should have a localKeyId. 1709 if (entry instanceof KeyEntry) { 1710 KeyEntry keyEntry = (KeyEntry) entry; 1711 bagAttrs = 1712 getBagAttributes(keyEntry.alias, keyEntry.keyId, 1713 keyEntry.attributes); 1714 } else { 1715 CertEntry certEntry = (CertEntry) entry; 1716 bagAttrs = 1717 getBagAttributes(certEntry.alias, certEntry.keyId, 1718 certEntry.trustedKeyUsage, 1719 certEntry.attributes); 1720 } 1721 } else { 1722 // Trusted root CA certs and Intermediate CA certs do not 1723 // need to have a localKeyId, and hence localKeyId is null 1724 // This change is made to meet NSS/Netscape requirements. 1725 // NSS pkcs12 library requires trusted CA certs in the 1726 // certificate chain to have unique or null localKeyID. 1727 // However, IE/OpenSSL do not impose this restriction. 1728 bagAttrs = getBagAttributes( 1729 cert.getSubjectX500Principal().getName(), null, 1730 entry.attributes); 1731 } 1732 if (bagAttrs != null) { 1733 safeBag.write(bagAttrs); 1734 } 1735 1736 // wrap as Sequence 1737 out.write(DerValue.tag_Sequence, safeBag); 1738 } // for cert-chain 1739 } 1740 1741 // wrap as SequenceOf SafeBag 1742 DerOutputStream safeBagValue = new DerOutputStream(); 1743 safeBagValue.write(DerValue.tag_SequenceOf, out); 1744 byte[] safeBagData = safeBagValue.toByteArray(); 1745 1746 // encrypt the content (EncryptedContentInfo) 1747 byte[] encrContentInfo = encryptContent(safeBagData, password); 1748 1749 // -- SEQUENCE of EncryptedData 1750 DerOutputStream encrData = new DerOutputStream(); 1751 DerOutputStream encrDataContent = new DerOutputStream(); 1752 encrData.putInteger(0); 1753 encrData.write(encrContentInfo); 1754 encrDataContent.write(DerValue.tag_Sequence, encrData); 1755 return encrDataContent.toByteArray(); 1756 } 1757 1758 /* 1759 * Create SafeContent Data content type. 1760 * Includes encrypted secret key in a SafeBag of type SecretBag. 1761 * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag. 1762 * Each PKCS8ShroudedKeyBag includes pkcs12 attributes 1763 * (see comments in getBagAttributes) 1764 */ 1765 private byte[] createSafeContent() 1766 throws CertificateException, IOException { 1767 1768 DerOutputStream out = new DerOutputStream(); 1769 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1770 1771 String alias = e.nextElement(); 1772 Entry entry = entries.get(alias); 1773 if (entry == null || (!(entry instanceof KeyEntry))) { 1774 continue; 1775 } 1776 DerOutputStream safeBag = new DerOutputStream(); 1777 KeyEntry keyEntry = (KeyEntry) entry; 1778 1779 // DER encode the private key 1780 if (keyEntry instanceof PrivateKeyEntry) { 1781 // Create SafeBag of type pkcs8ShroudedKeyBag 1782 safeBag.putOID(PKCS8ShroudedKeyBag_OID); 1783 1784 // get the encrypted private key 1785 byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey; 1786 EncryptedPrivateKeyInfo encrInfo = null; 1787 try { 1788 encrInfo = new EncryptedPrivateKeyInfo(encrBytes); 1789 1790 } catch (IOException ioe) { 1791 throw new IOException("Private key not stored as " 1792 + "PKCS#8 EncryptedPrivateKeyInfo" 1793 + ioe.getMessage()); 1794 } 1795 1796 // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. 1797 DerOutputStream bagValue = new DerOutputStream(); 1798 bagValue.write(encrInfo.getEncoded()); 1799 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1800 true, (byte) 0), bagValue); 1801 1802 // DER encode the secret key 1803 } else if (keyEntry instanceof SecretKeyEntry) { 1804 // Create SafeBag of type SecretBag 1805 safeBag.putOID(SecretBag_OID); 1806 1807 // Create a SecretBag 1808 DerOutputStream secretBag = new DerOutputStream(); 1809 secretBag.putOID(PKCS8ShroudedKeyBag_OID); 1810 1811 // Write secret key in a context-specific tag 1812 DerOutputStream secretKeyValue = new DerOutputStream(); 1813 secretKeyValue.putOctetString( 1814 ((SecretKeyEntry) keyEntry).protectedSecretKey); 1815 secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1816 true, (byte) 0), secretKeyValue); 1817 1818 // Wrap SecretBag in a Sequence 1819 DerOutputStream secretBagSeq = new DerOutputStream(); 1820 secretBagSeq.write(DerValue.tag_Sequence, secretBag); 1821 byte[] secretBagValue = secretBagSeq.toByteArray(); 1822 1823 // Wrap the secret bag in a context-specific tag. 1824 DerOutputStream bagValue = new DerOutputStream(); 1825 bagValue.write(secretBagValue); 1826 1827 // Write SafeBag value 1828 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1829 true, (byte) 0), bagValue); 1830 } else { 1831 continue; // skip this entry 1832 } 1833 1834 // write SafeBag Attributes 1835 byte[] bagAttrs = 1836 getBagAttributes(alias, entry.keyId, entry.attributes); 1837 safeBag.write(bagAttrs); 1838 1839 // wrap as Sequence 1840 out.write(DerValue.tag_Sequence, safeBag); 1841 } 1842 1843 // wrap as Sequence 1844 DerOutputStream safeBagValue = new DerOutputStream(); 1845 safeBagValue.write(DerValue.tag_Sequence, out); 1846 return safeBagValue.toByteArray(); 1847 } 1848 1849 1850 /* 1851 * Encrypt the contents using Password-based (PBE) encryption 1852 * as defined in PKCS #5. 1853 * 1854 * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used 1855 * to derive the key and IV. 1856 * 1857 * @return encrypted contents encoded as EncryptedContentInfo 1858 */ 1859 private byte[] encryptContent(byte[] data, char[] password) 1860 throws IOException { 1861 1862 byte[] encryptedData = null; 1863 1864 // create AlgorithmParameters 1865 AlgorithmParameters algParams = 1866 getPBEAlgorithmParameters("PBEWithSHA1AndRC2_40"); 1867 DerOutputStream bytes = new DerOutputStream(); 1868 AlgorithmId algId = 1869 new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams); 1870 algId.encode(bytes); 1871 byte[] encodedAlgId = bytes.toByteArray(); 1872 1873 try { 1874 // Use JCE 1875 SecretKey skey = getPBEKey(password); 1876 Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40"); 1877 cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); 1878 encryptedData = cipher.doFinal(data); 1879 1880 if (debug != null) { 1881 debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + 1882 ")"); 1883 } 1884 1885 } catch (Exception e) { 1886 throw new IOException("Failed to encrypt" + 1887 " safe contents entry: " + e, e); 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 } 1906 1907 /** 1908 * Loads the keystore from the given input stream. 1909 * 1910 * <p>If a password is given, it is used to check the integrity of the 1911 * keystore data. Otherwise, the integrity of the keystore is not checked. 1912 * 1913 * @param stream the input stream from which the keystore is loaded 1914 * @param password the (optional) password used to check the integrity of 1915 * the keystore. 1916 * 1917 * @exception IOException if there is an I/O or format problem with the 1918 * keystore data 1919 * @exception NoSuchAlgorithmException if the algorithm used to check 1920 * the integrity of the keystore cannot be found 1921 * @exception CertificateException if any of the certificates in the 1922 * keystore could not be loaded 1923 */ 1924 public synchronized void engineLoad(InputStream stream, char[] password) 1925 throws IOException, NoSuchAlgorithmException, CertificateException 1926 { 1927 DataInputStream dis; 1928 CertificateFactory cf = null; 1929 ByteArrayInputStream bais = null; 1930 byte[] encoded = null; 1931 1932 if (stream == null) 1933 return; 1934 1935 // reset the counter 1936 counter = 0; 1937 1938 DerValue val = new DerValue(stream); 1939 DerInputStream s = val.toDerInputStream(); 1940 int version = s.getInteger(); 1941 1942 if (version != VERSION_3) { 1943 throw new IOException("PKCS12 keystore not in version 3 format"); 1944 } 1945 1946 entries.clear(); 1947 1948 /* 1949 * Read the authSafe. 1950 */ 1951 byte[] authSafeData; 1952 ContentInfo authSafe = new ContentInfo(s); 1953 ObjectIdentifier contentType = authSafe.getContentType(); 1954 1955 if (contentType.equals((Object)ContentInfo.DATA_OID)) { 1956 authSafeData = authSafe.getData(); 1957 } else /* signed data */ { 1958 throw new IOException("public key protected PKCS12 not supported"); 1959 } 1960 1961 DerInputStream as = new DerInputStream(authSafeData); 1962 DerValue[] safeContentsArray = as.getSequence(2); 1963 int count = safeContentsArray.length; 1964 1965 // reset the counters at the start 1966 privateKeyCount = 0; 1967 secretKeyCount = 0; 1968 certificateCount = 0; 1969 1970 /* 1971 * Spin over the ContentInfos. 1972 */ 1973 for (int i = 0; i < count; i++) { 1974 byte[] safeContentsData; 1975 ContentInfo safeContents; 1976 DerInputStream sci; 1977 byte[] eAlgId = null; 1978 1979 sci = new DerInputStream(safeContentsArray[i].toByteArray()); 1980 safeContents = new ContentInfo(sci); 1981 contentType = safeContents.getContentType(); 1982 safeContentsData = null; 1983 if (contentType.equals((Object)ContentInfo.DATA_OID)) { 1984 1985 if (debug != null) { 1986 debug.println("Loading PKCS#7 data"); 1987 } 1988 1989 safeContentsData = safeContents.getData(); 1990 } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) { 1991 if (password == null) { 1992 1993 if (debug != null) { 1994 debug.println("Warning: skipping PKCS#7 encryptedData" + 1995 " - no password was supplied"); 1996 } 1997 continue; 1998 } 1999 2000 DerInputStream edi = 2001 safeContents.getContent().toDerInputStream(); 2002 int edVersion = edi.getInteger(); 2003 DerValue[] seq = edi.getSequence(2); 2004 ObjectIdentifier edContentType = seq[0].getOID(); 2005 eAlgId = seq[1].toByteArray(); 2006 if (!seq[2].isContextSpecific((byte)0)) { 2007 throw new IOException("encrypted content not present!"); 2008 } 2009 byte newTag = DerValue.tag_OctetString; 2010 if (seq[2].isConstructed()) 2011 newTag |= 0x20; 2012 seq[2].resetTag(newTag); 2013 safeContentsData = seq[2].getOctetString(); 2014 2015 // parse Algorithm parameters 2016 DerInputStream in = seq[1].toDerInputStream(); 2017 ObjectIdentifier algOid = in.getOID(); 2018 AlgorithmParameters algParams = parseAlgParameters(algOid, in); 2019 2020 PBEParameterSpec pbeSpec; 2021 int ic = 0; 2022 2023 if (algParams != null) { 2024 try { 2025 pbeSpec = 2026 algParams.getParameterSpec(PBEParameterSpec.class); 2027 } catch (InvalidParameterSpecException ipse) { 2028 throw new IOException( 2029 "Invalid PBE algorithm parameters"); 2030 } 2031 ic = pbeSpec.getIterationCount(); 2032 2033 if (ic > MAX_ITERATION_COUNT) { 2034 throw new IOException("PBE iteration count too large"); 2035 } 2036 } 2037 2038 if (debug != null) { 2039 debug.println("Loading PKCS#7 encryptedData " + 2040 "(" + new AlgorithmId(algOid).getName() + 2041 " iterations: " + ic + ")"); 2042 } 2043 2044 while (true) { 2045 try { 2046 // Use JCE 2047 SecretKey skey = getPBEKey(password); 2048 Cipher cipher = Cipher.getInstance(algOid.toString()); 2049 cipher.init(Cipher.DECRYPT_MODE, skey, algParams); 2050 safeContentsData = cipher.doFinal(safeContentsData); 2051 break; 2052 } catch (Exception e) { 2053 if (password.length == 0) { 2054 // Retry using an empty password 2055 // without a NULL terminator. 2056 password = new char[1]; 2057 continue; 2058 } 2059 throw new IOException("keystore password was incorrect", 2060 new UnrecoverableKeyException( 2061 "failed to decrypt safe contents entry: " + e)); 2062 } 2063 } 2064 } else { 2065 throw new IOException("public key protected PKCS12" + 2066 " not supported"); 2067 } 2068 DerInputStream sc = new DerInputStream(safeContentsData); 2069 loadSafeContents(sc, password); 2070 } 2071 2072 // The MacData is optional. 2073 if (password != null && s.available() > 0) { 2074 MacData macData = new MacData(s); 2075 int ic = macData.getIterations(); 2076 2077 try { 2078 if (ic > MAX_ITERATION_COUNT) { 2079 throw new InvalidAlgorithmParameterException( 2080 "MAC iteration count too large: " + ic); 2081 } 2082 2083 String algName = 2084 macData.getDigestAlgName().toUpperCase(Locale.ENGLISH); 2085 2086 // Change SHA-1 to SHA1 2087 algName = algName.replace("-", ""); 2088 2089 // generate MAC (MAC key is created within JCE) 2090 Mac m = Mac.getInstance("HmacPBE" + algName); 2091 PBEParameterSpec params = 2092 new PBEParameterSpec(macData.getSalt(), ic); 2093 SecretKey key = getPBEKey(password); 2094 m.init(key, params); 2095 m.update(authSafeData); 2096 byte[] macResult = m.doFinal(); 2097 2098 if (debug != null) { 2099 debug.println("Checking keystore integrity " + 2100 "(" + m.getAlgorithm() + " iterations: " + ic + ")"); 2101 } 2102 2103 if (!MessageDigest.isEqual(macData.getDigest(), macResult)) { 2104 throw new UnrecoverableKeyException("Failed PKCS12" + 2105 " integrity checking"); 2106 } 2107 } catch (Exception e) { 2108 throw new IOException("Integrity check failed: " + e, e); 2109 } 2110 } 2111 2112 /* 2113 * Match up private keys with certificate chains. 2114 */ 2115 PrivateKeyEntry[] list = 2116 keyList.toArray(new PrivateKeyEntry[keyList.size()]); 2117 for (int m = 0; m < list.length; m++) { 2118 PrivateKeyEntry entry = list[m]; 2119 if (entry.keyId != null) { 2120 ArrayList<X509Certificate> chain = 2121 new ArrayList<X509Certificate>(); 2122 X509Certificate cert = findMatchedCertificate(entry); 2123 2124 mainloop: 2125 while (cert != null) { 2126 // Check for loops in the certificate chain 2127 if (!chain.isEmpty()) { 2128 for (X509Certificate chainCert : chain) { 2129 if (cert.equals(chainCert)) { 2130 if (debug != null) { 2131 debug.println("Loop detected in " + 2132 "certificate chain. Skip adding " + 2133 "repeated cert to chain. Subject: " + 2134 cert.getSubjectX500Principal() 2135 .toString()); 2136 } 2137 break mainloop; 2138 } 2139 } 2140 } 2141 chain.add(cert); 2142 X500Principal issuerDN = cert.getIssuerX500Principal(); 2143 if (issuerDN.equals(cert.getSubjectX500Principal())) { 2144 break; 2145 } 2146 cert = certsMap.get(issuerDN); 2147 } 2148 /* Update existing KeyEntry in entries table */ 2149 if (chain.size() > 0) 2150 entry.chain = chain.toArray(new Certificate[chain.size()]); 2151 } 2152 } 2153 2154 if (debug != null) { 2155 if (privateKeyCount > 0) { 2156 debug.println("Loaded " + privateKeyCount + 2157 " protected private key(s)"); 2158 } 2159 if (secretKeyCount > 0) { 2160 debug.println("Loaded " + secretKeyCount + 2161 " protected secret key(s)"); 2162 } 2163 if (certificateCount > 0) { 2164 debug.println("Loaded " + certificateCount + 2165 " certificate(s)"); 2166 } 2167 } 2168 2169 certEntries.clear(); 2170 certsMap.clear(); 2171 keyList.clear(); 2172 } 2173 2174 /** 2175 * Locates a matched CertEntry from certEntries, and returns its cert. 2176 * @param entry the KeyEntry to match 2177 * @return a certificate, null if not found 2178 */ 2179 private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) { 2180 CertEntry keyIdMatch = null; 2181 CertEntry aliasMatch = null; 2182 for (CertEntry ce: certEntries) { 2183 if (Arrays.equals(entry.keyId, ce.keyId)) { 2184 keyIdMatch = ce; 2185 if (entry.alias.equalsIgnoreCase(ce.alias)) { 2186 // Full match! 2187 return ce.cert; 2188 } 2189 } else if (entry.alias.equalsIgnoreCase(ce.alias)) { 2190 aliasMatch = ce; 2191 } 2192 } 2193 // keyId match first, for compatibility 2194 if (keyIdMatch != null) return keyIdMatch.cert; 2195 else if (aliasMatch != null) return aliasMatch.cert; 2196 else return null; 2197 } 2198 2199 private void loadSafeContents(DerInputStream stream, char[] password) 2200 throws IOException, NoSuchAlgorithmException, CertificateException 2201 { 2202 DerValue[] safeBags = stream.getSequence(2); 2203 int count = safeBags.length; 2204 2205 /* 2206 * Spin over the SafeBags. 2207 */ 2208 for (int i = 0; i < count; i++) { 2209 ObjectIdentifier bagId; 2210 DerInputStream sbi; 2211 DerValue bagValue; 2212 Object bagItem = null; 2213 2214 sbi = safeBags[i].toDerInputStream(); 2215 bagId = sbi.getOID(); 2216 bagValue = sbi.getDerValue(); 2217 if (!bagValue.isContextSpecific((byte)0)) { 2218 throw new IOException("unsupported PKCS12 bag value type " 2219 + bagValue.tag); 2220 } 2221 bagValue = bagValue.data.getDerValue(); 2222 if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) { 2223 PrivateKeyEntry kEntry = new PrivateKeyEntry(); 2224 kEntry.protectedPrivKey = bagValue.toByteArray(); 2225 bagItem = kEntry; 2226 privateKeyCount++; 2227 } else if (bagId.equals((Object)CertBag_OID)) { 2228 DerInputStream cs = new DerInputStream(bagValue.toByteArray()); 2229 DerValue[] certValues = cs.getSequence(2); 2230 ObjectIdentifier certId = certValues[0].getOID(); 2231 if (!certValues[1].isContextSpecific((byte)0)) { 2232 throw new IOException("unsupported PKCS12 cert value type " 2233 + certValues[1].tag); 2234 } 2235 DerValue certValue = certValues[1].data.getDerValue(); 2236 CertificateFactory cf = CertificateFactory.getInstance("X509"); 2237 X509Certificate cert; 2238 cert = (X509Certificate)cf.generateCertificate 2239 (new ByteArrayInputStream(certValue.getOctetString())); 2240 bagItem = cert; 2241 certificateCount++; 2242 } else if (bagId.equals((Object)SecretBag_OID)) { 2243 DerInputStream ss = new DerInputStream(bagValue.toByteArray()); 2244 DerValue[] secretValues = ss.getSequence(2); 2245 ObjectIdentifier secretId = secretValues[0].getOID(); 2246 if (!secretValues[1].isContextSpecific((byte)0)) { 2247 throw new IOException( 2248 "unsupported PKCS12 secret value type " 2249 + secretValues[1].tag); 2250 } 2251 DerValue secretValue = secretValues[1].data.getDerValue(); 2252 SecretKeyEntry kEntry = new SecretKeyEntry(); 2253 kEntry.protectedSecretKey = secretValue.getOctetString(); 2254 bagItem = kEntry; 2255 secretKeyCount++; 2256 } else { 2257 2258 if (debug != null) { 2259 debug.println("Unsupported PKCS12 bag type: " + bagId); 2260 } 2261 } 2262 2263 DerValue[] attrSet; 2264 try { 2265 attrSet = sbi.getSet(3); 2266 } catch (IOException e) { 2267 // entry does not have attributes 2268 // Note: CA certs can have no attributes 2269 // OpenSSL generates pkcs12 with no attr for CA certs. 2270 attrSet = null; 2271 } 2272 2273 String alias = null; 2274 byte[] keyId = null; 2275 ObjectIdentifier[] trustedKeyUsage = null; 2276 Set<PKCS12Attribute> attributes = new HashSet<>(); 2277 2278 if (attrSet != null) { 2279 for (int j = 0; j < attrSet.length; j++) { 2280 byte[] encoded = attrSet[j].toByteArray(); 2281 DerInputStream as = new DerInputStream(encoded); 2282 DerValue[] attrSeq = as.getSequence(2); 2283 ObjectIdentifier attrId = attrSeq[0].getOID(); 2284 DerInputStream vs = 2285 new DerInputStream(attrSeq[1].toByteArray()); 2286 DerValue[] valSet; 2287 try { 2288 valSet = vs.getSet(1); 2289 } catch (IOException e) { 2290 throw new IOException("Attribute " + attrId + 2291 " should have a value " + e.getMessage()); 2292 } 2293 if (attrId.equals((Object)PKCS9FriendlyName_OID)) { 2294 alias = valSet[0].getBMPString(); 2295 } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) { 2296 keyId = valSet[0].getOctetString(); 2297 } else if 2298 (attrId.equals((Object)TrustedKeyUsage_OID)) { 2299 trustedKeyUsage = new ObjectIdentifier[valSet.length]; 2300 for (int k = 0; k < valSet.length; k++) { 2301 trustedKeyUsage[k] = valSet[k].getOID(); 2302 } 2303 } else { 2304 attributes.add(new PKCS12Attribute(encoded)); 2305 } 2306 } 2307 } 2308 2309 /* 2310 * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId) 2311 * are optional PKCS12 bagAttributes. But entries in the keyStore 2312 * are identified by their alias. Hence we need to have an 2313 * Unfriendlyname in the alias, if alias is null. The keyId 2314 * attribute is required to match the private key with the 2315 * certificate. If we get a bagItem of type KeyEntry with a 2316 * null keyId, we should skip it entirely. 2317 */ 2318 if (bagItem instanceof KeyEntry) { 2319 KeyEntry entry = (KeyEntry)bagItem; 2320 2321 if (bagItem instanceof PrivateKeyEntry) { 2322 if (keyId == null) { 2323 // Insert a localKeyID for the privateKey 2324 // Note: This is a workaround to allow null localKeyID 2325 // attribute in pkcs12 with one private key entry and 2326 // associated cert-chain 2327 if (privateKeyCount == 1) { 2328 keyId = "01".getBytes("UTF8"); 2329 } else { 2330 continue; 2331 } 2332 } 2333 } 2334 entry.keyId = keyId; 2335 // restore date if it exists 2336 String keyIdStr = new String(keyId, "UTF8"); 2337 Date date = null; 2338 if (keyIdStr.startsWith("Time ")) { 2339 try { 2340 date = new Date( 2341 Long.parseLong(keyIdStr.substring(5))); 2342 } catch (Exception e) { 2343 date = null; 2344 } 2345 } 2346 if (date == null) { 2347 date = new Date(); 2348 } 2349 entry.date = date; 2350 2351 if (bagItem instanceof PrivateKeyEntry) { 2352 keyList.add((PrivateKeyEntry) entry); 2353 } 2354 if (entry.attributes == null) { 2355 entry.attributes = new HashSet<>(); 2356 } 2357 entry.attributes.addAll(attributes); 2358 if (alias == null) { 2359 alias = getUnfriendlyName(); 2360 } 2361 entry.alias = alias; 2362 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 2363 2364 } else if (bagItem instanceof X509Certificate) { 2365 X509Certificate cert = (X509Certificate)bagItem; 2366 // Insert a localKeyID for the corresponding cert 2367 // Note: This is a workaround to allow null localKeyID 2368 // attribute in pkcs12 with one private key entry and 2369 // associated cert-chain 2370 if ((keyId == null) && (privateKeyCount == 1)) { 2371 // insert localKeyID only for EE cert or self-signed cert 2372 if (i == 0) { 2373 keyId = "01".getBytes("UTF8"); 2374 } 2375 } 2376 // Trusted certificate 2377 if (trustedKeyUsage != null) { 2378 if (alias == null) { 2379 alias = getUnfriendlyName(); 2380 } 2381 CertEntry certEntry = 2382 new CertEntry(cert, keyId, alias, trustedKeyUsage, 2383 attributes); 2384 entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry); 2385 } else { 2386 certEntries.add(new CertEntry(cert, keyId, alias)); 2387 } 2388 X500Principal subjectDN = cert.getSubjectX500Principal(); 2389 if (subjectDN != null) { 2390 if (!certsMap.containsKey(subjectDN)) { 2391 certsMap.put(subjectDN, cert); 2392 } 2393 } 2394 } 2395 } 2396 } 2397 2398 private String getUnfriendlyName() { 2399 counter++; 2400 return (String.valueOf(counter)); 2401 } 2402 }