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