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