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 * Determines if the keystore {@code Entry} for the specified 1062 * {@code alias} is an instance or subclass of the specified 1063 * {@code entryClass}. 1064 * 1065 * @param alias the alias name 1066 * @param entryClass the entry class 1067 * 1068 * @return true if the keystore {@code Entry} for the specified 1069 * {@code alias} is an instance or subclass of the 1070 * specified {@code entryClass}, false otherwise 1071 * 1072 * @since 1.5 1073 */ 1074 @Override 1075 public boolean 1076 engineEntryInstanceOf(String alias, 1077 Class<? extends KeyStore.Entry> entryClass) 1078 { 1079 if (entryClass == KeyStore.TrustedCertificateEntry.class) { 1080 return engineIsCertificateEntry(alias); 1081 } 1082 1083 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1084 if (entryClass == KeyStore.PrivateKeyEntry.class) { 1085 return (entry != null && entry instanceof PrivateKeyEntry); 1086 } 1087 if (entryClass == KeyStore.SecretKeyEntry.class) { 1088 return (entry != null && entry instanceof SecretKeyEntry); 1089 } 1090 return false; 1091 } 1092 1093 /** 1094 * Returns the (alias) name of the first keystore entry whose certificate 1095 * matches the given certificate. 1096 * 1097 * <p>This method attempts to match the given certificate with each 1098 * keystore entry. If the entry being considered 1099 * is a <i>trusted certificate entry</i>, the given certificate is 1100 * compared to that entry's certificate. If the entry being considered is 1101 * a <i>key entry</i>, the given certificate is compared to the first 1102 * element of that entry's certificate chain (if a chain exists). 1103 * 1104 * @param cert the certificate to match with. 1105 * 1106 * @return the (alias) name of the first entry with matching certificate, 1107 * or null if no such entry exists in this keystore. 1108 */ 1109 public String engineGetCertificateAlias(Certificate cert) { 1110 Certificate certElem = null; 1111 1112 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1113 String alias = e.nextElement(); 1114 Entry entry = entries.get(alias); 1115 if (entry instanceof PrivateKeyEntry) { 1116 if (((PrivateKeyEntry) entry).chain != null) { 1117 certElem = ((PrivateKeyEntry) entry).chain[0]; 1118 } 1119 } else if (entry instanceof CertEntry && 1120 ((CertEntry) entry).trustedKeyUsage != null) { 1121 certElem = ((CertEntry) entry).cert; 1122 } else { 1123 continue; 1124 } 1125 if (certElem != null && certElem.equals(cert)) { 1126 return alias; 1127 } 1128 } 1129 return null; 1130 } 1131 1132 /** 1133 * Stores this keystore to the given output stream, and protects its 1134 * integrity with the given password. 1135 * 1136 * @param stream the output stream to which this keystore is written. 1137 * @param password the password to generate the keystore integrity check 1138 * 1139 * @exception IOException if there was an I/O problem with data 1140 * @exception NoSuchAlgorithmException if the appropriate data integrity 1141 * algorithm could not be found 1142 * @exception CertificateException if any of the certificates included in 1143 * the keystore data could not be stored 1144 */ 1145 public synchronized void engineStore(OutputStream stream, char[] password) 1146 throws IOException, NoSuchAlgorithmException, CertificateException 1147 { 1148 // password is mandatory when storing 1149 if (password == null) { 1150 throw new IllegalArgumentException("password can't be null"); 1151 } 1152 1153 // -- Create PFX 1154 DerOutputStream pfx = new DerOutputStream(); 1155 1156 // PFX version (always write the latest version) 1157 DerOutputStream version = new DerOutputStream(); 1158 version.putInteger(VERSION_3); 1159 byte[] pfxVersion = version.toByteArray(); 1160 pfx.write(pfxVersion); 1161 1162 // -- Create AuthSafe 1163 DerOutputStream authSafe = new DerOutputStream(); 1164 1165 // -- Create ContentInfos 1166 DerOutputStream authSafeContentInfo = new DerOutputStream(); 1167 1168 // -- create safeContent Data ContentInfo 1169 if (privateKeyCount > 0 || secretKeyCount > 0) { 1170 1171 if (debug != null) { 1172 debug.println("Storing " + (privateKeyCount + secretKeyCount) + 1173 " protected key(s) in a PKCS#7 data content-type"); 1174 } 1175 1176 byte[] safeContentData = createSafeContent(); 1177 ContentInfo dataContentInfo = new ContentInfo(safeContentData); 1178 dataContentInfo.encode(authSafeContentInfo); 1179 } 1180 1181 // -- create EncryptedContentInfo 1182 if (certificateCount > 0) { 1183 1184 if (debug != null) { 1185 debug.println("Storing " + certificateCount + 1186 " certificate(s) in a PKCS#7 encryptedData content-type"); 1187 } 1188 1189 byte[] encrData = createEncryptedData(password); 1190 ContentInfo encrContentInfo = 1191 new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, 1192 new DerValue(encrData)); 1193 encrContentInfo.encode(authSafeContentInfo); 1194 } 1195 1196 // wrap as SequenceOf ContentInfos 1197 DerOutputStream cInfo = new DerOutputStream(); 1198 cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo); 1199 byte[] authenticatedSafe = cInfo.toByteArray(); 1200 1201 // Create Encapsulated ContentInfo 1202 ContentInfo contentInfo = new ContentInfo(authenticatedSafe); 1203 contentInfo.encode(authSafe); 1204 byte[] authSafeData = authSafe.toByteArray(); 1205 pfx.write(authSafeData); 1206 1207 // -- MAC 1208 byte[] macData = calculateMac(password, authenticatedSafe); 1209 pfx.write(macData); 1210 1211 // write PFX to output stream 1212 DerOutputStream pfxout = new DerOutputStream(); 1213 pfxout.write(DerValue.tag_Sequence, pfx); 1214 byte[] pfxData = pfxout.toByteArray(); 1215 stream.write(pfxData); 1216 stream.flush(); 1217 } 1218 1219 /** 1220 * Gets a <code>KeyStore.Entry</code> for the specified alias 1221 * with the specified protection parameter. 1222 * 1223 * @param alias get the <code>KeyStore.Entry</code> for this alias 1224 * @param protParam the <code>ProtectionParameter</code> 1225 * used to protect the <code>Entry</code>, 1226 * which may be <code>null</code> 1227 * 1228 * @return the <code>KeyStore.Entry</code> for the specified alias, 1229 * or <code>null</code> if there is no such entry 1230 * 1231 * @exception KeyStoreException if the operation failed 1232 * @exception NoSuchAlgorithmException if the algorithm for recovering the 1233 * entry cannot be found 1234 * @exception UnrecoverableEntryException if the specified 1235 * <code>protParam</code> were insufficient or invalid 1236 * @exception UnrecoverableKeyException if the entry is a 1237 * <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code> 1238 * and the specified <code>protParam</code> does not contain 1239 * the information needed to recover the key (e.g. wrong password) 1240 * 1241 * @since 1.5 1242 */ 1243 @Override 1244 public KeyStore.Entry engineGetEntry(String alias, 1245 KeyStore.ProtectionParameter protParam) 1246 throws KeyStoreException, NoSuchAlgorithmException, 1247 UnrecoverableEntryException { 1248 1249 if (!engineContainsAlias(alias)) { 1250 return null; 1251 } 1252 1253 Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); 1254 if (protParam == null) { 1255 if (engineIsCertificateEntry(alias)) { 1256 if (entry instanceof CertEntry && 1257 ((CertEntry) entry).trustedKeyUsage != null) { 1258 1259 if (debug != null) { 1260 debug.println("Retrieved a trusted certificate at " + 1261 "alias '" + alias + "'"); 1262 } 1263 1264 return new KeyStore.TrustedCertificateEntry( 1265 ((CertEntry)entry).cert, getAttributes(entry)); 1266 } 1267 } else { 1268 throw new UnrecoverableKeyException 1269 ("requested entry requires a password"); 1270 } 1271 } 1272 1273 if (protParam instanceof KeyStore.PasswordProtection) { 1274 if (engineIsCertificateEntry(alias)) { 1275 throw new UnsupportedOperationException 1276 ("trusted certificate entries are not password-protected"); 1277 } else if (engineIsKeyEntry(alias)) { 1278 KeyStore.PasswordProtection pp = 1279 (KeyStore.PasswordProtection)protParam; 1280 char[] password = pp.getPassword(); 1281 1282 Key key = engineGetKey(alias, password); 1283 if (key instanceof PrivateKey) { 1284 Certificate[] chain = engineGetCertificateChain(alias); 1285 1286 return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain, 1287 getAttributes(entry)); 1288 1289 } else if (key instanceof SecretKey) { 1290 1291 return new KeyStore.SecretKeyEntry((SecretKey)key, 1292 getAttributes(entry)); 1293 } 1294 } else if (!engineIsKeyEntry(alias)) { 1295 throw new UnsupportedOperationException 1296 ("untrusted certificate entries are not " + 1297 "password-protected"); 1298 } 1299 } 1300 1301 throw new UnsupportedOperationException(); 1302 } 1303 1304 /** 1305 * Saves a <code>KeyStore.Entry</code> under the specified alias. 1306 * The specified protection parameter is used to protect the 1307 * <code>Entry</code>. 1308 * 1309 * <p> If an entry already exists for the specified alias, 1310 * it is overridden. 1311 * 1312 * @param alias save the <code>KeyStore.Entry</code> under this alias 1313 * @param entry the <code>Entry</code> to save 1314 * @param protParam the <code>ProtectionParameter</code> 1315 * used to protect the <code>Entry</code>, 1316 * which may be <code>null</code> 1317 * 1318 * @exception KeyStoreException if this operation fails 1319 * 1320 * @since 1.5 1321 */ 1322 @Override 1323 public synchronized void engineSetEntry(String alias, KeyStore.Entry entry, 1324 KeyStore.ProtectionParameter protParam) throws KeyStoreException { 1325 1326 // get password 1327 if (protParam != null && 1328 !(protParam instanceof KeyStore.PasswordProtection)) { 1329 throw new KeyStoreException("unsupported protection parameter"); 1330 } 1331 KeyStore.PasswordProtection pProtect = null; 1332 if (protParam != null) { 1333 pProtect = (KeyStore.PasswordProtection)protParam; 1334 } 1335 1336 // set entry 1337 if (entry instanceof KeyStore.TrustedCertificateEntry) { 1338 if (protParam != null && pProtect.getPassword() != null) { 1339 // pre-1.5 style setCertificateEntry did not allow password 1340 throw new KeyStoreException 1341 ("trusted certificate entries are not password-protected"); 1342 } else { 1343 KeyStore.TrustedCertificateEntry tce = 1344 (KeyStore.TrustedCertificateEntry)entry; 1345 setCertEntry(alias, tce.getTrustedCertificate(), 1346 tce.getAttributes()); 1347 1348 return; 1349 } 1350 } else if (entry instanceof KeyStore.PrivateKeyEntry) { 1351 if (pProtect == null || pProtect.getPassword() == null) { 1352 // pre-1.5 style setKeyEntry required password 1353 throw new KeyStoreException 1354 ("non-null password required to create PrivateKeyEntry"); 1355 } else { 1356 KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry; 1357 setKeyEntry(alias, pke.getPrivateKey(), pProtect, 1358 pke.getCertificateChain(), pke.getAttributes()); 1359 1360 return; 1361 } 1362 } else if (entry instanceof KeyStore.SecretKeyEntry) { 1363 if (pProtect == null || pProtect.getPassword() == null) { 1364 // pre-1.5 style setKeyEntry required password 1365 throw new KeyStoreException 1366 ("non-null password required to create SecretKeyEntry"); 1367 } else { 1368 KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry; 1369 setKeyEntry(alias, ske.getSecretKey(), pProtect, 1370 (Certificate[])null, ske.getAttributes()); 1371 1372 return; 1373 } 1374 } 1375 1376 throw new KeyStoreException 1377 ("unsupported entry type: " + entry.getClass().getName()); 1378 } 1379 1380 /* 1381 * Assemble the entry attributes 1382 */ 1383 private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) { 1384 1385 if (entry.attributes == null) { 1386 entry.attributes = new HashSet<>(); 1387 } 1388 1389 // friendlyName 1390 entry.attributes.add(new PKCS12Attribute( 1391 PKCS9FriendlyName_OID.toString(), entry.alias)); 1392 1393 // localKeyID 1394 byte[] keyIdValue = entry.keyId; 1395 if (keyIdValue != null) { 1396 entry.attributes.add(new PKCS12Attribute( 1397 PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue))); 1398 } 1399 1400 // trustedKeyUsage 1401 if (entry instanceof CertEntry) { 1402 ObjectIdentifier[] trustedKeyUsageValue = 1403 ((CertEntry) entry).trustedKeyUsage; 1404 if (trustedKeyUsageValue != null) { 1405 if (trustedKeyUsageValue.length == 1) { // omit brackets 1406 entry.attributes.add(new PKCS12Attribute( 1407 TrustedKeyUsage_OID.toString(), 1408 trustedKeyUsageValue[0].toString())); 1409 } else { // multi-valued 1410 entry.attributes.add(new PKCS12Attribute( 1411 TrustedKeyUsage_OID.toString(), 1412 Arrays.toString(trustedKeyUsageValue))); 1413 } 1414 } 1415 } 1416 1417 return entry.attributes; 1418 } 1419 1420 /* 1421 * Generate Hash. 1422 */ 1423 private byte[] generateHash(byte[] data) throws IOException 1424 { 1425 byte[] digest = null; 1426 1427 try { 1428 MessageDigest md = MessageDigest.getInstance("SHA1"); 1429 md.update(data); 1430 digest = md.digest(); 1431 } catch (Exception e) { 1432 throw new IOException("generateHash failed: " + e, e); 1433 } 1434 return digest; 1435 } 1436 1437 1438 /* 1439 * Calculate MAC using HMAC algorithm (required for password integrity) 1440 * 1441 * Hash-based MAC algorithm combines secret key with message digest to 1442 * create a message authentication code (MAC) 1443 */ 1444 private byte[] calculateMac(char[] passwd, byte[] data) 1445 throws IOException 1446 { 1447 byte[] mData = null; 1448 String algName = "SHA1"; 1449 1450 try { 1451 // Generate a random salt. 1452 byte[] salt = getSalt(); 1453 1454 // generate MAC (MAC key is generated within JCE) 1455 Mac m = Mac.getInstance("HmacPBESHA1"); 1456 PBEParameterSpec params = 1457 new PBEParameterSpec(salt, iterationCount); 1458 SecretKey key = getPBEKey(passwd); 1459 m.init(key, params); 1460 m.update(data); 1461 byte[] macResult = m.doFinal(); 1462 1463 // encode as MacData 1464 MacData macData = new MacData(algName, macResult, salt, 1465 iterationCount); 1466 DerOutputStream bytes = new DerOutputStream(); 1467 bytes.write(macData.getEncoded()); 1468 mData = bytes.toByteArray(); 1469 } catch (Exception e) { 1470 throw new IOException("calculateMac failed: " + e, e); 1471 } 1472 return mData; 1473 } 1474 1475 1476 /* 1477 * Validate Certificate Chain 1478 */ 1479 private boolean validateChain(Certificate[] certChain) 1480 { 1481 for (int i = 0; i < certChain.length-1; i++) { 1482 X500Principal issuerDN = 1483 ((X509Certificate)certChain[i]).getIssuerX500Principal(); 1484 X500Principal subjectDN = 1485 ((X509Certificate)certChain[i+1]).getSubjectX500Principal(); 1486 if (!(issuerDN.equals(subjectDN))) 1487 return false; 1488 } 1489 1490 // Check for loops in the chain. If there are repeated certs, 1491 // the Set of certs in the chain will contain fewer certs than 1492 // the chain 1493 Set<Certificate> set = new HashSet<>(Arrays.asList(certChain)); 1494 return set.size() == certChain.length; 1495 } 1496 1497 1498 /* 1499 * Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage. 1500 * 1501 * Although attributes are optional, they could be required. 1502 * For e.g. localKeyId attribute is required to match the 1503 * private key with the associated end-entity certificate. 1504 * The trustedKeyUsage attribute is used to denote a trusted certificate. 1505 * 1506 * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName. 1507 * CertBags may or may not include attributes depending on the type 1508 * of Certificate. In end-entity certificates, localKeyID should be 1509 * unique, and the corresponding private key should have the same 1510 * localKeyID. For trusted CA certs in the cert-chain, localKeyID 1511 * attribute is not required, hence most vendors don't include it. 1512 * NSS/Netscape require it to be unique or null, where as IE/OpenSSL 1513 * ignore it. 1514 * 1515 * Here is a list of pkcs12 attribute values in CertBags. 1516 * 1517 * PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE 1518 * -------------------------------------------------------------- 1519 * LocalKeyId 1520 * (In EE cert only, 1521 * NULL in CA certs) true true true true 1522 * 1523 * friendlyName unique same/ same/ unique 1524 * unique unique/ 1525 * null 1526 * trustedKeyUsage - - - true 1527 * 1528 * Note: OpenSSL adds friendlyName for end-entity cert only, and 1529 * removes the localKeyID and friendlyName for CA certs. 1530 * If the CertBag did not have a friendlyName, most vendors will 1531 * add it, and assign it to the DN of the cert. 1532 */ 1533 private byte[] getBagAttributes(String alias, byte[] keyId, 1534 Set<KeyStore.Entry.Attribute> attributes) throws IOException { 1535 return getBagAttributes(alias, keyId, null, attributes); 1536 } 1537 1538 private byte[] getBagAttributes(String alias, byte[] keyId, 1539 ObjectIdentifier[] trustedUsage, 1540 Set<KeyStore.Entry.Attribute> attributes) throws IOException { 1541 1542 byte[] localKeyID = null; 1543 byte[] friendlyName = null; 1544 byte[] trustedKeyUsage = null; 1545 1546 // return null if all three attributes are null 1547 if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) { 1548 return null; 1549 } 1550 1551 // SafeBag Attributes 1552 DerOutputStream bagAttrs = new DerOutputStream(); 1553 1554 // Encode the friendlyname oid. 1555 if (alias != null) { 1556 DerOutputStream bagAttr1 = new DerOutputStream(); 1557 bagAttr1.putOID(PKCS9FriendlyName_OID); 1558 DerOutputStream bagAttrContent1 = new DerOutputStream(); 1559 DerOutputStream bagAttrValue1 = new DerOutputStream(); 1560 bagAttrContent1.putBMPString(alias); 1561 bagAttr1.write(DerValue.tag_Set, bagAttrContent1); 1562 bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1); 1563 friendlyName = bagAttrValue1.toByteArray(); 1564 } 1565 1566 // Encode the localkeyId oid. 1567 if (keyId != null) { 1568 DerOutputStream bagAttr2 = new DerOutputStream(); 1569 bagAttr2.putOID(PKCS9LocalKeyId_OID); 1570 DerOutputStream bagAttrContent2 = new DerOutputStream(); 1571 DerOutputStream bagAttrValue2 = new DerOutputStream(); 1572 bagAttrContent2.putOctetString(keyId); 1573 bagAttr2.write(DerValue.tag_Set, bagAttrContent2); 1574 bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2); 1575 localKeyID = bagAttrValue2.toByteArray(); 1576 } 1577 1578 // Encode the trustedKeyUsage oid. 1579 if (trustedUsage != null) { 1580 DerOutputStream bagAttr3 = new DerOutputStream(); 1581 bagAttr3.putOID(TrustedKeyUsage_OID); 1582 DerOutputStream bagAttrContent3 = new DerOutputStream(); 1583 DerOutputStream bagAttrValue3 = new DerOutputStream(); 1584 for (ObjectIdentifier usage : trustedUsage) { 1585 bagAttrContent3.putOID(usage); 1586 } 1587 bagAttr3.write(DerValue.tag_Set, bagAttrContent3); 1588 bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3); 1589 trustedKeyUsage = bagAttrValue3.toByteArray(); 1590 } 1591 1592 DerOutputStream attrs = new DerOutputStream(); 1593 if (friendlyName != null) { 1594 attrs.write(friendlyName); 1595 } 1596 if (localKeyID != null) { 1597 attrs.write(localKeyID); 1598 } 1599 if (trustedKeyUsage != null) { 1600 attrs.write(trustedKeyUsage); 1601 } 1602 1603 if (attributes != null) { 1604 for (KeyStore.Entry.Attribute attribute : attributes) { 1605 String attributeName = attribute.getName(); 1606 // skip friendlyName, localKeyId and trustedKeyUsage 1607 if (CORE_ATTRIBUTES[0].equals(attributeName) || 1608 CORE_ATTRIBUTES[1].equals(attributeName) || 1609 CORE_ATTRIBUTES[2].equals(attributeName)) { 1610 continue; 1611 } 1612 attrs.write(((PKCS12Attribute) attribute).getEncoded()); 1613 } 1614 } 1615 1616 bagAttrs.write(DerValue.tag_Set, attrs); 1617 return bagAttrs.toByteArray(); 1618 } 1619 1620 /* 1621 * Create EncryptedData content type, that contains EncryptedContentInfo. 1622 * Includes certificates in individual SafeBags of type CertBag. 1623 * Each CertBag may include pkcs12 attributes 1624 * (see comments in getBagAttributes) 1625 */ 1626 private byte[] createEncryptedData(char[] password) 1627 throws CertificateException, IOException 1628 { 1629 DerOutputStream out = new DerOutputStream(); 1630 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1631 1632 String alias = e.nextElement(); 1633 Entry entry = entries.get(alias); 1634 1635 // certificate chain 1636 Certificate[] certs; 1637 1638 if (entry instanceof PrivateKeyEntry) { 1639 PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; 1640 if (keyEntry.chain != null) { 1641 certs = keyEntry.chain; 1642 } else { 1643 certs = new Certificate[0]; 1644 } 1645 } else if (entry instanceof CertEntry) { 1646 certs = new Certificate[]{((CertEntry) entry).cert}; 1647 } else { 1648 certs = new Certificate[0]; 1649 } 1650 1651 for (int i = 0; i < certs.length; i++) { 1652 // create SafeBag of Type CertBag 1653 DerOutputStream safeBag = new DerOutputStream(); 1654 safeBag.putOID(CertBag_OID); 1655 1656 // create a CertBag 1657 DerOutputStream certBag = new DerOutputStream(); 1658 certBag.putOID(PKCS9CertType_OID); 1659 1660 // write encoded certs in a context-specific tag 1661 DerOutputStream certValue = new DerOutputStream(); 1662 X509Certificate cert = (X509Certificate) certs[i]; 1663 certValue.putOctetString(cert.getEncoded()); 1664 certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1665 true, (byte) 0), certValue); 1666 1667 // wrap CertBag in a Sequence 1668 DerOutputStream certout = new DerOutputStream(); 1669 certout.write(DerValue.tag_Sequence, certBag); 1670 byte[] certBagValue = certout.toByteArray(); 1671 1672 // Wrap the CertBag encoding in a context-specific tag. 1673 DerOutputStream bagValue = new DerOutputStream(); 1674 bagValue.write(certBagValue); 1675 // write SafeBag Value 1676 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1677 true, (byte) 0), bagValue); 1678 1679 // write SafeBag Attributes 1680 // All Certs should have a unique friendlyName. 1681 // This change is made to meet NSS requirements. 1682 byte[] bagAttrs = null; 1683 if (i == 0) { 1684 // Only End-Entity Cert should have a localKeyId. 1685 if (entry instanceof KeyEntry) { 1686 KeyEntry keyEntry = (KeyEntry) entry; 1687 bagAttrs = 1688 getBagAttributes(keyEntry.alias, keyEntry.keyId, 1689 keyEntry.attributes); 1690 } else { 1691 CertEntry certEntry = (CertEntry) entry; 1692 bagAttrs = 1693 getBagAttributes(certEntry.alias, certEntry.keyId, 1694 certEntry.trustedKeyUsage, 1695 certEntry.attributes); 1696 } 1697 } else { 1698 // Trusted root CA certs and Intermediate CA certs do not 1699 // need to have a localKeyId, and hence localKeyId is null 1700 // This change is made to meet NSS/Netscape requirements. 1701 // NSS pkcs12 library requires trusted CA certs in the 1702 // certificate chain to have unique or null localKeyID. 1703 // However, IE/OpenSSL do not impose this restriction. 1704 bagAttrs = getBagAttributes( 1705 cert.getSubjectX500Principal().getName(), null, 1706 entry.attributes); 1707 } 1708 if (bagAttrs != null) { 1709 safeBag.write(bagAttrs); 1710 } 1711 1712 // wrap as Sequence 1713 out.write(DerValue.tag_Sequence, safeBag); 1714 } // for cert-chain 1715 } 1716 1717 // wrap as SequenceOf SafeBag 1718 DerOutputStream safeBagValue = new DerOutputStream(); 1719 safeBagValue.write(DerValue.tag_SequenceOf, out); 1720 byte[] safeBagData = safeBagValue.toByteArray(); 1721 1722 // encrypt the content (EncryptedContentInfo) 1723 byte[] encrContentInfo = encryptContent(safeBagData, password); 1724 1725 // -- SEQUENCE of EncryptedData 1726 DerOutputStream encrData = new DerOutputStream(); 1727 DerOutputStream encrDataContent = new DerOutputStream(); 1728 encrData.putInteger(0); 1729 encrData.write(encrContentInfo); 1730 encrDataContent.write(DerValue.tag_Sequence, encrData); 1731 return encrDataContent.toByteArray(); 1732 } 1733 1734 /* 1735 * Create SafeContent Data content type. 1736 * Includes encrypted secret key in a SafeBag of type SecretBag. 1737 * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag. 1738 * Each PKCS8ShroudedKeyBag includes pkcs12 attributes 1739 * (see comments in getBagAttributes) 1740 */ 1741 private byte[] createSafeContent() 1742 throws CertificateException, IOException { 1743 1744 DerOutputStream out = new DerOutputStream(); 1745 for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) { 1746 1747 String alias = e.nextElement(); 1748 Entry entry = entries.get(alias); 1749 if (entry == null || (!(entry instanceof KeyEntry))) { 1750 continue; 1751 } 1752 DerOutputStream safeBag = new DerOutputStream(); 1753 KeyEntry keyEntry = (KeyEntry) entry; 1754 1755 // DER encode the private key 1756 if (keyEntry instanceof PrivateKeyEntry) { 1757 // Create SafeBag of type pkcs8ShroudedKeyBag 1758 safeBag.putOID(PKCS8ShroudedKeyBag_OID); 1759 1760 // get the encrypted private key 1761 byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey; 1762 EncryptedPrivateKeyInfo encrInfo = null; 1763 try { 1764 encrInfo = new EncryptedPrivateKeyInfo(encrBytes); 1765 1766 } catch (IOException ioe) { 1767 throw new IOException("Private key not stored as " 1768 + "PKCS#8 EncryptedPrivateKeyInfo" 1769 + ioe.getMessage()); 1770 } 1771 1772 // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. 1773 DerOutputStream bagValue = new DerOutputStream(); 1774 bagValue.write(encrInfo.getEncoded()); 1775 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1776 true, (byte) 0), bagValue); 1777 1778 // DER encode the secret key 1779 } else if (keyEntry instanceof SecretKeyEntry) { 1780 // Create SafeBag of type SecretBag 1781 safeBag.putOID(SecretBag_OID); 1782 1783 // Create a SecretBag 1784 DerOutputStream secretBag = new DerOutputStream(); 1785 secretBag.putOID(PKCS8ShroudedKeyBag_OID); 1786 1787 // Write secret key in a context-specific tag 1788 DerOutputStream secretKeyValue = new DerOutputStream(); 1789 secretKeyValue.putOctetString( 1790 ((SecretKeyEntry) keyEntry).protectedSecretKey); 1791 secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1792 true, (byte) 0), secretKeyValue); 1793 1794 // Wrap SecretBag in a Sequence 1795 DerOutputStream secretBagSeq = new DerOutputStream(); 1796 secretBagSeq.write(DerValue.tag_Sequence, secretBag); 1797 byte[] secretBagValue = secretBagSeq.toByteArray(); 1798 1799 // Wrap the secret bag in a context-specific tag. 1800 DerOutputStream bagValue = new DerOutputStream(); 1801 bagValue.write(secretBagValue); 1802 1803 // Write SafeBag value 1804 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, 1805 true, (byte) 0), bagValue); 1806 } else { 1807 continue; // skip this entry 1808 } 1809 1810 // write SafeBag Attributes 1811 byte[] bagAttrs = 1812 getBagAttributes(alias, entry.keyId, entry.attributes); 1813 safeBag.write(bagAttrs); 1814 1815 // wrap as Sequence 1816 out.write(DerValue.tag_Sequence, safeBag); 1817 } 1818 1819 // wrap as Sequence 1820 DerOutputStream safeBagValue = new DerOutputStream(); 1821 safeBagValue.write(DerValue.tag_Sequence, out); 1822 return safeBagValue.toByteArray(); 1823 } 1824 1825 1826 /* 1827 * Encrypt the contents using Password-based (PBE) encryption 1828 * as defined in PKCS #5. 1829 * 1830 * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used 1831 * to derive the key and IV. 1832 * 1833 * @return encrypted contents encoded as EncryptedContentInfo 1834 */ 1835 private byte[] encryptContent(byte[] data, char[] password) 1836 throws IOException { 1837 1838 byte[] encryptedData = null; 1839 1840 // create AlgorithmParameters 1841 AlgorithmParameters algParams = 1842 getAlgorithmParameters("PBEWithSHA1AndRC2_40"); 1843 DerOutputStream bytes = new DerOutputStream(); 1844 AlgorithmId algId = 1845 new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams); 1846 algId.encode(bytes); 1847 byte[] encodedAlgId = bytes.toByteArray(); 1848 1849 try { 1850 // Use JCE 1851 SecretKey skey = getPBEKey(password); 1852 Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40"); 1853 cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); 1854 encryptedData = cipher.doFinal(data); 1855 1856 if (debug != null) { 1857 debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + 1858 ")"); 1859 } 1860 1861 } catch (Exception e) { 1862 throw new IOException("Failed to encrypt" + 1863 " safe contents entry: " + e, e); 1864 } 1865 1866 // create EncryptedContentInfo 1867 DerOutputStream bytes2 = new DerOutputStream(); 1868 bytes2.putOID(ContentInfo.DATA_OID); 1869 bytes2.write(encodedAlgId); 1870 1871 // Wrap encrypted data in a context-specific tag. 1872 DerOutputStream tmpout2 = new DerOutputStream(); 1873 tmpout2.putOctetString(encryptedData); 1874 bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, 1875 false, (byte)0), tmpout2); 1876 1877 // wrap EncryptedContentInfo in a Sequence 1878 DerOutputStream out = new DerOutputStream(); 1879 out.write(DerValue.tag_Sequence, bytes2); 1880 return out.toByteArray(); 1881 } 1882 1883 /** 1884 * Loads the keystore from the given input stream. 1885 * 1886 * <p>If a password is given, it is used to check the integrity of the 1887 * keystore data. Otherwise, the integrity of the keystore is not checked. 1888 * 1889 * @param stream the input stream from which the keystore is loaded 1890 * @param password the (optional) password used to check the integrity of 1891 * the keystore. 1892 * 1893 * @exception IOException if there is an I/O or format problem with the 1894 * keystore data 1895 * @exception NoSuchAlgorithmException if the algorithm used to check 1896 * the integrity of the keystore cannot be found 1897 * @exception CertificateException if any of the certificates in the 1898 * keystore could not be loaded 1899 */ 1900 public synchronized void engineLoad(InputStream stream, char[] password) 1901 throws IOException, NoSuchAlgorithmException, CertificateException 1902 { 1903 DataInputStream dis; 1904 CertificateFactory cf = null; 1905 ByteArrayInputStream bais = null; 1906 byte[] encoded = null; 1907 1908 if (stream == null) 1909 return; 1910 1911 // reset the counter 1912 counter = 0; 1913 1914 DerValue val = new DerValue(stream); 1915 DerInputStream s = val.toDerInputStream(); 1916 int version = s.getInteger(); 1917 1918 if (version != VERSION_3) { 1919 throw new IOException("PKCS12 keystore not in version 3 format"); 1920 } 1921 1922 entries.clear(); 1923 1924 /* 1925 * Read the authSafe. 1926 */ 1927 byte[] authSafeData; 1928 ContentInfo authSafe = new ContentInfo(s); 1929 ObjectIdentifier contentType = authSafe.getContentType(); 1930 1931 if (contentType.equals((Object)ContentInfo.DATA_OID)) { 1932 authSafeData = authSafe.getData(); 1933 } else /* signed data */ { 1934 throw new IOException("public key protected PKCS12 not supported"); 1935 } 1936 1937 DerInputStream as = new DerInputStream(authSafeData); 1938 DerValue[] safeContentsArray = as.getSequence(2); 1939 int count = safeContentsArray.length; 1940 1941 // reset the counters at the start 1942 privateKeyCount = 0; 1943 secretKeyCount = 0; 1944 certificateCount = 0; 1945 1946 /* 1947 * Spin over the ContentInfos. 1948 */ 1949 for (int i = 0; i < count; i++) { 1950 byte[] safeContentsData; 1951 ContentInfo safeContents; 1952 DerInputStream sci; 1953 byte[] eAlgId = null; 1954 1955 sci = new DerInputStream(safeContentsArray[i].toByteArray()); 1956 safeContents = new ContentInfo(sci); 1957 contentType = safeContents.getContentType(); 1958 safeContentsData = null; 1959 if (contentType.equals((Object)ContentInfo.DATA_OID)) { 1960 1961 if (debug != null) { 1962 debug.println("Loading PKCS#7 data content-type"); 1963 } 1964 1965 safeContentsData = safeContents.getData(); 1966 } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) { 1967 if (password == null) { 1968 1969 if (debug != null) { 1970 debug.println("Warning: skipping PKCS#7 encryptedData" + 1971 " content-type - no password was supplied"); 1972 } 1973 continue; 1974 } 1975 1976 if (debug != null) { 1977 debug.println("Loading PKCS#7 encryptedData content-type"); 1978 } 1979 1980 DerInputStream edi = 1981 safeContents.getContent().toDerInputStream(); 1982 int edVersion = edi.getInteger(); 1983 DerValue[] seq = edi.getSequence(2); 1984 ObjectIdentifier edContentType = seq[0].getOID(); 1985 eAlgId = seq[1].toByteArray(); 1986 if (!seq[2].isContextSpecific((byte)0)) { 1987 throw new IOException("encrypted content not present!"); 1988 } 1989 byte newTag = DerValue.tag_OctetString; 1990 if (seq[2].isConstructed()) 1991 newTag |= 0x20; 1992 seq[2].resetTag(newTag); 1993 safeContentsData = seq[2].getOctetString(); 1994 1995 // parse Algorithm parameters 1996 DerInputStream in = seq[1].toDerInputStream(); 1997 ObjectIdentifier algOid = in.getOID(); 1998 AlgorithmParameters algParams = parseAlgParameters(algOid, in); 1999 2000 while (true) { 2001 try { 2002 // Use JCE 2003 SecretKey skey = getPBEKey(password); 2004 Cipher cipher = Cipher.getInstance(algOid.toString()); 2005 cipher.init(Cipher.DECRYPT_MODE, skey, algParams); 2006 safeContentsData = cipher.doFinal(safeContentsData); 2007 break; 2008 } catch (Exception e) { 2009 if (password.length == 0) { 2010 // Retry using an empty password 2011 // without a NULL terminator. 2012 password = new char[1]; 2013 continue; 2014 } 2015 throw new IOException("keystore password was incorrect", 2016 new UnrecoverableKeyException( 2017 "failed to decrypt safe contents entry: " + e)); 2018 } 2019 } 2020 } else { 2021 throw new IOException("public key protected PKCS12" + 2022 " not supported"); 2023 } 2024 DerInputStream sc = new DerInputStream(safeContentsData); 2025 loadSafeContents(sc, password); 2026 } 2027 2028 // The MacData is optional. 2029 if (password != null && s.available() > 0) { 2030 MacData macData = new MacData(s); 2031 try { 2032 String algName = 2033 macData.getDigestAlgName().toUpperCase(Locale.ENGLISH); 2034 2035 // Change SHA-1 to SHA1 2036 algName = algName.replace("-", ""); 2037 2038 // generate MAC (MAC key is created within JCE) 2039 Mac m = Mac.getInstance("HmacPBE" + algName); 2040 PBEParameterSpec params = 2041 new PBEParameterSpec(macData.getSalt(), 2042 macData.getIterations()); 2043 SecretKey key = getPBEKey(password); 2044 m.init(key, params); 2045 m.update(authSafeData); 2046 byte[] macResult = m.doFinal(); 2047 2048 if (debug != null) { 2049 debug.println("Checking keystore integrity " + 2050 "(MAC algorithm: " + m.getAlgorithm() + ")"); 2051 } 2052 2053 if (!Arrays.equals(macData.getDigest(), macResult)) { 2054 throw new SecurityException("Failed PKCS12" + 2055 " integrity checking"); 2056 } 2057 } catch (Exception e) { 2058 throw new IOException("Integrity check failed: " + e, e); 2059 } 2060 } 2061 2062 /* 2063 * Match up private keys with certificate chains. 2064 */ 2065 PrivateKeyEntry[] list = 2066 keyList.toArray(new PrivateKeyEntry[keyList.size()]); 2067 for (int m = 0; m < list.length; m++) { 2068 PrivateKeyEntry entry = list[m]; 2069 if (entry.keyId != null) { 2070 ArrayList<X509Certificate> chain = 2071 new ArrayList<X509Certificate>(); 2072 X509Certificate cert = findMatchedCertificate(entry); 2073 2074 mainloop: 2075 while (cert != null) { 2076 // Check for loops in the certificate chain 2077 if (!chain.isEmpty()) { 2078 for (X509Certificate chainCert : chain) { 2079 if (cert.equals(chainCert)) { 2080 if (debug != null) { 2081 debug.println("Loop detected in " + 2082 "certificate chain. Skip adding " + 2083 "repeated cert to chain. Subject: " + 2084 cert.getSubjectX500Principal() 2085 .toString()); 2086 } 2087 break mainloop; 2088 } 2089 } 2090 } 2091 chain.add(cert); 2092 X500Principal issuerDN = cert.getIssuerX500Principal(); 2093 if (issuerDN.equals(cert.getSubjectX500Principal())) { 2094 break; 2095 } 2096 cert = certsMap.get(issuerDN); 2097 } 2098 /* Update existing KeyEntry in entries table */ 2099 if (chain.size() > 0) 2100 entry.chain = chain.toArray(new Certificate[chain.size()]); 2101 } 2102 } 2103 2104 if (debug != null) { 2105 if (privateKeyCount > 0) { 2106 debug.println("Loaded " + privateKeyCount + 2107 " protected private key(s)"); 2108 } 2109 if (secretKeyCount > 0) { 2110 debug.println("Loaded " + secretKeyCount + 2111 " protected secret key(s)"); 2112 } 2113 if (certificateCount > 0) { 2114 debug.println("Loaded " + certificateCount + 2115 " certificate(s)"); 2116 } 2117 } 2118 2119 certEntries.clear(); 2120 certsMap.clear(); 2121 keyList.clear(); 2122 } 2123 2124 /** 2125 * Locates a matched CertEntry from certEntries, and returns its cert. 2126 * @param entry the KeyEntry to match 2127 * @return a certificate, null if not found 2128 */ 2129 private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) { 2130 CertEntry keyIdMatch = null; 2131 CertEntry aliasMatch = null; 2132 for (CertEntry ce: certEntries) { 2133 if (Arrays.equals(entry.keyId, ce.keyId)) { 2134 keyIdMatch = ce; 2135 if (entry.alias.equalsIgnoreCase(ce.alias)) { 2136 // Full match! 2137 return ce.cert; 2138 } 2139 } else if (entry.alias.equalsIgnoreCase(ce.alias)) { 2140 aliasMatch = ce; 2141 } 2142 } 2143 // keyId match first, for compatibility 2144 if (keyIdMatch != null) return keyIdMatch.cert; 2145 else if (aliasMatch != null) return aliasMatch.cert; 2146 else return null; 2147 } 2148 2149 private void loadSafeContents(DerInputStream stream, char[] password) 2150 throws IOException, NoSuchAlgorithmException, CertificateException 2151 { 2152 DerValue[] safeBags = stream.getSequence(2); 2153 int count = safeBags.length; 2154 2155 /* 2156 * Spin over the SafeBags. 2157 */ 2158 for (int i = 0; i < count; i++) { 2159 ObjectIdentifier bagId; 2160 DerInputStream sbi; 2161 DerValue bagValue; 2162 Object bagItem = null; 2163 2164 sbi = safeBags[i].toDerInputStream(); 2165 bagId = sbi.getOID(); 2166 bagValue = sbi.getDerValue(); 2167 if (!bagValue.isContextSpecific((byte)0)) { 2168 throw new IOException("unsupported PKCS12 bag value type " 2169 + bagValue.tag); 2170 } 2171 bagValue = bagValue.data.getDerValue(); 2172 if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) { 2173 PrivateKeyEntry kEntry = new PrivateKeyEntry(); 2174 kEntry.protectedPrivKey = bagValue.toByteArray(); 2175 bagItem = kEntry; 2176 privateKeyCount++; 2177 } else if (bagId.equals((Object)CertBag_OID)) { 2178 DerInputStream cs = new DerInputStream(bagValue.toByteArray()); 2179 DerValue[] certValues = cs.getSequence(2); 2180 ObjectIdentifier certId = certValues[0].getOID(); 2181 if (!certValues[1].isContextSpecific((byte)0)) { 2182 throw new IOException("unsupported PKCS12 cert value type " 2183 + certValues[1].tag); 2184 } 2185 DerValue certValue = certValues[1].data.getDerValue(); 2186 CertificateFactory cf = CertificateFactory.getInstance("X509"); 2187 X509Certificate cert; 2188 cert = (X509Certificate)cf.generateCertificate 2189 (new ByteArrayInputStream(certValue.getOctetString())); 2190 bagItem = cert; 2191 certificateCount++; 2192 } else if (bagId.equals((Object)SecretBag_OID)) { 2193 DerInputStream ss = new DerInputStream(bagValue.toByteArray()); 2194 DerValue[] secretValues = ss.getSequence(2); 2195 ObjectIdentifier secretId = secretValues[0].getOID(); 2196 if (!secretValues[1].isContextSpecific((byte)0)) { 2197 throw new IOException( 2198 "unsupported PKCS12 secret value type " 2199 + secretValues[1].tag); 2200 } 2201 DerValue secretValue = secretValues[1].data.getDerValue(); 2202 SecretKeyEntry kEntry = new SecretKeyEntry(); 2203 kEntry.protectedSecretKey = secretValue.getOctetString(); 2204 bagItem = kEntry; 2205 secretKeyCount++; 2206 } else { 2207 2208 if (debug != null) { 2209 debug.println("Unsupported PKCS12 bag type: " + bagId); 2210 } 2211 } 2212 2213 DerValue[] attrSet; 2214 try { 2215 attrSet = sbi.getSet(3); 2216 } catch (IOException e) { 2217 // entry does not have attributes 2218 // Note: CA certs can have no attributes 2219 // OpenSSL generates pkcs12 with no attr for CA certs. 2220 attrSet = null; 2221 } 2222 2223 String alias = null; 2224 byte[] keyId = null; 2225 ObjectIdentifier[] trustedKeyUsage = null; 2226 Set<PKCS12Attribute> attributes = new HashSet<>(); 2227 2228 if (attrSet != null) { 2229 for (int j = 0; j < attrSet.length; j++) { 2230 byte[] encoded = attrSet[j].toByteArray(); 2231 DerInputStream as = new DerInputStream(encoded); 2232 DerValue[] attrSeq = as.getSequence(2); 2233 ObjectIdentifier attrId = attrSeq[0].getOID(); 2234 DerInputStream vs = 2235 new DerInputStream(attrSeq[1].toByteArray()); 2236 DerValue[] valSet; 2237 try { 2238 valSet = vs.getSet(1); 2239 } catch (IOException e) { 2240 throw new IOException("Attribute " + attrId + 2241 " should have a value " + e.getMessage()); 2242 } 2243 if (attrId.equals((Object)PKCS9FriendlyName_OID)) { 2244 alias = valSet[0].getBMPString(); 2245 } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) { 2246 keyId = valSet[0].getOctetString(); 2247 } else if 2248 (attrId.equals((Object)TrustedKeyUsage_OID)) { 2249 trustedKeyUsage = new ObjectIdentifier[valSet.length]; 2250 for (int k = 0; k < valSet.length; k++) { 2251 trustedKeyUsage[k] = valSet[k].getOID(); 2252 } 2253 } else { 2254 attributes.add(new PKCS12Attribute(encoded)); 2255 } 2256 } 2257 } 2258 2259 /* 2260 * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId) 2261 * are optional PKCS12 bagAttributes. But entries in the keyStore 2262 * are identified by their alias. Hence we need to have an 2263 * Unfriendlyname in the alias, if alias is null. The keyId 2264 * attribute is required to match the private key with the 2265 * certificate. If we get a bagItem of type KeyEntry with a 2266 * null keyId, we should skip it entirely. 2267 */ 2268 if (bagItem instanceof KeyEntry) { 2269 KeyEntry entry = (KeyEntry)bagItem; 2270 2271 if (bagItem instanceof PrivateKeyEntry) { 2272 if (keyId == null) { 2273 // Insert a localKeyID for the privateKey 2274 // Note: This is a workaround to allow null localKeyID 2275 // attribute in pkcs12 with one private key entry and 2276 // associated cert-chain 2277 if (privateKeyCount == 1) { 2278 keyId = "01".getBytes("UTF8"); 2279 } else { 2280 continue; 2281 } 2282 } 2283 } 2284 entry.keyId = keyId; 2285 // restore date if it exists 2286 String keyIdStr = new String(keyId, "UTF8"); 2287 Date date = null; 2288 if (keyIdStr.startsWith("Time ")) { 2289 try { 2290 date = new Date( 2291 Long.parseLong(keyIdStr.substring(5))); 2292 } catch (Exception e) { 2293 date = null; 2294 } 2295 } 2296 if (date == null) { 2297 date = new Date(); 2298 } 2299 entry.date = date; 2300 2301 if (bagItem instanceof PrivateKeyEntry) { 2302 keyList.add((PrivateKeyEntry) entry); 2303 } 2304 if (entry.attributes == null) { 2305 entry.attributes = new HashSet<>(); 2306 } 2307 entry.attributes.addAll(attributes); 2308 if (alias == null) { 2309 alias = getUnfriendlyName(); 2310 } 2311 entry.alias = alias; 2312 entries.put(alias.toLowerCase(Locale.ENGLISH), entry); 2313 2314 } else if (bagItem instanceof X509Certificate) { 2315 X509Certificate cert = (X509Certificate)bagItem; 2316 // Insert a localKeyID for the corresponding cert 2317 // Note: This is a workaround to allow null localKeyID 2318 // attribute in pkcs12 with one private key entry and 2319 // associated cert-chain 2320 if ((keyId == null) && (privateKeyCount == 1)) { 2321 // insert localKeyID only for EE cert or self-signed cert 2322 if (i == 0) { 2323 keyId = "01".getBytes("UTF8"); 2324 } 2325 } 2326 // Trusted certificate 2327 if (trustedKeyUsage != null) { 2328 if (alias == null) { 2329 alias = getUnfriendlyName(); 2330 } 2331 CertEntry certEntry = 2332 new CertEntry(cert, keyId, alias, trustedKeyUsage, 2333 attributes); 2334 entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry); 2335 } else { 2336 certEntries.add(new CertEntry(cert, keyId, alias)); 2337 } 2338 X500Principal subjectDN = cert.getSubjectX500Principal(); 2339 if (subjectDN != null) { 2340 if (!certsMap.containsKey(subjectDN)) { 2341 certsMap.put(subjectDN, cert); 2342 } 2343 } 2344 } 2345 } 2346 } 2347 2348 private String getUnfriendlyName() { 2349 counter++; 2350 return (String.valueOf(counter)); 2351 } 2352 }