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