1 /*
   2  * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider;
  27 
  28 import java.io.*;
  29 import java.security.*;
  30 import java.security.cert.Certificate;
  31 import java.security.cert.CertificateFactory;
  32 import java.security.cert.CertificateException;
  33 import java.util.*;
  34 
  35 import sun.security.pkcs.EncryptedPrivateKeyInfo;
  36 import sun.security.pkcs12.PKCS12KeyStore;
  37 import sun.security.util.Debug;
  38 import sun.security.util.IOUtils;
  39 import sun.security.util.KeyStoreDelegator;
  40 
  41 /**
  42  * This class provides the keystore implementation referred to as "JKS".
  43  *
  44  * @author Jan Luehe
  45  * @author David Brownell
  46  *
  47  *
  48  * @see KeyProtector
  49  * @see java.security.KeyStoreSpi
  50  * @see KeyTool
  51  *
  52  * @since 1.2
  53  */
  54 
  55 public abstract class JavaKeyStore extends KeyStoreSpi {
  56 
  57     // regular JKS
  58     public static final class JKS extends JavaKeyStore {
  59         String convertAlias(String alias) {
  60             return alias.toLowerCase(Locale.ENGLISH);
  61         }
  62     }
  63 
  64     // special JKS that uses case sensitive aliases
  65     public static final class CaseExactJKS extends JavaKeyStore {
  66         String convertAlias(String alias) {
  67             return alias;
  68         }
  69     }
  70 
  71     // special JKS that supports JKS/PKCS12/PEM file formats
  72     public static final class MultipleFormatJKS extends KeyStoreDelegator {
  73         public MultipleFormatJKS() {
  74             super("JKS", JKS.class,
  75                     List.of("PKCS12", "PEM"),
  76                     List.of(PKCS12KeyStore.class, PemKeyStore.class));
  77         }
  78     }
  79 
  80     private static final Debug debug = Debug.getInstance("keystore");
  81     private static final int MAGIC = 0xfeedfeed;
  82     private static final int VERSION_1 = 0x01;
  83     private static final int VERSION_2 = 0x02;
  84 
  85     // Private keys and their supporting certificate chains
  86     private static class KeyEntry {
  87         Date date; // the creation date of this entry
  88         byte[] protectedPrivKey;
  89         Certificate[] chain;
  90     };
  91 
  92     // Trusted certificates
  93     private static class TrustedCertEntry {
  94         Date date; // the creation date of this entry
  95         Certificate cert;
  96     };
  97 
  98     /**
  99      * Private keys and certificates are stored in a hashtable.
 100      * Hash entries are keyed by alias names.
 101      */
 102     private final Hashtable<String, Object> entries;
 103 
 104     JavaKeyStore() {
 105         entries = new Hashtable<String, Object>();
 106     }
 107 
 108     // convert an alias to internal form, overridden in subclasses:
 109     // lower case for regular JKS
 110     // original string for CaseExactJKS
 111     abstract String convertAlias(String alias);
 112 
 113     /**
 114      * Returns the key associated with the given alias, using the given
 115      * password to recover it.
 116      *
 117      * @param alias the alias name
 118      * @param password the password for recovering the key
 119      *
 120      * @return the requested key, or null if the given alias does not exist
 121      * or does not identify a <i>key entry</i>.
 122      *
 123      * @exception NoSuchAlgorithmException if the algorithm for recovering the
 124      * key cannot be found
 125      * @exception UnrecoverableKeyException if the key cannot be recovered
 126      * (e.g., the given password is wrong).
 127      */
 128     public Key engineGetKey(String alias, char[] password)
 129         throws NoSuchAlgorithmException, UnrecoverableKeyException
 130     {
 131         Object entry = entries.get(convertAlias(alias));
 132 
 133         if (entry == null || !(entry instanceof KeyEntry)) {
 134             return null;
 135         }
 136         if (password == null) {
 137             throw new UnrecoverableKeyException("Password must not be null");
 138         }
 139 
 140         byte[] passwordBytes = convertToBytes(password);
 141         KeyProtector keyProtector = new KeyProtector(passwordBytes);
 142         byte[] encrBytes = ((KeyEntry)entry).protectedPrivKey;
 143         EncryptedPrivateKeyInfo encrInfo;
 144         try {
 145             encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
 146             return keyProtector.recover(encrInfo);
 147         } catch (IOException ioe) {
 148             throw new UnrecoverableKeyException("Private key not stored as "
 149                                                 + "PKCS #8 "
 150                                                 + "EncryptedPrivateKeyInfo");
 151         } finally {
 152             Arrays.fill(passwordBytes, (byte) 0x00);
 153         }
 154     }
 155 
 156     /**
 157      * Returns the certificate chain associated with the given alias.
 158      *
 159      * @param alias the alias name
 160      *
 161      * @return the certificate chain (ordered with the user's certificate first
 162      * and the root certificate authority last), or null if the given alias
 163      * does not exist or does not contain a certificate chain (i.e., the given
 164      * alias identifies either a <i>trusted certificate entry</i> or a
 165      * <i>key entry</i> without a certificate chain).
 166      */
 167     public Certificate[] engineGetCertificateChain(String alias) {
 168         Object entry = entries.get(convertAlias(alias));
 169 
 170         if (entry != null && entry instanceof KeyEntry) {
 171             if (((KeyEntry)entry).chain == null) {
 172                 return null;
 173             } else {
 174                 return ((KeyEntry)entry).chain.clone();
 175             }
 176         } else {
 177             return null;
 178         }
 179     }
 180 
 181     /**
 182      * Returns the certificate associated with the given alias.
 183      *
 184      * <p>If the given alias name identifies a
 185      * <i>trusted certificate entry</i>, the certificate associated with that
 186      * entry is returned. If the given alias name identifies a
 187      * <i>key entry</i>, the first element of the certificate chain of that
 188      * entry is returned, or null if that entry does not have a certificate
 189      * chain.
 190      *
 191      * @param alias the alias name
 192      *
 193      * @return the certificate, or null if the given alias does not exist or
 194      * does not contain a certificate.
 195      */
 196     public Certificate engineGetCertificate(String alias) {
 197         Object entry = entries.get(convertAlias(alias));
 198 
 199         if (entry != null) {
 200             if (entry instanceof TrustedCertEntry) {
 201                 return ((TrustedCertEntry)entry).cert;
 202             } else {
 203                 if (((KeyEntry)entry).chain == null) {
 204                     return null;
 205                 } else {
 206                     return ((KeyEntry)entry).chain[0];
 207                 }
 208             }
 209         } else {
 210             return null;
 211         }
 212     }
 213 
 214     /**
 215      * Returns the creation date of the entry identified by the given alias.
 216      *
 217      * @param alias the alias name
 218      *
 219      * @return the creation date of this entry, or null if the given alias does
 220      * not exist
 221      */
 222     public Date engineGetCreationDate(String alias) {
 223         Object entry = entries.get(convertAlias(alias));
 224 
 225         if (entry != null) {
 226             if (entry instanceof TrustedCertEntry) {
 227                 return new Date(((TrustedCertEntry)entry).date.getTime());
 228             } else {
 229                 return new Date(((KeyEntry)entry).date.getTime());
 230             }
 231         } else {
 232             return null;
 233         }
 234     }
 235 
 236     /**
 237      * Assigns the given private key to the given alias, protecting
 238      * it with the given password as defined in PKCS8.
 239      *
 240      * <p>The given java.security.PrivateKey <code>key</code> must
 241      * be accompanied by a certificate chain certifying the
 242      * corresponding public key.
 243      *
 244      * <p>If the given alias already exists, the keystore information
 245      * associated with it is overridden by the given key and certificate
 246      * chain.
 247      *
 248      * @param alias the alias name
 249      * @param key the private key to be associated with the alias
 250      * @param password the password to protect the key
 251      * @param chain the certificate chain for the corresponding public
 252      * key (only required if the given key is of type
 253      * <code>java.security.PrivateKey</code>).
 254      *
 255      * @exception KeyStoreException if the given key is not a private key,
 256      * cannot be protected, or this operation fails for some other reason
 257      */
 258     public void engineSetKeyEntry(String alias, Key key, char[] password,
 259                                   Certificate[] chain)
 260         throws KeyStoreException
 261     {
 262         KeyProtector keyProtector;
 263         byte[] passwordBytes = null;
 264 
 265         if (!(key instanceof java.security.PrivateKey)) {
 266             throw new KeyStoreException("Cannot store non-PrivateKeys");
 267         }
 268         try {
 269             synchronized(entries) {
 270                 KeyEntry entry = new KeyEntry();
 271                 entry.date = new Date();
 272 
 273                 // Protect the encoding of the key
 274                 passwordBytes = convertToBytes(password);
 275                 keyProtector = new KeyProtector(passwordBytes);
 276                 entry.protectedPrivKey = keyProtector.protect(key);
 277 
 278                 // clone the chain
 279                 if ((chain != null) &&
 280                     (chain.length != 0)) {
 281                     entry.chain = chain.clone();
 282                 } else {
 283                     entry.chain = null;
 284                 }
 285 
 286                 entries.put(convertAlias(alias), entry);
 287             }
 288         } catch (NoSuchAlgorithmException nsae) {
 289             throw new KeyStoreException("Key protection algorithm not found");
 290         } finally {
 291             if (passwordBytes != null)
 292                 Arrays.fill(passwordBytes, (byte) 0x00);
 293         }
 294     }
 295 
 296     /**
 297      * Assigns the given key (that has already been protected) to the given
 298      * alias.
 299      *
 300      * <p>If the protected key is of type
 301      * <code>java.security.PrivateKey</code>, it must be accompanied by a
 302      * certificate chain certifying the corresponding public key. If the
 303      * underlying keystore implementation is of type <code>jks</code>,
 304      * <code>key</code> must be encoded as an
 305      * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
 306      *
 307      * <p>If the given alias already exists, the keystore information
 308      * associated with it is overridden by the given key (and possibly
 309      * certificate chain).
 310      *
 311      * @param alias the alias name
 312      * @param key the key (in protected format) to be associated with the alias
 313      * @param chain the certificate chain for the corresponding public
 314      * key (only useful if the protected key is of type
 315      * <code>java.security.PrivateKey</code>).
 316      *
 317      * @exception KeyStoreException if this operation fails.
 318      */
 319     public void engineSetKeyEntry(String alias, byte[] key,
 320                                   Certificate[] chain)
 321         throws KeyStoreException
 322     {
 323         synchronized(entries) {
 324             // key must be encoded as EncryptedPrivateKeyInfo as defined in
 325             // PKCS#8
 326             try {
 327                 new EncryptedPrivateKeyInfo(key);
 328             } catch (IOException ioe) {
 329                 throw new KeyStoreException("key is not encoded as "
 330                                             + "EncryptedPrivateKeyInfo");
 331             }
 332 
 333             KeyEntry entry = new KeyEntry();
 334             entry.date = new Date();
 335 
 336             entry.protectedPrivKey = key.clone();
 337             if ((chain != null) &&
 338                 (chain.length != 0)) {
 339                 entry.chain = chain.clone();
 340             } else {
 341                 entry.chain = null;
 342             }
 343 
 344             entries.put(convertAlias(alias), entry);
 345         }
 346     }
 347 
 348     /**
 349      * Assigns the given certificate to the given alias.
 350      *
 351      * <p>If the given alias already exists in this keystore and identifies a
 352      * <i>trusted certificate entry</i>, the certificate associated with it is
 353      * overridden by the given certificate.
 354      *
 355      * @param alias the alias name
 356      * @param cert the certificate
 357      *
 358      * @exception KeyStoreException if the given alias already exists and does
 359      * not identify a <i>trusted certificate entry</i>, or this operation
 360      * fails for some other reason.
 361      */
 362     public void engineSetCertificateEntry(String alias, Certificate cert)
 363         throws KeyStoreException
 364     {
 365         synchronized(entries) {
 366 
 367             Object entry = entries.get(convertAlias(alias));
 368             if ((entry != null) && (entry instanceof KeyEntry)) {
 369                 throw new KeyStoreException
 370                     ("Cannot overwrite own certificate");
 371             }
 372 
 373             TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
 374             trustedCertEntry.cert = cert;
 375             trustedCertEntry.date = new Date();
 376             entries.put(convertAlias(alias), trustedCertEntry);
 377         }
 378     }
 379 
 380     /**
 381      * Deletes the entry identified by the given alias from this keystore.
 382      *
 383      * @param alias the alias name
 384      *
 385      * @exception KeyStoreException if the entry cannot be removed.
 386      */
 387     public void engineDeleteEntry(String alias)
 388         throws KeyStoreException
 389     {
 390         synchronized(entries) {
 391             entries.remove(convertAlias(alias));
 392         }
 393     }
 394 
 395     /**
 396      * Lists all the alias names of this keystore.
 397      *
 398      * @return enumeration of the alias names
 399      */
 400     public Enumeration<String> engineAliases() {
 401         return entries.keys();
 402     }
 403 
 404     /**
 405      * Checks if the given alias exists in this keystore.
 406      *
 407      * @param alias the alias name
 408      *
 409      * @return true if the alias exists, false otherwise
 410      */
 411     public boolean engineContainsAlias(String alias) {
 412         return entries.containsKey(convertAlias(alias));
 413     }
 414 
 415     /**
 416      * Retrieves the number of entries in this keystore.
 417      *
 418      * @return the number of entries in this keystore
 419      */
 420     public int engineSize() {
 421         return entries.size();
 422     }
 423 
 424     /**
 425      * Returns true if the entry identified by the given alias is a
 426      * <i>key entry</i>, and false otherwise.
 427      *
 428      * @return true if the entry identified by the given alias is a
 429      * <i>key entry</i>, false otherwise.
 430      */
 431     public boolean engineIsKeyEntry(String alias) {
 432         Object entry = entries.get(convertAlias(alias));
 433         if ((entry != null) && (entry instanceof KeyEntry)) {
 434             return true;
 435         } else {
 436             return false;
 437         }
 438     }
 439 
 440     /**
 441      * Returns true if the entry identified by the given alias is a
 442      * <i>trusted certificate entry</i>, and false otherwise.
 443      *
 444      * @return true if the entry identified by the given alias is a
 445      * <i>trusted certificate entry</i>, false otherwise.
 446      */
 447     public boolean engineIsCertificateEntry(String alias) {
 448         Object entry = entries.get(convertAlias(alias));
 449         if ((entry != null) && (entry instanceof TrustedCertEntry)) {
 450             return true;
 451         } else {
 452             return false;
 453         }
 454     }
 455 
 456     /**
 457      * Returns the (alias) name of the first keystore entry whose certificate
 458      * matches the given certificate.
 459      *
 460      * <p>This method attempts to match the given certificate with each
 461      * keystore entry. If the entry being considered
 462      * is a <i>trusted certificate entry</i>, the given certificate is
 463      * compared to that entry's certificate. If the entry being considered is
 464      * a <i>key entry</i>, the given certificate is compared to the first
 465      * element of that entry's certificate chain (if a chain exists).
 466      *
 467      * @param cert the certificate to match with.
 468      *
 469      * @return the (alias) name of the first entry with matching certificate,
 470      * or null if no such entry exists in this keystore.
 471      */
 472     public String engineGetCertificateAlias(Certificate cert) {
 473         Certificate certElem;
 474 
 475         for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
 476             String alias = e.nextElement();
 477             Object entry = entries.get(alias);
 478             if (entry instanceof TrustedCertEntry) {
 479                 certElem = ((TrustedCertEntry)entry).cert;
 480             } else if (((KeyEntry)entry).chain != null) {
 481                 certElem = ((KeyEntry)entry).chain[0];
 482             } else {
 483                 continue;
 484             }
 485             if (certElem.equals(cert)) {
 486                 return alias;
 487             }
 488         }
 489         return null;
 490     }
 491 
 492     /**
 493      * Stores this keystore to the given output stream, and protects its
 494      * integrity with the given password.
 495      *
 496      * @param stream the output stream to which this keystore is written.
 497      * @param password the password to generate the keystore integrity check
 498      *
 499      * @exception IOException if there was an I/O problem with data
 500      * @exception NoSuchAlgorithmException if the appropriate data integrity
 501      * algorithm could not be found
 502      * @exception CertificateException if any of the certificates included in
 503      * the keystore data could not be stored
 504      */
 505     public void engineStore(OutputStream stream, char[] password)
 506         throws IOException, NoSuchAlgorithmException, CertificateException
 507     {
 508         synchronized(entries) {
 509             /*
 510              * KEYSTORE FORMAT:
 511              *
 512              * Magic number (big-endian integer),
 513              * Version of this file format (big-endian integer),
 514              *
 515              * Count (big-endian integer),
 516              * followed by "count" instances of either:
 517              *
 518              *     {
 519              *      tag=1 (big-endian integer),
 520              *      alias (UTF string)
 521              *      timestamp
 522              *      encrypted private-key info according to PKCS #8
 523              *          (integer length followed by encoding)
 524              *      cert chain (integer count, then certs; for each cert,
 525              *          integer length followed by encoding)
 526              *     }
 527              *
 528              * or:
 529              *
 530              *     {
 531              *      tag=2 (big-endian integer)
 532              *      alias (UTF string)
 533              *      timestamp
 534              *      cert (integer length followed by encoding)
 535              *     }
 536              *
 537              * ended by a keyed SHA1 hash (bytes only) of
 538              *     { password + whitener + preceding body }
 539              */
 540 
 541             // password is mandatory when storing
 542             if (password == null) {
 543                 throw new IllegalArgumentException("password can't be null");
 544             }
 545 
 546             byte[] encoded; // the certificate encoding
 547 
 548             MessageDigest md = getPreKeyedHash(password);
 549             DataOutputStream dos
 550                 = new DataOutputStream(new DigestOutputStream(stream, md));
 551 
 552             dos.writeInt(MAGIC);
 553             // always write the latest version
 554             dos.writeInt(VERSION_2);
 555 
 556             dos.writeInt(entries.size());
 557 
 558             for (Enumeration<String> e = entries.keys(); e.hasMoreElements();) {
 559 
 560                 String alias = e.nextElement();
 561                 Object entry = entries.get(alias);
 562 
 563                 if (entry instanceof KeyEntry) {
 564 
 565                     // Store this entry as a KeyEntry
 566                     dos.writeInt(1);
 567 
 568                     // Write the alias
 569                     dos.writeUTF(alias);
 570 
 571                     // Write the (entry creation) date
 572                     dos.writeLong(((KeyEntry)entry).date.getTime());
 573 
 574                     // Write the protected private key
 575                     dos.writeInt(((KeyEntry)entry).protectedPrivKey.length);
 576                     dos.write(((KeyEntry)entry).protectedPrivKey);
 577 
 578                     // Write the certificate chain
 579                     int chainLen;
 580                     if (((KeyEntry)entry).chain == null) {
 581                         chainLen = 0;
 582                     } else {
 583                         chainLen = ((KeyEntry)entry).chain.length;
 584                     }
 585                     dos.writeInt(chainLen);
 586                     for (int i = 0; i < chainLen; i++) {
 587                         encoded = ((KeyEntry)entry).chain[i].getEncoded();
 588                         dos.writeUTF(((KeyEntry)entry).chain[i].getType());
 589                         dos.writeInt(encoded.length);
 590                         dos.write(encoded);
 591                     }
 592                 } else {
 593 
 594                     // Store this entry as a certificate
 595                     dos.writeInt(2);
 596 
 597                     // Write the alias
 598                     dos.writeUTF(alias);
 599 
 600                     // Write the (entry creation) date
 601                     dos.writeLong(((TrustedCertEntry)entry).date.getTime());
 602 
 603                     // Write the trusted certificate
 604                     encoded = ((TrustedCertEntry)entry).cert.getEncoded();
 605                     dos.writeUTF(((TrustedCertEntry)entry).cert.getType());
 606                     dos.writeInt(encoded.length);
 607                     dos.write(encoded);
 608                 }
 609             }
 610 
 611             /*
 612              * Write the keyed hash which is used to detect tampering with
 613              * the keystore (such as deleting or modifying key or
 614              * certificate entries).
 615              */
 616             byte[] digest = md.digest();
 617 
 618             dos.write(digest);
 619             dos.flush();
 620         }
 621     }
 622 
 623     /**
 624      * Loads the keystore from the given input stream.
 625      *
 626      * <p>If a password is given, it is used to check the integrity of the
 627      * keystore data. Otherwise, the integrity of the keystore is not checked.
 628      *
 629      * @param stream the input stream from which the keystore is loaded
 630      * @param password the (optional) password used to check the integrity of
 631      * the keystore.
 632      *
 633      * @exception IOException if there is an I/O or format problem with the
 634      * keystore data
 635      * @exception NoSuchAlgorithmException if the algorithm used to check
 636      * the integrity of the keystore cannot be found
 637      * @exception CertificateException if any of the certificates in the
 638      * keystore could not be loaded
 639      */
 640     public void engineLoad(InputStream stream, char[] password)
 641         throws IOException, NoSuchAlgorithmException, CertificateException
 642     {
 643         synchronized(entries) {
 644             DataInputStream dis;
 645             MessageDigest md = null;
 646             CertificateFactory cf = null;
 647             Hashtable<String, CertificateFactory> cfs = null;
 648             ByteArrayInputStream bais = null;
 649             byte[] encoded = null;
 650             int trustedKeyCount = 0, privateKeyCount = 0;
 651 
 652             if (stream == null)
 653                 return;
 654 
 655             if (password != null) {
 656                 md = getPreKeyedHash(password);
 657                 dis = new DataInputStream(new DigestInputStream(stream, md));
 658             } else {
 659                 dis = new DataInputStream(stream);
 660             }
 661 
 662             // Body format: see store method
 663 
 664             int xMagic = dis.readInt();
 665             int xVersion = dis.readInt();
 666 
 667             if (xMagic!=MAGIC ||
 668                 (xVersion!=VERSION_1 && xVersion!=VERSION_2)) {
 669                 throw new IOException("Invalid keystore format");
 670             }
 671 
 672             if (xVersion == VERSION_1) {
 673                 cf = CertificateFactory.getInstance("X509");
 674             } else {
 675                 // version 2
 676                 cfs = new Hashtable<String, CertificateFactory>(3);
 677             }
 678 
 679             entries.clear();
 680             int count = dis.readInt();
 681 
 682             for (int i = 0; i < count; i++) {
 683                 int tag;
 684                 String alias;
 685 
 686                 tag = dis.readInt();
 687 
 688                 if (tag == 1) { // private key entry
 689                     privateKeyCount++;
 690                     KeyEntry entry = new KeyEntry();
 691 
 692                     // Read the alias
 693                     alias = dis.readUTF();
 694 
 695                     // Read the (entry creation) date
 696                     entry.date = new Date(dis.readLong());
 697 
 698                     // Read the private key
 699                     entry.protectedPrivKey =
 700                             IOUtils.readFully(dis, dis.readInt(), true);
 701 
 702                     // Read the certificate chain
 703                     int numOfCerts = dis.readInt();
 704                     if (numOfCerts > 0) {
 705                         List<Certificate> certs = new ArrayList<>(
 706                                 numOfCerts > 10 ? 10 : numOfCerts);
 707                         for (int j = 0; j < numOfCerts; j++) {
 708                             if (xVersion == 2) {
 709                                 // read the certificate type, and instantiate a
 710                                 // certificate factory of that type (reuse
 711                                 // existing factory if possible)
 712                                 String certType = dis.readUTF();
 713                                 if (cfs.containsKey(certType)) {
 714                                     // reuse certificate factory
 715                                     cf = cfs.get(certType);
 716                                 } else {
 717                                     // create new certificate factory
 718                                     cf = CertificateFactory.getInstance(certType);
 719                                     // store the certificate factory so we can
 720                                     // reuse it later
 721                                     cfs.put(certType, cf);
 722                                 }
 723                             }
 724                             // instantiate the certificate
 725                             encoded = IOUtils.readFully(dis, dis.readInt(), true);
 726                             bais = new ByteArrayInputStream(encoded);
 727                             certs.add(cf.generateCertificate(bais));
 728                             bais.close();
 729                         }
 730                         // We can be sure now that numOfCerts of certs are read
 731                         entry.chain = certs.toArray(new Certificate[numOfCerts]);
 732                     }
 733 
 734                     // Add the entry to the list
 735                     entries.put(alias, entry);
 736 
 737                 } else if (tag == 2) { // trusted certificate entry
 738                     trustedKeyCount++;
 739                     TrustedCertEntry entry = new TrustedCertEntry();
 740 
 741                     // Read the alias
 742                     alias = dis.readUTF();
 743 
 744                     // Read the (entry creation) date
 745                     entry.date = new Date(dis.readLong());
 746 
 747                     // Read the trusted certificate
 748                     if (xVersion == 2) {
 749                         // read the certificate type, and instantiate a
 750                         // certificate factory of that type (reuse
 751                         // existing factory if possible)
 752                         String certType = dis.readUTF();
 753                         if (cfs.containsKey(certType)) {
 754                             // reuse certificate factory
 755                             cf = cfs.get(certType);
 756                         } else {
 757                             // create new certificate factory
 758                             cf = CertificateFactory.getInstance(certType);
 759                             // store the certificate factory so we can
 760                             // reuse it later
 761                             cfs.put(certType, cf);
 762                         }
 763                     }
 764                     encoded = IOUtils.readFully(dis, dis.readInt(), true);
 765                     bais = new ByteArrayInputStream(encoded);
 766                     entry.cert = cf.generateCertificate(bais);
 767                     bais.close();
 768 
 769                     // Add the entry to the list
 770                     entries.put(alias, entry);
 771 
 772                 } else {
 773                     throw new IOException("Unrecognized keystore entry: " +
 774                             tag);
 775                 }
 776             }
 777 
 778             if (debug != null) {
 779                 debug.println("JavaKeyStore load: private key count: " +
 780                     privateKeyCount + ". trusted key count: " + trustedKeyCount);
 781             }
 782 
 783             /*
 784              * If a password has been provided, we check the keyed digest
 785              * at the end. If this check fails, the store has been tampered
 786              * with
 787              */
 788             if (password != null) {
 789                 byte[] computed = md.digest();
 790                 byte[] actual = new byte[computed.length];
 791                 dis.readFully(actual);
 792                 for (int i = 0; i < computed.length; i++) {
 793                     if (computed[i] != actual[i]) {
 794                         Throwable t = new UnrecoverableKeyException
 795                             ("Password verification failed");
 796                         throw (IOException)new IOException
 797                             ("Keystore was tampered with, or "
 798                             + "password was incorrect").initCause(t);
 799                     }
 800                 }
 801             }
 802         }
 803     }
 804 
 805     /**
 806      * To guard against tampering with the keystore, we append a keyed
 807      * hash with a bit of whitener.
 808      */
 809     private MessageDigest getPreKeyedHash(char[] password)
 810         throws NoSuchAlgorithmException, UnsupportedEncodingException
 811     {
 812 
 813         MessageDigest md = MessageDigest.getInstance("SHA");
 814         byte[] passwdBytes = convertToBytes(password);
 815         md.update(passwdBytes);
 816         Arrays.fill(passwdBytes, (byte) 0x00);
 817         md.update("Mighty Aphrodite".getBytes("UTF8"));
 818         return md;
 819     }
 820 
 821     /**
 822      * Helper method to convert char[] to byte[]
 823      */
 824 
 825     private byte[] convertToBytes(char[] password) {
 826         int i, j;
 827         byte[] passwdBytes = new byte[password.length * 2];
 828         for (i=0, j=0; i<password.length; i++) {
 829             passwdBytes[j++] = (byte)(password[i] >> 8);
 830             passwdBytes[j++] = (byte)password[i];
 831         }
 832         return passwdBytes;
 833     }
 834 
 835     /**
 836      * Probe the first few bytes of the keystore data stream for a valid
 837      * JKS keystore encoding.
 838      */
 839     @Override
 840     public boolean engineProbe(InputStream stream) throws IOException {
 841         DataInputStream dataStream;
 842         if (stream instanceof DataInputStream) {
 843             dataStream = (DataInputStream)stream;
 844         } else {
 845             dataStream = new DataInputStream(stream);
 846         }
 847 
 848         return MAGIC == dataStream.readInt();
 849     }
 850 }