1 /*
   2  * Copyright (c) 1998, 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 com.sun.crypto.provider;
  27 
  28 import sun.security.util.Debug;
  29 import sun.security.util.IOUtils;
  30 
  31 import java.io.*;
  32 import java.util.*;
  33 import java.security.AccessController;
  34 import java.security.DigestInputStream;
  35 import java.security.DigestOutputStream;
  36 import java.security.MessageDigest;
  37 import java.security.NoSuchAlgorithmException;
  38 import java.security.Key;
  39 import java.security.PrivateKey;
  40 import java.security.PrivilegedAction;
  41 import java.security.KeyStoreSpi;
  42 import java.security.KeyStoreException;
  43 import java.security.UnrecoverableKeyException;
  44 import java.security.cert.Certificate;
  45 import java.security.cert.CertificateFactory;
  46 import java.security.cert.CertificateException;
  47 import javax.crypto.SealedObject;
  48 
  49 /**
  50  * This class provides the keystore implementation referred to as "jceks".
  51  * This implementation strongly protects the keystore private keys using
  52  * triple-DES, where the triple-DES encryption/decryption key is derived from
  53  * the user's password.
  54  * The encrypted private keys are stored in the keystore in a standard format,
  55  * namely the <code>EncryptedPrivateKeyInfo</code> format defined in PKCS #8.
  56  *
  57  * @author Jan Luehe
  58  *
  59  *
  60  * @see java.security.KeyStoreSpi
  61  */
  62 
  63 public final class JceKeyStore extends KeyStoreSpi {
  64 
  65     private static final Debug debug = Debug.getInstance("keystore");
  66     private static final int JCEKS_MAGIC = 0xcececece;
  67     private static final int JKS_MAGIC = 0xfeedfeed;
  68     private static final int VERSION_1 = 0x01;
  69     private static final int VERSION_2 = 0x02;
  70 
  71     // Private key and supporting certificate chain
  72     private static final class PrivateKeyEntry {
  73         Date date; // the creation date of this entry
  74         byte[] protectedKey;
  75         Certificate[] chain;
  76     };
  77 
  78     // Secret key
  79     private static final class SecretKeyEntry {
  80         Date date; // the creation date of this entry
  81         SealedObject sealedKey;
  82 
  83         // Maximum possible length of sealedKey. Used to detect malicious
  84         // input data. This field is set to the file length of the keystore
  85         // at loading. It is useless when creating a new SecretKeyEntry
  86         // to be store in a keystore.
  87         int maxLength;
  88     }
  89 
  90     // Trusted certificate
  91     private static final class TrustedCertEntry {
  92         Date date; // the creation date of this entry
  93         Certificate cert;
  94     };
  95 
  96     /**
  97      * Private keys and certificates are stored in a hashtable.
  98      * Hash entries are keyed by alias names.
  99      */
 100     private Hashtable<String, Object> entries = new Hashtable<String, Object>();
 101 
 102     /**
 103      * Returns the key associated with the given alias, using the given
 104      * password to recover it.
 105      *
 106      * @param alias the alias name
 107      * @param password the password for recovering the key
 108      *
 109      * @return the requested key, or null if the given alias does not exist
 110      * or does not identify a <i>key entry</i>.
 111      *
 112      * @exception NoSuchAlgorithmException if the algorithm for recovering the
 113      * key cannot be found
 114      * @exception UnrecoverableKeyException if the key cannot be recovered
 115      * (e.g., the given password is wrong).
 116      */
 117     public Key engineGetKey(String alias, char[] password)
 118         throws NoSuchAlgorithmException, UnrecoverableKeyException
 119     {
 120         Key key = null;
 121 
 122         Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 123 
 124         if (!((entry instanceof PrivateKeyEntry) ||
 125               (entry instanceof SecretKeyEntry))) {
 126             return null;
 127         }
 128 
 129         KeyProtector keyProtector = new KeyProtector(password);
 130 
 131         if (entry instanceof PrivateKeyEntry) {
 132             byte[] encrBytes = ((PrivateKeyEntry)entry).protectedKey;
 133             EncryptedPrivateKeyInfo encrInfo;
 134             try {
 135                 encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
 136             } catch (IOException ioe) {
 137                 throw new UnrecoverableKeyException("Private key not stored "
 138                                                     + "as PKCS #8 " +
 139                                                     "EncryptedPrivateKeyInfo");
 140             }
 141             key = keyProtector.recover(encrInfo);
 142         } else {
 143             SecretKeyEntry ske = ((SecretKeyEntry)entry);
 144             key = keyProtector.unseal(ske.sealedKey, ske.maxLength);
 145         }
 146 
 147         return key;
 148     }
 149 
 150     /**
 151      * Returns the certificate chain associated with the given alias.
 152      *
 153      * @param alias the alias name
 154      *
 155      * @return the certificate chain (ordered with the user's certificate first
 156      * and the root certificate authority last), or null if the given alias
 157      * does not exist or does not contain a certificate chain (i.e., the given
 158      * alias identifies either a <i>trusted certificate entry</i> or a
 159      * <i>key entry</i> without a certificate chain).
 160      */
 161     public Certificate[] engineGetCertificateChain(String alias)
 162     {
 163         Certificate[] chain = null;
 164 
 165         Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 166 
 167         if ((entry instanceof PrivateKeyEntry)
 168             && (((PrivateKeyEntry)entry).chain != null)) {
 169             chain = ((PrivateKeyEntry)entry).chain.clone();
 170         }
 171 
 172         return chain;
 173     }
 174 
 175     /**
 176      * Returns the certificate associated with the given alias.
 177      *
 178      * <p>If the given alias name identifies a
 179      * <i>trusted certificate entry</i>, the certificate associated with that
 180      * entry is returned. If the given alias name identifies a
 181      * <i>key entry</i>, the first element of the certificate chain of that
 182      * entry is returned, or null if that entry does not have a certificate
 183      * chain.
 184      *
 185      * @param alias the alias name
 186      *
 187      * @return the certificate, or null if the given alias does not exist or
 188      * does not contain a certificate.
 189      */
 190     public Certificate engineGetCertificate(String alias) {
 191         Certificate cert = null;
 192 
 193         Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 194 
 195         if (entry != null) {
 196             if (entry instanceof TrustedCertEntry) {
 197                 cert = ((TrustedCertEntry)entry).cert;
 198             } else if ((entry instanceof PrivateKeyEntry) &&
 199                        (((PrivateKeyEntry)entry).chain != null)) {
 200                 cert = ((PrivateKeyEntry)entry).chain[0];
 201             }
 202         }
 203 
 204         return cert;
 205     }
 206 
 207     /**
 208      * Returns the creation date of the entry identified by the given alias.
 209      *
 210      * @param alias the alias name
 211      *
 212      * @return the creation date of this entry, or null if the given alias does
 213      * not exist
 214      */
 215     public Date engineGetCreationDate(String alias) {
 216         Date date = null;
 217 
 218         Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 219 
 220         if (entry != null) {
 221             // We have to create a new instance of java.util.Date because
 222             // dates are not immutable
 223             if (entry instanceof TrustedCertEntry) {
 224                 date = new Date(((TrustedCertEntry)entry).date.getTime());
 225             } else if (entry instanceof PrivateKeyEntry) {
 226                 date = new Date(((PrivateKeyEntry)entry).date.getTime());
 227             } else {
 228                 date = new Date(((SecretKeyEntry)entry).date.getTime());
 229             }
 230         }
 231 
 232         return date;
 233     }
 234 
 235     /**
 236      * Assigns the given key to the given alias, protecting it with the given
 237      * password.
 238      *
 239      * <p>If the given key is of type <code>java.security.PrivateKey</code>,
 240      * it must be accompanied by a certificate chain certifying the
 241      * corresponding public key.
 242      *
 243      * <p>If the given alias already exists, the keystore information
 244      * associated with it is overridden by the given key (and possibly
 245      * certificate chain).
 246      *
 247      * @param alias the alias name
 248      * @param key the key to be associated with the alias
 249      * @param password the password to protect the key
 250      * @param chain the certificate chain for the corresponding public
 251      * key (only required if the given key is of type
 252      * <code>java.security.PrivateKey</code>).
 253      *
 254      * @exception KeyStoreException if the given key cannot be protected, or
 255      * this operation fails for some other reason
 256      */
 257     public void engineSetKeyEntry(String alias, Key key, char[] password,
 258                                   Certificate[] chain)
 259         throws KeyStoreException
 260     {
 261         synchronized(entries) {
 262             try {
 263                 KeyProtector keyProtector = new KeyProtector(password);
 264 
 265                 if (key instanceof PrivateKey) {
 266                     PrivateKeyEntry entry = new PrivateKeyEntry();
 267                     entry.date = new Date();
 268 
 269                     // protect the private key
 270                     entry.protectedKey = keyProtector.protect((PrivateKey)key);
 271 
 272                     // clone the chain
 273                     if ((chain != null) &&
 274                         (chain.length !=0)) {
 275                         entry.chain = chain.clone();
 276                     } else {
 277                         entry.chain = null;
 278                     }
 279 
 280                     // store the entry
 281                     entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
 282 
 283                 } else {
 284                     SecretKeyEntry entry = new SecretKeyEntry();
 285                     entry.date = new Date();
 286 
 287                     // seal and store the key
 288                     entry.sealedKey = keyProtector.seal(key);
 289                     entry.maxLength = Integer.MAX_VALUE;
 290                     entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
 291                 }
 292 
 293             } catch (Exception e) {
 294                 throw new KeyStoreException(e.getMessage());
 295             }
 296         }
 297     }
 298 
 299     /**
 300      * Assigns the given key (that has already been protected) to the given
 301      * alias.
 302      *
 303      * <p>If the protected key is of type
 304      * <code>java.security.PrivateKey</code>,
 305      * it must be accompanied by a certificate chain certifying the
 306      * corresponding public key.
 307      *
 308      * <p>If the given alias already exists, the keystore information
 309      * associated with it is overridden by the given key (and possibly
 310      * certificate chain).
 311      *
 312      * @param alias the alias name
 313      * @param key the key (in protected format) to be associated with the alias
 314      * @param chain the certificate chain for the corresponding public
 315      * key (only useful if the protected key is of type
 316      * <code>java.security.PrivateKey</code>).
 317      *
 318      * @exception KeyStoreException if this operation fails.
 319      */
 320     public void engineSetKeyEntry(String alias, byte[] key,
 321                                   Certificate[] chain)
 322         throws KeyStoreException
 323     {
 324         synchronized(entries) {
 325             // We assume it's a private key, because there is no standard
 326             // (ASN.1) encoding format for wrapped secret keys
 327             PrivateKeyEntry entry = new PrivateKeyEntry();
 328             entry.date = new Date();
 329 
 330             entry.protectedKey = key.clone();
 331             if ((chain != null) &&
 332                 (chain.length != 0)) {
 333                 entry.chain = chain.clone();
 334             } else {
 335                 entry.chain = null;
 336             }
 337 
 338             entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
 339         }
 340     }
 341 
 342     /**
 343      * Assigns the given certificate to the given alias.
 344      *
 345      * <p>If the given alias already exists in this keystore and identifies a
 346      * <i>trusted certificate entry</i>, the certificate associated with it is
 347      * overridden by the given certificate.
 348      *
 349      * @param alias the alias name
 350      * @param cert the certificate
 351      *
 352      * @exception KeyStoreException if the given alias already exists and does
 353      * not identify a <i>trusted certificate entry</i>, or this operation
 354      * fails for some other reason.
 355      */
 356     public void engineSetCertificateEntry(String alias, Certificate cert)
 357         throws KeyStoreException
 358     {
 359         synchronized(entries) {
 360 
 361             Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 362             if (entry != null) {
 363                 if (entry instanceof PrivateKeyEntry) {
 364                     throw new KeyStoreException("Cannot overwrite own "
 365                                                 + "certificate");
 366                 } else if (entry instanceof SecretKeyEntry) {
 367                     throw new KeyStoreException("Cannot overwrite secret key");
 368                 }
 369             }
 370 
 371             TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
 372             trustedCertEntry.cert = cert;
 373             trustedCertEntry.date = new Date();
 374             entries.put(alias.toLowerCase(Locale.ENGLISH), trustedCertEntry);
 375         }
 376     }
 377 
 378     /**
 379      * Deletes the entry identified by the given alias from this keystore.
 380      *
 381      * @param alias the alias name
 382      *
 383      * @exception KeyStoreException if the entry cannot be removed.
 384      */
 385     public void engineDeleteEntry(String alias)
 386         throws KeyStoreException
 387     {
 388         synchronized(entries) {
 389             entries.remove(alias.toLowerCase(Locale.ENGLISH));
 390         }
 391     }
 392 
 393     /**
 394      * Lists all the alias names of this keystore.
 395      *
 396      * @return enumeration of the alias names
 397      */
 398     public Enumeration<String> engineAliases() {
 399         return entries.keys();
 400     }
 401 
 402     /**
 403      * Checks if the given alias exists in this keystore.
 404      *
 405      * @param alias the alias name
 406      *
 407      * @return true if the alias exists, false otherwise
 408      */
 409     public boolean engineContainsAlias(String alias) {
 410         return entries.containsKey(alias.toLowerCase(Locale.ENGLISH));
 411     }
 412 
 413     /**
 414      * Retrieves the number of entries in this keystore.
 415      *
 416      * @return the number of entries in this keystore
 417      */
 418     public int engineSize() {
 419         return entries.size();
 420     }
 421 
 422     /**
 423      * Returns true if the entry identified by the given alias is a
 424      * <i>key entry</i>, and false otherwise.
 425      *
 426      * @return true if the entry identified by the given alias is a
 427      * <i>key entry</i>, false otherwise.
 428      */
 429     public boolean engineIsKeyEntry(String alias) {
 430         boolean isKey = false;
 431 
 432         Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 433         if ((entry instanceof PrivateKeyEntry)
 434             || (entry instanceof SecretKeyEntry)) {
 435             isKey = true;
 436         }
 437 
 438         return isKey;
 439     }
 440 
 441     /**
 442      * Returns true if the entry identified by the given alias is a
 443      * <i>trusted certificate entry</i>, and false otherwise.
 444      *
 445      * @return true if the entry identified by the given alias is a
 446      * <i>trusted certificate entry</i>, false otherwise.
 447      */
 448     public boolean engineIsCertificateEntry(String alias) {
 449         boolean isCert = false;
 450         Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
 451         if (entry instanceof TrustedCertEntry) {
 452             isCert = true;
 453         }
 454         return isCert;
 455     }
 456 
 457     /**
 458      * Returns the (alias) name of the first keystore entry whose certificate
 459      * matches the given certificate.
 460      *
 461      * <p>This method attempts to match the given certificate with each
 462      * keystore entry. If the entry being considered
 463      * is a <i>trusted certificate entry</i>, the given certificate is
 464      * compared to that entry's certificate. If the entry being considered is
 465      * a <i>key entry</i>, the given certificate is compared to the first
 466      * element of that entry's certificate chain (if a chain exists).
 467      *
 468      * @param cert the certificate to match with.
 469      *
 470      * @return the (alias) name of the first entry with matching certificate,
 471      * or null if no such entry exists in this keystore.
 472      */
 473     public String engineGetCertificateAlias(Certificate cert) {
 474         Certificate certElem;
 475 
 476         Enumeration<String> e = entries.keys();
 477         while (e.hasMoreElements()) {
 478             String alias = e.nextElement();
 479             Object entry = entries.get(alias);
 480             if (entry instanceof TrustedCertEntry) {
 481                 certElem = ((TrustedCertEntry)entry).cert;
 482             } else if ((entry instanceof PrivateKeyEntry) &&
 483                        (((PrivateKeyEntry)entry).chain != null)) {
 484                 certElem = ((PrivateKeyEntry)entry).chain[0];
 485             } else {
 486                 continue;
 487             }
 488             if (certElem.equals(cert)) {
 489                 return alias;
 490             }
 491         }
 492         return null;
 493     }
 494 
 495     /**
 496      * Stores this keystore to the given output stream, and protects its
 497      * integrity with the given password.
 498      *
 499      * @param stream the output stream to which this keystore is written.
 500      * @param password the password to generate the keystore integrity check
 501      *
 502      * @exception IOException if there was an I/O problem with data
 503      * @exception NoSuchAlgorithmException if the appropriate data integrity
 504      * algorithm could not be found
 505      * @exception CertificateException if any of the certificates included in
 506      * the keystore data could not be stored
 507      */
 508     public void engineStore(OutputStream stream, char[] password)
 509         throws IOException, NoSuchAlgorithmException, CertificateException
 510     {
 511         synchronized(entries) {
 512             /*
 513              * KEYSTORE FORMAT:
 514              *
 515              * Magic number (big-endian integer),
 516              * Version of this file format (big-endian integer),
 517              *
 518              * Count (big-endian integer),
 519              * followed by "count" instances of either:
 520              *
 521              *     {
 522              *      tag=1 (big-endian integer)
 523              *      alias (UTF string)
 524              *      timestamp
 525              *      encrypted private-key info according to PKCS #8
 526              *          (integer length followed by encoding)
 527              *      cert chain (integer count followed by certs;
 528              *          for each cert: type UTF string, followed by integer
 529              *              length, followed by encoding)
 530              *     }
 531              *
 532              * or:
 533              *
 534              *     {
 535              *      tag=2 (big-endian integer)
 536              *      alias (UTF string)
 537              *      timestamp
 538              *      cert (type UTF string, followed by integer length,
 539              *          followed by encoding)
 540              *     }
 541              *
 542              * or:
 543              *
 544              *     {
 545              *      tag=3 (big-endian integer)
 546              *      alias (UTF string)
 547              *      timestamp
 548              *      sealed secret key (in serialized form)
 549              *     }
 550              *
 551              * ended by a keyed SHA1 hash (bytes only) of
 552              *     { password + whitener + preceding body }
 553              */
 554 
 555             // password is mandatory when storing
 556             if (password == null) {
 557                 throw new IllegalArgumentException("password can't be null");
 558             }
 559 
 560             byte[] encoded; // the certificate encoding
 561 
 562             MessageDigest md = getPreKeyedHash(password);
 563             DataOutputStream dos
 564                 = new DataOutputStream(new DigestOutputStream(stream, md));
 565             // NOTE: don't pass dos to oos at this point or it'll corrupt
 566             // the keystore!!!
 567             ObjectOutputStream oos = null;
 568             try {
 569                 dos.writeInt(JCEKS_MAGIC);
 570                 dos.writeInt(VERSION_2); // always write the latest version
 571 
 572                 dos.writeInt(entries.size());
 573 
 574                 Enumeration<String> e = entries.keys();
 575                 while (e.hasMoreElements()) {
 576 
 577                     String alias = e.nextElement();
 578                     Object entry = entries.get(alias);
 579 
 580                     if (entry instanceof PrivateKeyEntry) {
 581 
 582                         PrivateKeyEntry pentry = (PrivateKeyEntry)entry;
 583 
 584                         // write PrivateKeyEntry tag
 585                         dos.writeInt(1);
 586 
 587                         // write the alias
 588                         dos.writeUTF(alias);
 589 
 590                         // write the (entry creation) date
 591                         dos.writeLong(pentry.date.getTime());
 592 
 593                         // write the protected private key
 594                         dos.writeInt(pentry.protectedKey.length);
 595                         dos.write(pentry.protectedKey);
 596 
 597                         // write the certificate chain
 598                         int chainLen;
 599                         if (pentry.chain == null) {
 600                             chainLen = 0;
 601                         } else {
 602                             chainLen = pentry.chain.length;
 603                         }
 604                         dos.writeInt(chainLen);
 605                         for (int i = 0; i < chainLen; i++) {
 606                             encoded = pentry.chain[i].getEncoded();
 607                             dos.writeUTF(pentry.chain[i].getType());
 608                             dos.writeInt(encoded.length);
 609                             dos.write(encoded);
 610                         }
 611 
 612                     } else if (entry instanceof TrustedCertEntry) {
 613 
 614                         // write TrustedCertEntry tag
 615                         dos.writeInt(2);
 616 
 617                         // write the alias
 618                         dos.writeUTF(alias);
 619 
 620                         // write the (entry creation) date
 621                         dos.writeLong(((TrustedCertEntry)entry).date.getTime());
 622 
 623                         // write the trusted certificate
 624                         encoded = ((TrustedCertEntry)entry).cert.getEncoded();
 625                         dos.writeUTF(((TrustedCertEntry)entry).cert.getType());
 626                         dos.writeInt(encoded.length);
 627                         dos.write(encoded);
 628 
 629                     } else {
 630 
 631                         // write SecretKeyEntry tag
 632                         dos.writeInt(3);
 633 
 634                         // write the alias
 635                         dos.writeUTF(alias);
 636 
 637                         // write the (entry creation) date
 638                         dos.writeLong(((SecretKeyEntry)entry).date.getTime());
 639 
 640                         // write the sealed key
 641                         oos = new ObjectOutputStream(dos);
 642                         oos.writeObject(((SecretKeyEntry)entry).sealedKey);
 643                         // NOTE: don't close oos here since we are still
 644                         // using dos!!!
 645                     }
 646                 }
 647 
 648                 /*
 649                  * Write the keyed hash which is used to detect tampering with
 650                  * the keystore (such as deleting or modifying key or
 651                  * certificate entries).
 652                  */
 653                 byte digest[] = md.digest();
 654 
 655                 dos.write(digest);
 656                 dos.flush();
 657             } finally {
 658                 if (oos != null) {
 659                     oos.close();
 660                 } else {
 661                     dos.close();
 662                 }
 663             }
 664         }
 665     }
 666 
 667     /**
 668      * Loads the keystore from the given input stream.
 669      *
 670      * <p>If a password is given, it is used to check the integrity of the
 671      * keystore data. Otherwise, the integrity of the keystore is not checked.
 672      *
 673      * @param stream the input stream from which the keystore is loaded
 674      * @param password the (optional) password used to check the integrity of
 675      * the keystore.
 676      *
 677      * @exception IOException if there is an I/O or format problem with the
 678      * keystore data
 679      * @exception NoSuchAlgorithmException if the algorithm used to check
 680      * the integrity of the keystore cannot be found
 681      * @exception CertificateException if any of the certificates in the
 682      * keystore could not be loaded
 683      */
 684     public void engineLoad(InputStream stream, char[] password)
 685         throws IOException, NoSuchAlgorithmException, CertificateException
 686     {
 687         synchronized(entries) {
 688             DataInputStream dis;
 689             MessageDigest md = null;
 690             CertificateFactory cf = null;
 691             Hashtable<String, CertificateFactory> cfs = null;
 692             ByteArrayInputStream bais = null;
 693             byte[] encoded = null;
 694             int trustedKeyCount = 0, privateKeyCount = 0, secretKeyCount = 0;
 695 
 696             if (stream == null)
 697                 return;
 698 
 699             byte[] allData = stream.readAllBytes();
 700             int fullLength = allData.length;
 701 
 702             stream = new ByteArrayInputStream(allData);
 703             if (password != null) {
 704                 md = getPreKeyedHash(password);
 705                 dis = new DataInputStream(new DigestInputStream(stream, md));
 706             } else {
 707                 dis = new DataInputStream(stream);
 708             }
 709             // NOTE: don't pass dis to ois at this point or it'll fail to load
 710             // the keystore!!!
 711             ObjectInputStream ois = null;
 712 
 713             try {
 714                 // Body format: see store method
 715 
 716                 int xMagic = dis.readInt();
 717                 int xVersion = dis.readInt();
 718 
 719                 // Accept the following keystore implementations:
 720                 // - JCEKS (this implementation), versions 1 and 2
 721                 // - JKS (Sun's keystore implementation in JDK 1.2),
 722                 //   versions 1 and 2
 723                 if (((xMagic != JCEKS_MAGIC) && (xMagic != JKS_MAGIC)) ||
 724                     ((xVersion != VERSION_1) && (xVersion != VERSION_2))) {
 725                     throw new IOException("Invalid keystore format");
 726                 }
 727 
 728                 if (xVersion == VERSION_1) {
 729                     cf = CertificateFactory.getInstance("X509");
 730                 } else {
 731                     // version 2
 732                     cfs = new Hashtable<>(3);
 733                 }
 734 
 735                 entries.clear();
 736                 int count = dis.readInt();
 737 
 738                 for (int i = 0; i < count; i++) {
 739                     int tag;
 740                     String alias;
 741 
 742                     tag = dis.readInt();
 743 
 744                     if (tag == 1) { // private-key entry
 745                         privateKeyCount++;
 746                         PrivateKeyEntry entry = new PrivateKeyEntry();
 747 
 748                         // read the alias
 749                         alias = dis.readUTF();
 750 
 751                         // read the (entry creation) date
 752                         entry.date = new Date(dis.readLong());
 753 
 754                         // read the private key
 755                         entry.protectedKey = IOUtils.readExactlyNBytes(dis, dis.readInt());
 756 
 757                         // read the certificate chain
 758                         int numOfCerts = dis.readInt();
 759                         List<Certificate> tmpCerts = new ArrayList<>();
 760                         for (int j = 0; j < numOfCerts; j++) {
 761                             if (xVersion == 2) {
 762                                 // read the certificate type, and instantiate a
 763                                 // certificate factory of that type (reuse
 764                                 // existing factory if possible)
 765                                 String certType = dis.readUTF();
 766                                 if (cfs.containsKey(certType)) {
 767                                     // reuse certificate factory
 768                                     cf = cfs.get(certType);
 769                                 } else {
 770                                     // create new certificate factory
 771                                     cf = CertificateFactory.getInstance(
 772                                         certType);
 773                                     // store the certificate factory so we can
 774                                     // reuse it later
 775                                     cfs.put(certType, cf);
 776                                 }
 777                             }
 778                             // instantiate the certificate
 779                             encoded = IOUtils.readExactlyNBytes(dis, dis.readInt());
 780                             bais = new ByteArrayInputStream(encoded);
 781                             tmpCerts.add(cf.generateCertificate(bais));
 782                         }
 783                         entry.chain = tmpCerts.toArray(
 784                                 new Certificate[numOfCerts]);
 785 
 786                         // Add the entry to the list
 787                         entries.put(alias, entry);
 788 
 789                     } else if (tag == 2) { // trusted certificate entry
 790                         trustedKeyCount++;
 791                         TrustedCertEntry entry = new TrustedCertEntry();
 792 
 793                         // read the alias
 794                         alias = dis.readUTF();
 795 
 796                         // read the (entry creation) date
 797                         entry.date = new Date(dis.readLong());
 798 
 799                         // read the trusted certificate
 800                         if (xVersion == 2) {
 801                             // read the certificate type, and instantiate a
 802                             // certificate factory of that type (reuse
 803                             // existing factory if possible)
 804                             String certType = dis.readUTF();
 805                             if (cfs.containsKey(certType)) {
 806                                 // reuse certificate factory
 807                                 cf = cfs.get(certType);
 808                             } else {
 809                                 // create new certificate factory
 810                                 cf = CertificateFactory.getInstance(certType);
 811                                 // store the certificate factory so we can
 812                                 // reuse it later
 813                                 cfs.put(certType, cf);
 814                             }
 815                         }
 816                         encoded = IOUtils.readExactlyNBytes(dis, dis.readInt());
 817                         bais = new ByteArrayInputStream(encoded);
 818                         entry.cert = cf.generateCertificate(bais);
 819 
 820                         // Add the entry to the list
 821                         entries.put(alias, entry);
 822 
 823                     } else if (tag == 3) { // secret-key entry
 824                         secretKeyCount++;
 825                         SecretKeyEntry entry = new SecretKeyEntry();
 826 
 827                         // read the alias
 828                         alias = dis.readUTF();
 829 
 830                         // read the (entry creation) date
 831                         entry.date = new Date(dis.readLong());
 832 
 833                         // read the sealed key
 834                         try {
 835                             ois = new ObjectInputStream(dis);
 836                             final ObjectInputStream ois2 = ois;
 837                             // Set a deserialization checker
 838                             AccessController.doPrivileged(
 839                                 (PrivilegedAction<Void>)() -> {
 840                                     ois2.setObjectInputFilter(
 841                                         new DeserializationChecker(fullLength));
 842                                     return null;
 843                             });
 844                             entry.sealedKey = (SealedObject)ois.readObject();
 845                             entry.maxLength = fullLength;
 846                             // NOTE: don't close ois here since we are still
 847                             // using dis!!!
 848                         } catch (ClassNotFoundException cnfe) {
 849                             throw new IOException(cnfe.getMessage());
 850                         } catch (InvalidClassException ice) {
 851                             throw new IOException("Invalid secret key format");
 852                         }
 853 
 854                         // Add the entry to the list
 855                         entries.put(alias, entry);
 856 
 857                     } else {
 858                         throw new IOException("Unrecognized keystore entry: " +
 859                                 tag);
 860                     }
 861                 }
 862 
 863                 if (debug != null) {
 864                     debug.println("JceKeyStore load: private key count: " +
 865                         privateKeyCount + ". trusted key count: " +
 866                         trustedKeyCount + ". secret key count: " +
 867                         secretKeyCount);
 868                 }
 869 
 870                 /*
 871                  * If a password has been provided, we check the keyed digest
 872                  * at the end. If this check fails, the store has been tampered
 873                  * with
 874                  */
 875                 if (password != null) {
 876                     byte[] computed = md.digest();
 877                     byte[] actual = IOUtils.readExactlyNBytes(dis, computed.length);
 878                     if (!MessageDigest.isEqual(computed, actual)) {
 879                         throw new IOException(
 880                                 "Keystore was tampered with, or "
 881                                         + "password was incorrect",
 882                                 new UnrecoverableKeyException(
 883                                         "Password verification failed"));
 884                     }
 885                 }
 886             }  finally {
 887                 if (ois != null) {
 888                     ois.close();
 889                 } else {
 890                     dis.close();
 891                 }
 892             }
 893         }
 894     }
 895 
 896     /**
 897      * To guard against tampering with the keystore, we append a keyed
 898      * hash with a bit of whitener.
 899      */
 900     private MessageDigest getPreKeyedHash(char[] password)
 901     throws NoSuchAlgorithmException, UnsupportedEncodingException {
 902         int i, j;
 903 
 904         MessageDigest md = MessageDigest.getInstance("SHA");
 905         byte[] passwdBytes = new byte[password.length * 2];
 906         for (i=0, j=0; i<password.length; i++) {
 907             passwdBytes[j++] = (byte)(password[i] >> 8);
 908             passwdBytes[j++] = (byte)password[i];
 909         }
 910         md.update(passwdBytes);
 911         for (i=0; i<passwdBytes.length; i++)
 912             passwdBytes[i] = 0;
 913         md.update("Mighty Aphrodite".getBytes("UTF8"));
 914         return md;
 915     }
 916 
 917     /**
 918      * Probe the first few bytes of the keystore data stream for a valid
 919      * JCEKS keystore encoding.
 920      */
 921     @Override
 922     public boolean engineProbe(InputStream stream) throws IOException {
 923         DataInputStream dataStream;
 924         if (stream instanceof DataInputStream) {
 925             dataStream = (DataInputStream)stream;
 926         } else {
 927             dataStream = new DataInputStream(stream);
 928         }
 929 
 930         return JCEKS_MAGIC == dataStream.readInt();
 931     }
 932 
 933     /*
 934      * An ObjectInputFilter that checks the format of the secret key being
 935      * deserialized.
 936      */
 937     private static class DeserializationChecker implements ObjectInputFilter {
 938 
 939         private static final int MAX_NESTED_DEPTH = 2;
 940 
 941         // Full length of keystore, anything inside a SecretKeyEntry should not
 942         // be bigger. Otherwise, must be illegal.
 943         private final int fullLength;
 944 
 945         public DeserializationChecker(int fullLength) {
 946             this.fullLength = fullLength;
 947         }
 948 
 949         @Override
 950         public ObjectInputFilter.Status
 951             checkInput(ObjectInputFilter.FilterInfo info) {
 952 
 953             // First run a custom filter
 954             long nestedDepth = info.depth();
 955             if ((nestedDepth == 1 &&
 956                         info.serialClass() != SealedObjectForKeyProtector.class) ||
 957                     info.arrayLength() > fullLength ||
 958                     (nestedDepth > MAX_NESTED_DEPTH &&
 959                         info.serialClass() != null &&
 960                         info.serialClass() != Object.class)) {
 961                 return Status.REJECTED;
 962             }
 963 
 964             // Next run the default filter, if available
 965             ObjectInputFilter defaultFilter =
 966                 ObjectInputFilter.Config.getSerialFilter();
 967             if (defaultFilter != null) {
 968                 return defaultFilter.checkInput(info);
 969             }
 970 
 971             return Status.UNDECIDED;
 972         }
 973     }
 974 }