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

Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]