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