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