0N/A/*
3261N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.provider;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.security.*;
0N/Aimport java.security.cert.Certificate;
0N/Aimport java.security.cert.CertificateFactory;
0N/Aimport java.security.cert.CertificateException;
0N/Aimport java.util.*;
2201N/Aimport sun.misc.IOUtils;
0N/A
0N/Aimport sun.security.pkcs.EncryptedPrivateKeyInfo;
0N/A
0N/A/**
0N/A * This class provides the keystore implementation referred to as "JKS".
0N/A *
0N/A * @author Jan Luehe
0N/A * @author David Brownell
0N/A *
0N/A *
0N/A * @see KeyProtector
0N/A * @see java.security.KeyStoreSpi
0N/A * @see KeyTool
0N/A *
0N/A * @since 1.2
0N/A */
0N/A
0N/Aabstract class JavaKeyStore extends KeyStoreSpi {
0N/A
0N/A // regular JKS
0N/A public static final class JKS extends JavaKeyStore {
0N/A String convertAlias(String alias) {
0N/A return alias.toLowerCase();
0N/A }
0N/A }
0N/A
0N/A // special JKS that uses case sensitive aliases
0N/A public static final class CaseExactJKS extends JavaKeyStore {
0N/A String convertAlias(String alias) {
0N/A return alias;
0N/A }
0N/A }
0N/A
0N/A private static final int MAGIC = 0xfeedfeed;
0N/A private static final int VERSION_1 = 0x01;
0N/A private static final int VERSION_2 = 0x02;
0N/A
0N/A // Private keys and their supporting certificate chains
0N/A private static class KeyEntry {
0N/A Date date; // the creation date of this entry
0N/A byte[] protectedPrivKey;
0N/A Certificate chain[];
0N/A };
0N/A
0N/A // Trusted certificates
0N/A private static class TrustedCertEntry {
0N/A Date date; // the creation date of this entry
0N/A Certificate cert;
0N/A };
0N/A
0N/A /**
0N/A * Private keys and certificates are stored in a hashtable.
0N/A * Hash entries are keyed by alias names.
0N/A */
0N/A private final Hashtable<String, Object> entries;
0N/A
0N/A JavaKeyStore() {
0N/A entries = new Hashtable<String, Object>();
0N/A }
0N/A
0N/A // convert an alias to internal form, overridden in subclasses:
0N/A // lower case for regular JKS
0N/A // original string for CaseExactJKS
0N/A abstract String convertAlias(String alias);
0N/A
0N/A /**
0N/A * Returns the key associated with the given alias, using the given
0N/A * password to recover it.
0N/A *
0N/A * @param alias the alias name
0N/A * @param password the password for recovering the key
0N/A *
0N/A * @return the requested key, or null if the given alias does not exist
0N/A * or does not identify a <i>key entry</i>.
0N/A *
0N/A * @exception NoSuchAlgorithmException if the algorithm for recovering the
0N/A * key cannot be found
0N/A * @exception UnrecoverableKeyException if the key cannot be recovered
0N/A * (e.g., the given password is wrong).
0N/A */
0N/A public Key engineGetKey(String alias, char[] password)
0N/A throws NoSuchAlgorithmException, UnrecoverableKeyException
0N/A {
0N/A Object entry = entries.get(convertAlias(alias));
0N/A
0N/A if (entry == null || !(entry instanceof KeyEntry)) {
0N/A return null;
0N/A }
0N/A if (password == null) {
0N/A throw new UnrecoverableKeyException("Password must not be null");
0N/A }
0N/A
0N/A KeyProtector keyProtector = new KeyProtector(password);
0N/A byte[] encrBytes = ((KeyEntry)entry).protectedPrivKey;
0N/A EncryptedPrivateKeyInfo encrInfo;
0N/A byte[] plain;
0N/A try {
0N/A encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
0N/A } catch (IOException ioe) {
0N/A throw new UnrecoverableKeyException("Private key not stored as "
0N/A + "PKCS #8 "
0N/A + "EncryptedPrivateKeyInfo");
0N/A }
0N/A return keyProtector.recover(encrInfo);
0N/A }
0N/A
0N/A /**
0N/A * Returns the certificate chain associated with the given alias.
0N/A *
0N/A * @param alias the alias name
0N/A *
0N/A * @return the certificate chain (ordered with the user's certificate first
0N/A * and the root certificate authority last), or null if the given alias
0N/A * does not exist or does not contain a certificate chain (i.e., the given
0N/A * alias identifies either a <i>trusted certificate entry</i> or a
0N/A * <i>key entry</i> without a certificate chain).
0N/A */
0N/A public Certificate[] engineGetCertificateChain(String alias) {
0N/A Object entry = entries.get(convertAlias(alias));
0N/A
0N/A if (entry != null && entry instanceof KeyEntry) {
0N/A if (((KeyEntry)entry).chain == null) {
0N/A return null;
0N/A } else {
0N/A return ((KeyEntry)entry).chain.clone();
0N/A }
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the certificate associated with the given alias.
0N/A *
0N/A * <p>If the given alias name identifies a
0N/A * <i>trusted certificate entry</i>, the certificate associated with that
0N/A * entry is returned. If the given alias name identifies a
0N/A * <i>key entry</i>, the first element of the certificate chain of that
0N/A * entry is returned, or null if that entry does not have a certificate
0N/A * chain.
0N/A *
0N/A * @param alias the alias name
0N/A *
0N/A * @return the certificate, or null if the given alias does not exist or
0N/A * does not contain a certificate.
0N/A */
0N/A public Certificate engineGetCertificate(String alias) {
0N/A Object entry = entries.get(convertAlias(alias));
0N/A
0N/A if (entry != null) {
0N/A if (entry instanceof TrustedCertEntry) {
0N/A return ((TrustedCertEntry)entry).cert;
0N/A } else {
0N/A if (((KeyEntry)entry).chain == null) {
0N/A return null;
0N/A } else {
0N/A return ((KeyEntry)entry).chain[0];
0N/A }
0N/A }
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the creation date of the entry identified by the given alias.
0N/A *
0N/A * @param alias the alias name
0N/A *
0N/A * @return the creation date of this entry, or null if the given alias does
0N/A * not exist
0N/A */
0N/A public Date engineGetCreationDate(String alias) {
0N/A Object entry = entries.get(convertAlias(alias));
0N/A
0N/A if (entry != null) {
0N/A if (entry instanceof TrustedCertEntry) {
0N/A return new Date(((TrustedCertEntry)entry).date.getTime());
0N/A } else {
0N/A return new Date(((KeyEntry)entry).date.getTime());
0N/A }
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Assigns the given private key to the given alias, protecting
0N/A * it with the given password as defined in PKCS8.
0N/A *
0N/A * <p>The given java.security.PrivateKey <code>key</code> must
0N/A * be accompanied by a certificate chain certifying the
0N/A * corresponding public key.
0N/A *
0N/A * <p>If the given alias already exists, the keystore information
0N/A * associated with it is overridden by the given key and certificate
0N/A * chain.
0N/A *
0N/A * @param alias the alias name
0N/A * @param key the private key to be associated with the alias
0N/A * @param password the password to protect the key
0N/A * @param chain the certificate chain for the corresponding public
0N/A * key (only required if the given key is of type
0N/A * <code>java.security.PrivateKey</code>).
0N/A *
0N/A * @exception KeyStoreException if the given key is not a private key,
0N/A * cannot be protected, or this operation fails for some other reason
0N/A */
0N/A public void engineSetKeyEntry(String alias, Key key, char[] password,
0N/A Certificate[] chain)
0N/A throws KeyStoreException
0N/A {
0N/A KeyProtector keyProtector = null;
0N/A
0N/A if (!(key instanceof java.security.PrivateKey)) {
0N/A throw new KeyStoreException("Cannot store non-PrivateKeys");
0N/A }
0N/A try {
0N/A synchronized(entries) {
0N/A KeyEntry entry = new KeyEntry();
0N/A entry.date = new Date();
0N/A
0N/A // Protect the encoding of the key
0N/A keyProtector = new KeyProtector(password);
0N/A entry.protectedPrivKey = keyProtector.protect(key);
0N/A
0N/A // clone the chain
0N/A if ((chain != null) &&
0N/A (chain.length != 0)) {
0N/A entry.chain = chain.clone();
0N/A } else {
0N/A entry.chain = null;
0N/A }
0N/A
0N/A entries.put(convertAlias(alias), entry);
0N/A }
0N/A } catch (NoSuchAlgorithmException nsae) {
0N/A throw new KeyStoreException("Key protection algorithm not found");
0N/A } finally {
0N/A keyProtector = null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Assigns the given key (that has already been protected) to the given
0N/A * alias.
0N/A *
0N/A * <p>If the protected key is of type
0N/A * <code>java.security.PrivateKey</code>, it must be accompanied by a
0N/A * certificate chain certifying the corresponding public key. If the
0N/A * underlying keystore implementation is of type <code>jks</code>,
0N/A * <code>key</code> must be encoded as an
0N/A * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
0N/A *
0N/A * <p>If the given alias already exists, the keystore information
0N/A * associated with it is overridden by the given key (and possibly
0N/A * certificate chain).
0N/A *
0N/A * @param alias the alias name
0N/A * @param key the key (in protected format) to be associated with the alias
0N/A * @param chain the certificate chain for the corresponding public
0N/A * key (only useful if the protected key is of type
0N/A * <code>java.security.PrivateKey</code>).
0N/A *
0N/A * @exception KeyStoreException if this operation fails.
0N/A */
0N/A public void engineSetKeyEntry(String alias, byte[] key,
0N/A Certificate[] chain)
0N/A throws KeyStoreException
0N/A {
0N/A synchronized(entries) {
0N/A // key must be encoded as EncryptedPrivateKeyInfo as defined in
0N/A // PKCS#8
0N/A try {
0N/A new EncryptedPrivateKeyInfo(key);
0N/A } catch (IOException ioe) {
0N/A throw new KeyStoreException("key is not encoded as "
0N/A + "EncryptedPrivateKeyInfo");
0N/A }
0N/A
0N/A KeyEntry entry = new KeyEntry();
0N/A entry.date = new Date();
0N/A
0N/A entry.protectedPrivKey = key.clone();
0N/A if ((chain != null) &&
0N/A (chain.length != 0)) {
0N/A entry.chain = chain.clone();
0N/A } else {
0N/A entry.chain = null;
0N/A }
0N/A
0N/A entries.put(convertAlias(alias), entry);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Assigns the given certificate to the given alias.
0N/A *
0N/A * <p>If the given alias already exists in this keystore and identifies a
0N/A * <i>trusted certificate entry</i>, the certificate associated with it is
0N/A * overridden by the given certificate.
0N/A *
0N/A * @param alias the alias name
0N/A * @param cert the certificate
0N/A *
0N/A * @exception KeyStoreException if the given alias already exists and does
0N/A * not identify a <i>trusted certificate entry</i>, or this operation
0N/A * fails for some other reason.
0N/A */
0N/A public void engineSetCertificateEntry(String alias, Certificate cert)
0N/A throws KeyStoreException
0N/A {
0N/A synchronized(entries) {
0N/A
0N/A Object entry = entries.get(convertAlias(alias));
0N/A if ((entry != null) && (entry instanceof KeyEntry)) {
0N/A throw new KeyStoreException
0N/A ("Cannot overwrite own certificate");
0N/A }
0N/A
0N/A TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
0N/A trustedCertEntry.cert = cert;
0N/A trustedCertEntry.date = new Date();
0N/A entries.put(convertAlias(alias), trustedCertEntry);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Deletes the entry identified by the given alias from this keystore.
0N/A *
0N/A * @param alias the alias name
0N/A *
0N/A * @exception KeyStoreException if the entry cannot be removed.
0N/A */
0N/A public void engineDeleteEntry(String alias)
0N/A throws KeyStoreException
0N/A {
0N/A synchronized(entries) {
0N/A entries.remove(convertAlias(alias));
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Lists all the alias names of this keystore.
0N/A *
0N/A * @return enumeration of the alias names
0N/A */
0N/A public Enumeration<String> engineAliases() {
0N/A return entries.keys();
0N/A }
0N/A
0N/A /**
0N/A * Checks if the given alias exists in this keystore.
0N/A *
0N/A * @param alias the alias name
0N/A *
0N/A * @return true if the alias exists, false otherwise
0N/A */
0N/A public boolean engineContainsAlias(String alias) {
0N/A return entries.containsKey(convertAlias(alias));
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the number of entries in this keystore.
0N/A *
0N/A * @return the number of entries in this keystore
0N/A */
0N/A public int engineSize() {
0N/A return entries.size();
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the entry identified by the given alias is a
0N/A * <i>key entry</i>, and false otherwise.
0N/A *
0N/A * @return true if the entry identified by the given alias is a
0N/A * <i>key entry</i>, false otherwise.
0N/A */
0N/A public boolean engineIsKeyEntry(String alias) {
0N/A Object entry = entries.get(convertAlias(alias));
0N/A if ((entry != null) && (entry instanceof KeyEntry)) {
0N/A return true;
0N/A } else {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the entry identified by the given alias is a
0N/A * <i>trusted certificate entry</i>, and false otherwise.
0N/A *
0N/A * @return true if the entry identified by the given alias is a
0N/A * <i>trusted certificate entry</i>, false otherwise.
0N/A */
0N/A public boolean engineIsCertificateEntry(String alias) {
0N/A Object entry = entries.get(convertAlias(alias));
0N/A if ((entry != null) && (entry instanceof TrustedCertEntry)) {
0N/A return true;
0N/A } else {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the (alias) name of the first keystore entry whose certificate
0N/A * matches the given certificate.
0N/A *
0N/A * <p>This method attempts to match the given certificate with each
0N/A * keystore entry. If the entry being considered
0N/A * is a <i>trusted certificate entry</i>, the given certificate is
0N/A * compared to that entry's certificate. If the entry being considered is
0N/A * a <i>key entry</i>, the given certificate is compared to the first
0N/A * element of that entry's certificate chain (if a chain exists).
0N/A *
0N/A * @param cert the certificate to match with.
0N/A *
0N/A * @return the (alias) name of the first entry with matching certificate,
0N/A * or null if no such entry exists in this keystore.
0N/A */
0N/A public String engineGetCertificateAlias(Certificate cert) {
0N/A Certificate certElem;
0N/A
0N/A for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
0N/A String alias = e.nextElement();
0N/A Object entry = entries.get(alias);
0N/A if (entry instanceof TrustedCertEntry) {
0N/A certElem = ((TrustedCertEntry)entry).cert;
0N/A } else if (((KeyEntry)entry).chain != null) {
0N/A certElem = ((KeyEntry)entry).chain[0];
0N/A } else {
0N/A continue;
0N/A }
0N/A if (certElem.equals(cert)) {
0N/A return alias;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Stores this keystore to the given output stream, and protects its
0N/A * integrity with the given password.
0N/A *
0N/A * @param stream the output stream to which this keystore is written.
0N/A * @param password the password to generate the keystore integrity check
0N/A *
0N/A * @exception IOException if there was an I/O problem with data
0N/A * @exception NoSuchAlgorithmException if the appropriate data integrity
0N/A * algorithm could not be found
0N/A * @exception CertificateException if any of the certificates included in
0N/A * the keystore data could not be stored
0N/A */
0N/A public void engineStore(OutputStream stream, char[] password)
0N/A throws IOException, NoSuchAlgorithmException, CertificateException
0N/A {
0N/A synchronized(entries) {
0N/A /*
0N/A * KEYSTORE FORMAT:
0N/A *
0N/A * Magic number (big-endian integer),
0N/A * Version of this file format (big-endian integer),
0N/A *
0N/A * Count (big-endian integer),
0N/A * followed by "count" instances of either:
0N/A *
0N/A * {
0N/A * tag=1 (big-endian integer),
0N/A * alias (UTF string)
0N/A * timestamp
0N/A * encrypted private-key info according to PKCS #8
0N/A * (integer length followed by encoding)
0N/A * cert chain (integer count, then certs; for each cert,
0N/A * integer length followed by encoding)
0N/A * }
0N/A *
0N/A * or:
0N/A *
0N/A * {
0N/A * tag=2 (big-endian integer)
0N/A * alias (UTF string)
0N/A * timestamp
0N/A * cert (integer length followed by encoding)
0N/A * }
0N/A *
0N/A * ended by a keyed SHA1 hash (bytes only) of
0N/A * { password + whitener + preceding body }
0N/A */
0N/A
0N/A // password is mandatory when storing
0N/A if (password == null) {
0N/A throw new IllegalArgumentException("password can't be null");
0N/A }
0N/A
0N/A byte[] encoded; // the certificate encoding
0N/A
0N/A MessageDigest md = getPreKeyedHash(password);
0N/A DataOutputStream dos
0N/A = new DataOutputStream(new DigestOutputStream(stream, md));
0N/A
0N/A dos.writeInt(MAGIC);
0N/A // always write the latest version
0N/A dos.writeInt(VERSION_2);
0N/A
0N/A dos.writeInt(entries.size());
0N/A
0N/A for (Enumeration<String> e = entries.keys(); e.hasMoreElements();) {
0N/A
0N/A String alias = e.nextElement();
0N/A Object entry = entries.get(alias);
0N/A
0N/A if (entry instanceof KeyEntry) {
0N/A
0N/A // Store this entry as a KeyEntry
0N/A dos.writeInt(1);
0N/A
0N/A // Write the alias
0N/A dos.writeUTF(alias);
0N/A
0N/A // Write the (entry creation) date
0N/A dos.writeLong(((KeyEntry)entry).date.getTime());
0N/A
0N/A // Write the protected private key
0N/A dos.writeInt(((KeyEntry)entry).protectedPrivKey.length);
0N/A dos.write(((KeyEntry)entry).protectedPrivKey);
0N/A
0N/A // Write the certificate chain
0N/A int chainLen;
0N/A if (((KeyEntry)entry).chain == null) {
0N/A chainLen = 0;
0N/A } else {
0N/A chainLen = ((KeyEntry)entry).chain.length;
0N/A }
0N/A dos.writeInt(chainLen);
0N/A for (int i = 0; i < chainLen; i++) {
0N/A encoded = ((KeyEntry)entry).chain[i].getEncoded();
0N/A dos.writeUTF(((KeyEntry)entry).chain[i].getType());
0N/A dos.writeInt(encoded.length);
0N/A dos.write(encoded);
0N/A }
0N/A } else {
0N/A
0N/A // Store this entry as a certificate
0N/A dos.writeInt(2);
0N/A
0N/A // Write the alias
0N/A dos.writeUTF(alias);
0N/A
0N/A // Write the (entry creation) date
0N/A dos.writeLong(((TrustedCertEntry)entry).date.getTime());
0N/A
0N/A // Write the trusted certificate
0N/A encoded = ((TrustedCertEntry)entry).cert.getEncoded();
0N/A dos.writeUTF(((TrustedCertEntry)entry).cert.getType());
0N/A dos.writeInt(encoded.length);
0N/A dos.write(encoded);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Write the keyed hash which is used to detect tampering with
0N/A * the keystore (such as deleting or modifying key or
0N/A * certificate entries).
0N/A */
0N/A byte digest[] = md.digest();
0N/A
0N/A dos.write(digest);
0N/A dos.flush();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Loads the keystore from the given input stream.
0N/A *
0N/A * <p>If a password is given, it is used to check the integrity of the
0N/A * keystore data. Otherwise, the integrity of the keystore is not checked.
0N/A *
0N/A * @param stream the input stream from which the keystore is loaded
0N/A * @param password the (optional) password used to check the integrity of
0N/A * the keystore.
0N/A *
0N/A * @exception IOException if there is an I/O or format problem with the
0N/A * keystore data
0N/A * @exception NoSuchAlgorithmException if the algorithm used to check
0N/A * the integrity of the keystore cannot be found
0N/A * @exception CertificateException if any of the certificates in the
0N/A * keystore could not be loaded
0N/A */
0N/A public void engineLoad(InputStream stream, char[] password)
0N/A throws IOException, NoSuchAlgorithmException, CertificateException
0N/A {
0N/A synchronized(entries) {
0N/A DataInputStream dis;
0N/A MessageDigest md = null;
0N/A CertificateFactory cf = null;
0N/A Hashtable<String, CertificateFactory> cfs = null;
0N/A ByteArrayInputStream bais = null;
0N/A byte[] encoded = null;
0N/A
0N/A if (stream == null)
0N/A return;
0N/A
0N/A if (password != null) {
0N/A md = getPreKeyedHash(password);
0N/A dis = new DataInputStream(new DigestInputStream(stream, md));
0N/A } else {
0N/A dis = new DataInputStream(stream);
0N/A }
0N/A
0N/A // Body format: see store method
0N/A
0N/A int xMagic = dis.readInt();
0N/A int xVersion = dis.readInt();
0N/A
0N/A if (xMagic!=MAGIC ||
0N/A (xVersion!=VERSION_1 && xVersion!=VERSION_2)) {
0N/A throw new IOException("Invalid keystore format");
0N/A }
0N/A
0N/A if (xVersion == VERSION_1) {
0N/A cf = CertificateFactory.getInstance("X509");
0N/A } else {
0N/A // version 2
0N/A cfs = new Hashtable<String, CertificateFactory>(3);
0N/A }
0N/A
0N/A entries.clear();
0N/A int count = dis.readInt();
0N/A
0N/A for (int i = 0; i < count; i++) {
0N/A int tag;
0N/A String alias;
0N/A
0N/A tag = dis.readInt();
0N/A
0N/A if (tag == 1) { // private key entry
0N/A
0N/A KeyEntry entry = new KeyEntry();
0N/A
0N/A // Read the alias
0N/A alias = dis.readUTF();
0N/A
0N/A // Read the (entry creation) date
0N/A entry.date = new Date(dis.readLong());
0N/A
0N/A // Read the private key
2201N/A entry.protectedPrivKey =
2201N/A IOUtils.readFully(dis, dis.readInt(), true);
0N/A
0N/A // Read the certificate chain
0N/A int numOfCerts = dis.readInt();
2201N/A if (numOfCerts > 0) {
2201N/A List<Certificate> certs = new ArrayList<>(
2201N/A numOfCerts > 10 ? 10 : numOfCerts);
2201N/A for (int j = 0; j < numOfCerts; j++) {
2201N/A if (xVersion == 2) {
2201N/A // read the certificate type, and instantiate a
2201N/A // certificate factory of that type (reuse
2201N/A // existing factory if possible)
2201N/A String certType = dis.readUTF();
2201N/A if (cfs.containsKey(certType)) {
2201N/A // reuse certificate factory
2201N/A cf = cfs.get(certType);
2201N/A } else {
2201N/A // create new certificate factory
2201N/A cf = CertificateFactory.getInstance(certType);
2201N/A // store the certificate factory so we can
2201N/A // reuse it later
2201N/A cfs.put(certType, cf);
2201N/A }
2201N/A }
2201N/A // instantiate the certificate
2201N/A encoded = IOUtils.readFully(dis, dis.readInt(), true);
2201N/A bais = new ByteArrayInputStream(encoded);
2201N/A certs.add(cf.generateCertificate(bais));
2201N/A bais.close();
0N/A }
2201N/A // We can be sure now that numOfCerts of certs are read
2201N/A entry.chain = certs.toArray(new Certificate[numOfCerts]);
0N/A }
0N/A
0N/A // Add the entry to the list
0N/A entries.put(alias, entry);
0N/A
0N/A } else if (tag == 2) { // trusted certificate entry
0N/A
0N/A TrustedCertEntry entry = new TrustedCertEntry();
0N/A
0N/A // Read the alias
0N/A alias = dis.readUTF();
0N/A
0N/A // Read the (entry creation) date
0N/A entry.date = new Date(dis.readLong());
0N/A
0N/A // Read the trusted certificate
0N/A if (xVersion == 2) {
0N/A // read the certificate type, and instantiate a
0N/A // certificate factory of that type (reuse
0N/A // existing factory if possible)
0N/A String certType = dis.readUTF();
0N/A if (cfs.containsKey(certType)) {
0N/A // reuse certificate factory
0N/A cf = cfs.get(certType);
0N/A } else {
0N/A // create new certificate factory
0N/A cf = CertificateFactory.getInstance(certType);
0N/A // store the certificate factory so we can
0N/A // reuse it later
0N/A cfs.put(certType, cf);
0N/A }
0N/A }
2201N/A encoded = IOUtils.readFully(dis, dis.readInt(), true);
0N/A bais = new ByteArrayInputStream(encoded);
0N/A entry.cert = cf.generateCertificate(bais);
0N/A bais.close();
0N/A
0N/A // Add the entry to the list
0N/A entries.put(alias, entry);
0N/A
0N/A } else {
0N/A throw new IOException("Unrecognized keystore entry");
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * If a password has been provided, we check the keyed digest
0N/A * at the end. If this check fails, the store has been tampered
0N/A * with
0N/A */
0N/A if (password != null) {
0N/A byte computed[], actual[];
0N/A computed = md.digest();
0N/A actual = new byte[computed.length];
0N/A dis.readFully(actual);
0N/A for (int i = 0; i < computed.length; i++) {
0N/A if (computed[i] != actual[i]) {
0N/A Throwable t = new UnrecoverableKeyException
0N/A ("Password verification failed");
0N/A throw (IOException)new IOException
0N/A ("Keystore was tampered with, or "
0N/A + "password was incorrect").initCause(t);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * To guard against tampering with the keystore, we append a keyed
0N/A * hash with a bit of whitener.
0N/A */
0N/A private MessageDigest getPreKeyedHash(char[] password)
0N/A throws NoSuchAlgorithmException, UnsupportedEncodingException
0N/A {
0N/A int i, j;
0N/A
0N/A MessageDigest md = MessageDigest.getInstance("SHA");
0N/A byte[] passwdBytes = new byte[password.length * 2];
0N/A for (i=0, j=0; i<password.length; i++) {
0N/A passwdBytes[j++] = (byte)(password[i] >> 8);
0N/A passwdBytes[j++] = (byte)password[i];
0N/A }
0N/A md.update(passwdBytes);
0N/A for (i=0; i<passwdBytes.length; i++)
0N/A passwdBytes[i] = 0;
0N/A md.update("Mighty Aphrodite".getBytes("UTF8"));
0N/A return md;
0N/A }
0N/A}