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