File Source: CertificateManager.java

         /* 
    P/P   *  Method: com.dmdirc.CertificateManager__static_init
          */
     1  /*
     2   * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy
     5   * of this software and associated documentation files (the "Software"), to deal
     6   * in the Software without restriction, including without limitation the rights
     7   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   * copies of the Software, and to permit persons to whom the Software is
     9   * furnished to do so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in
    12   * all copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  package com.dmdirc;
    24  
    25  import com.dmdirc.config.ConfigManager;
    26  import com.dmdirc.config.IdentityManager;
    27  import com.dmdirc.logger.ErrorLevel;
    28  import com.dmdirc.logger.Logger;
    29  import com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction;
    30  import com.dmdirc.ui.core.dialogs.sslcertificate.SSLCertificateDialogModel;
    31  
    32  import java.io.File;
    33  import java.io.FileInputStream;
    34  import java.io.IOException;
    35  import java.security.InvalidAlgorithmParameterException;
    36  import java.security.KeyStore;
    37  import java.security.KeyStoreException;
    38  import java.security.NoSuchAlgorithmException;
    39  import java.security.UnrecoverableKeyException;
    40  import java.security.cert.CertificateException;
    41  import java.security.cert.CertificateParsingException;
    42  import java.security.cert.PKIXParameters;
    43  import java.security.cert.TrustAnchor;
    44  import java.security.cert.X509Certificate;
    45  import java.util.ArrayList;
    46  import java.util.Arrays;
    47  import java.util.HashMap;
    48  import java.util.HashSet;
    49  import java.util.List;
    50  import java.util.Map;
    51  import java.util.Set;
    52  import java.util.concurrent.Semaphore;
    53  
    54  import javax.naming.InvalidNameException;
    55  import javax.naming.ldap.LdapName;
    56  import javax.naming.ldap.Rdn;
    57  import javax.net.ssl.KeyManager;
    58  import javax.net.ssl.KeyManagerFactory;
    59  import javax.net.ssl.X509TrustManager;
    60  import net.miginfocom.Base64;
    61  
    62  /**
    63   * Manages storage and validation of certificates used when connecting to
    64   * SSL servers.
    65   *
    66   * @since 0.6.3m1
    67   * @author chris
    68   */
    69  public class CertificateManager implements X509TrustManager {
    70  
    71      /** The password for the global java cacert file. */
    72      private final String cacertpass;
    73  
    74      /** The server name the user is trying to connect to. */
    75      private final String serverName;
    76  
    77      /** The configuration manager to use for settings. */
    78      private final ConfigManager config;
    79  
    80      /** The set of CAs from the global cacert file. */
    81      private Set<X509Certificate> globalTrustedCAs = new HashSet<X509Certificate>();
    82  
    83      /** Whether or not to check specified parts of the certificate. */
    84      private boolean checkDate, checkIssuer, checkHost;
    85  
    86      /** Used to synchronise the manager with the certificate dialog. */
    87      private final Semaphore actionSem = new Semaphore(0);
    88      
    89      /** The action to perform. */
    90      private CertificateAction action;
    91  
    92      /**
    93       * Creates a new certificate manager for a client connecting to the
    94       * specified server.
    95       *
    96       * @param serverName The name the user used to connect to the server
    97       * @param config The configuration manager to use
    98       */
             /* 
    P/P       *  Method: void com.dmdirc.CertificateManager(String, ConfigManager)
              * 
              *  Preconditions:
              *    config != null
              * 
              *  Presumptions:
              *    com.dmdirc.config.ConfigManager:getOption(...)@102 != null
              * 
              *  Postconditions:
              *    this.actionSem == &amp;new Semaphore(CertificateManager#2)
              *    this.cacertpass != null
              *    init'ed(this.checkDate)
              *    init'ed(this.checkHost)
              *    init'ed(this.checkIssuer)
              *    this.config == config
              *    this.config != null
              *    this.globalTrustedCAs == &amp;new HashSet(CertificateManager#1)
              *    this.serverName == serverName
              *    init'ed(this.serverName)
              *    ...
              */
    99      public CertificateManager(final String serverName, final ConfigManager config) {
   100          this.serverName = serverName;
   101          this.config = config;
   102          this.cacertpass = config.getOption("ssl", "cacertpass");
   103          this.checkDate = config.getOptionBool("ssl", "checkdate");
   104          this.checkIssuer = config.getOptionBool("ssl", "checkissuer");
   105          this.checkHost = config.getOptionBool("ssl", "checkhost");
   106  
   107          loadTrustedCAs();
   108      }
   109  
   110      /**
   111       * Loads the trusted CA certificates from the Java cacerts store.
   112       */
   113      protected void loadTrustedCAs() {
                 /* 
    P/P           *  Method: void loadTrustedCAs()
                  * 
                  *  Preconditions:
                  *    (soft) this.cacertpass != null
                  *    (soft) this.globalTrustedCAs != null
                  * 
                  *  Presumptions:
                  *    init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
                  *    init'ed(java.io.File.separatorChar)
                  *    java.security.KeyStore:getInstance(...)@120 != null
                  *    java.security.cert.PKIXParameters:getTrustAnchors(...)@124 != null
                  *    java.util.Iterator:next(...)@124 != null
                  */
   114          FileInputStream is = null;
   115  
   116          try {
   117              final String filename = System.getProperty("java.home")
   118                  + "/lib/security/cacerts".replace('/', File.separatorChar);
   119              is = new FileInputStream(filename);
   120              final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
   121              keystore.load(is, cacertpass.toCharArray());
   122  
   123              final PKIXParameters params = new PKIXParameters(keystore);
   124              for (TrustAnchor anchor : params.getTrustAnchors()) {
   125                  globalTrustedCAs.add(anchor.getTrustedCert());
   126              }
   127          } catch (CertificateException ex) {
   128              Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
   129          } catch (IOException ex) {
   130              Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
   131          } catch (InvalidAlgorithmParameterException ex) {
   132              Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
   133          } catch (KeyStoreException ex) {
   134              Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
   135          } catch (NoSuchAlgorithmException ex) {
   136              Logger.appError(ErrorLevel.MEDIUM, "Unable to load trusted certificates", ex);
   137          } finally {
   138              if (is != null) {
   139                  try {
   140                      is.close();
   141                  } catch (IOException ex) {
   142                      // ...
   143                  }
   144              }
   145          }
   146      }
   147  
   148      /**
   149       * Retrieves a KeyManager[] for the client certicate specified in the
   150       * configuration, if there is one.
   151       *
   152       * @return A KeyManager to use for the SSL connection
   153       */
   154      public KeyManager[] getKeyManager() {
                 /* 
    P/P           *  Method: KeyManager[] getKeyManager()
                  * 
                  *  Preconditions:
                  *    this.config != null
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.ConfigManager:getOption(...)@161 != null
                  *    init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
                  *    java.security.KeyStore:getInstance(...)@167 != null
                  *    javax.net.ssl.KeyManagerFactory:getInstance(...)@170 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@155: {0}, {1}
                  *    com.dmdirc.config.ConfigManager:hasOptionString(...)@160: {0}, {1}
                  */
   155          if (config.hasOptionString("ssl", "clientcert.file")) {
   156              FileInputStream fis = null;
   157              try {
   158                  final char[] pass;
   159  
   160                  if (config.hasOptionString("ssl", "clientcert.pass")) {
   161                      pass = config.getOption("ssl", "clientcert.pass").toCharArray();
   162                  } else {
   163                      pass = null;
   164                  }
   165  
   166                  fis = new FileInputStream(config.getOption("ssl", "clientcert.file"));
   167                  final KeyStore ks = KeyStore.getInstance("pkcs12");
   168                  ks.load(fis, pass);
   169  
   170                  final KeyManagerFactory kmf = KeyManagerFactory.getInstance(
   171                          KeyManagerFactory.getDefaultAlgorithm());
   172                  kmf.init(ks, pass);
   173  
   174                  return kmf.getKeyManagers();
   175              } catch (KeyStoreException ex) {
   176                  Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
   177              } catch (IOException ex) {
   178                  Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
   179              } catch (CertificateException ex) {
   180                  Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
   181              } catch (NoSuchAlgorithmException ex) {
   182                  Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
   183              } catch (UnrecoverableKeyException ex) {
   184                  Logger.appError(ErrorLevel.MEDIUM, "Unable to get key manager", ex);
   185              } finally {
   186                  if (fis != null) {
   187                      try {
   188                          fis.close();
   189                      } catch (IOException ex) {
   190                          // ...
   191                      }
   192                  }
   193              }
   194          }
   195  
   196          return null;
   197      }
   198  
   199      /** {@inheritDoc} */
   200      @Override
   201      public void checkClientTrusted(final X509Certificate[] chain, final String authType)
   202              throws CertificateException {
                 /* 
    P/P           *  Method: void checkClientTrusted(X509Certificate[], String)
                  *    checkClientTrusted fails for all possible inputs
                  */
   203          throw new CertificateException("Not supported.");
   204      }
   205  
   206      /**
   207       * Determines if the specified certificate is trusted by the user.
   208       *
   209       * @param certificate The certificate to be checked
   210       * @return True if the certificate matches one in the trusted certificate
   211       * store, or if the certificate's details are marked as trusted in the
   212       * DMDirc configuration file.
   213       */
   214      public boolean isTrusted(final X509Certificate certificate) {
   215          try {
                     /* 
    P/P               *  Method: bool isTrusted(X509Certificate)
                      * 
                      *  Preconditions:
                      *    (soft) certificate != null
                      *    (soft) this.config != null
                      *    (soft) this.globalTrustedCAs != null
                      * 
                      *  Presumptions:
                      *    com.dmdirc.config.ConfigManager:getOptionList(...)@218 != null
                      *    java.security.Principal:getName(...)@223 != null
                      *    java.security.cert.X509Certificate:getIssuerDN(...)@223 != null
                      *    java.util.Iterator:next(...)@222 != null
                      * 
                      *  Postconditions:
                      *    init'ed(return_value)
                      * 
                      *  Test Vectors:
                      *    com.dmdirc.config.ConfigManager:hasOptionString(...)@218: {0}, {1}
                      *    java.lang.String:equals(...)@223: {0}, {1}
                      *    java.util.Arrays:equals(...)@223: {0}, {1}
                      *    java.util.Iterator:hasNext(...)@222: {0}, {1}
                      *    java.util.List:contains(...)@218: {0}, {1}
                      */
   216              final String sig = Base64.encodeToString(certificate.getSignature(), false);
   217  
   218              if (config.hasOptionString("ssl", "trusted") && config.getOptionList("ssl",
   219                      "trusted").contains(sig)) {
   220                  return true;
   221              } else {
   222                  for (X509Certificate trustedCert : globalTrustedCAs) {
   223                      if (Arrays.equals(certificate.getSignature(), trustedCert.getSignature())
   224                              && certificate.getIssuerDN().getName()
   225                              .equals(trustedCert.getIssuerDN().getName())) {
   226                          certificate.verify(trustedCert.getPublicKey());
   227                          return true;
   228                      }
   229                  }
   230              }
   231          } catch (Exception ex) {
   232             return false;
   233          }
   234  
   235          return false;
   236      }
   237  
   238      public boolean isValidHost(final X509Certificate certificate) {
                 /* 
    P/P           *  Method: bool isValidHost(X509Certificate)
                  * 
                  *  Preconditions:
                  *    (soft) certificate != null
                  * 
                  *  Presumptions:
                  *    java.security.cert.X509Certificate:getSubjectAlternativeNames(...)@246 != null
                  *    java.util.Iterator:next(...)@246 != null
                  *    java.util.List:get(...)@247 != null
                  *    java.util.List:get(...)@250 != null
                  *    java.util.Map:get(...)@240 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.lang.Integer:intValue(...)@247: {-231..1, 3..6, 8..232-1}, {7}
                  *    java.lang.Object:equals(...)@250: {0}, {1}
                  *    java.lang.String:equals(...)@240: {0}, {1}
                  *    java.security.cert.X509Certificate:getSubjectAlternativeNames(...)@245: Addr_Set{null}, Inverse{null}
                  *    java.util.Iterator:hasNext(...)@246: {0}, {1}
                  *    java.util.Map:containsKey(...)@240: {0}, {1}
                  */
   239          final Map<String, String> fields = getDNFieldsFromCert(certificate);
   240          if (fields.containsKey("CN") && fields.get("CN").equals(serverName)) {
   241              return true;
   242          }
   243  
   244          try {
   245              if (certificate.getSubjectAlternativeNames() != null) {
   246                  for (List<?> entry : certificate.getSubjectAlternativeNames()) {
   247                      final int type = ((Integer) entry.get(0)).intValue();
   248  
   249                      // DNS or IP
   250                      if ((type == 2 || type == 7) && entry.get(1).equals(serverName)) {
   251                          return true;
   252                      }
   253                  }
   254              }
   255          } catch (CertificateParsingException ex) {
   256              return false;
   257          }
   258  
   259          return false;
   260      }
   261  
   262      /** {@inheritDoc} */
   263      @Override
   264      public void checkServerTrusted(final X509Certificate[] chain, final String authType)
   265              throws CertificateException {
                 /* 
    P/P           *  Method: void checkServerTrusted(X509Certificate[], String)
                  * 
                  *  Preconditions:
                  *    chain != null
                  *    init'ed(this.checkHost)
                  *    (soft) chain.length in {1..232-1}
                  *    (soft) chain[0] != null
                  *    (soft) chain[...] != null
                  *    (soft) com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1)[...] != 1
                  *    (soft) com/dmdirc/Main.controller != null
                  *    (soft) this.action != null
                  *    (soft) this.actionSem != null
                  *    (soft) init'ed(this.checkDate)
                  *    ...
                  * 
                  *  Presumptions:
                  *    com.dmdirc.config.IdentityManager:getConfigIdentity(...)@319 != null
                  *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 >= 0
                  *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length >= 1
                  *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 < com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length
                  * 
                  *  Test Vectors:
                  *    com.dmdirc.CertificateManager$1__static_init.new int[](CertificateManager$1__static_init#1)[...]: {2}, {-231..0, 3..232-1}
                  *    this.checkDate: {0}, {1}
                  *    this.checkHost: {0}, {1}
                  *    this.checkIssuer: {0}, {1}
                  *    java.util.List:isEmpty(...)@302: {1}, {0}
                  */
   266          final List<CertificateException> problems = new ArrayList<CertificateException>();
   267          boolean verified = false;
   268  
   269          if (checkHost) {
   270              // Check that the cert is issued to the correct host
   271              verified = isValidHost(chain[0]);
   272  
   273              if (!verified) {
   274                  problems.add(new CertificateDoesntMatchHostException(
   275                          "Certificate was not issued to " + serverName));
   276              }
   277  
   278              verified = false;
   279          }
   280  
   281          for (X509Certificate cert : chain) {
   282              if (checkDate) {
   283                  // Check that the certificate is in-date
   284                  try {
   285                      cert.checkValidity();
   286                  } catch (CertificateException ex) {
   287                      problems.add(ex);
   288                  }
   289              }
   290  
   291              if (checkIssuer) {
   292                  // Check that we trust an issuer
   293  
   294                  verified |= isTrusted(cert);
   295              }
   296          }
   297  
   298          if (!verified && checkIssuer) {
   299              problems.add(new CertificateNotTrustedException("Issuer is not trusted"));
   300          }
   301  
   302          if (!problems.isEmpty()) {
   303              final SSLCertificateDialogModel test = new SSLCertificateDialogModel(chain, problems, this);
   304              Main.getUI().showSSLCertificateDialog(test);
   305  
   306              try {
   307                  actionSem.acquire();
   308              } catch (InterruptedException ie) {
   309                throw new CertificateException("Thread aborted, ");
   310              }
   311              
                     /* 
    P/P               *  Method: com.dmdirc.CertificateManager$1__static_init
                      * 
                      *  Presumptions:
                      *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.DISCONNECT != null
                      *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.IGNORE_PERMANENTY != null
                      *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction.IGNORE_TEMPORARILY != null
                      *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 >= 0
                      *    com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:ordinal(...)@312 < com.dmdirc.ui.core.dialogs.sslcertificate.CertificateAction:values(...).length@312
                      *    ...
                      * 
                      *  Postconditions:
                      *    new int[](CertificateManager$1__static_init#1) num objects == 1
                      */
   312              switch (action) {
   313                  case DISCONNECT:
   314                      throw new CertificateException("Not trusted");
   315                  case IGNORE_PERMANENTY:
   316                      final List<String> list = new ArrayList<String>(config
   317                              .getOptionList("ssl", "trusted"));
   318                      list.add(Base64.encodeToString(chain[0].getSignature(), false));
   319                      IdentityManager.getConfigIdentity().setOption("ssl",
   320                              "trusted", list);
   321                      break;
   322                  case IGNORE_TEMPORARILY:
   323                      // Do nothing, continue connecting
   324                      break;
   325              }
   326          }
   327      }
   328  
   329      /**
   330       * Sets the action to perform for the request that's in progress.
   331       *
   332       * @param action The action that's been selected
   333       */
   334      public void setAction(final CertificateAction action) {
                 /* 
    P/P           *  Method: void setAction(CertificateAction)
                  * 
                  *  Preconditions:
                  *    this.actionSem != null
                  * 
                  *  Postconditions:
                  *    this.action == action
                  *    init'ed(this.action)
                  */
   335          this.action = action;
   336          
   337          actionSem.release();
   338      }
   339  
   340      /**
   341       * Retrieves the name of the server to which the user is trying to connect.
   342       *
   343       * @return The name of the server that the user is trying to connect to
   344       */
   345      public String getServerName() {
                 /* 
    P/P           *  Method: String getServerName()
                  * 
                  *  Postconditions:
                  *    return_value == this.serverName
                  *    init'ed(return_value)
                  */
   346          return serverName;
   347      }
   348  
   349      /**
   350       * Reads the fields from the subject's designated name in the specified
   351       * certificate.
   352       *
   353       * @param cert The certificate to read
   354       * @return A map of the fields in the certificate's subject's designated
   355       * name
   356       */
   357      public static Map<String, String> getDNFieldsFromCert(final X509Certificate cert) {
                 /* 
    P/P           *  Method: Map getDNFieldsFromCert(X509Certificate)
                  * 
                  *  Preconditions:
                  *    (soft) cert != null
                  * 
                  *  Presumptions:
                  *    java.security.cert.X509Certificate:getSubjectX500Principal(...)@361 != null
                  *    java.util.Iterator:next(...)@362 != null
                  *    javax.naming.ldap.LdapName:getRdns(...)@362 != null
                  *    javax.naming.ldap.Rdn:getValue(...)@363 != null
                  * 
                  *  Postconditions:
                  *    return_value == &amp;new HashMap(getDNFieldsFromCert#1)
                  *    new HashMap(getDNFieldsFromCert#1) num objects == 1
                  */
   358          final Map<String, String> res = new HashMap<String, String>();
   359  
   360          try {
   361              final LdapName name = new LdapName(cert.getSubjectX500Principal().getName());
   362              for (Rdn rdn : name.getRdns()) {
   363                  res.put(rdn.getType(), rdn.getValue().toString());
   364              }
   365          } catch (InvalidNameException ex) {
   366              // Don't care
   367          }
   368  
   369          return res;
   370      }
   371  
   372      /** {@inheritDoc} */
   373      @Override
   374      public X509Certificate[] getAcceptedIssuers() {
                 /* 
    P/P           *  Method: X509Certificate[] getAcceptedIssuers()
                  * 
                  *  Preconditions:
                  *    this.globalTrustedCAs != null
                  * 
                  *  Presumptions:
                  *    java.util.Set:size(...)@375 >= 0
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   375          return globalTrustedCAs.toArray(new X509Certificate[globalTrustedCAs.size()]);
   376      }
   377  
   378      /**
   379       * An exception to indicate that the host on a certificate doesn't match
   380       * the host we're trying to connect to.
   381       */
   382      public static class CertificateDoesntMatchHostException extends CertificateException {
   383  
   384          /**
   385           * A version number for this class. It should be changed whenever the
   386           * class structure is changed (or anything else that would prevent
   387           * serialized objects being unserialized with the new class).
   388           */
   389          private static final long serialVersionUID = 1;
   390  
   391          /**
   392           * Creates a new CertificateDoesntMatchHostException
   393           *
   394           * @param msg A description of the problem
   395           */
   396          public CertificateDoesntMatchHostException(String msg) {
                     /* 
    P/P               *  Method: void com.dmdirc.CertificateManager$CertificateDoesntMatchHostException(String)
                      */
   397              super(msg);
   398          }
   399  
   400      }
   401  
   402      /**
   403       * An exception to indicate that we do not trust the issuer of the
   404       * certificate (or the CA).
   405       */
   406      public static class CertificateNotTrustedException extends CertificateException {
   407  
   408          /**
   409           * A version number for this class. It should be changed whenever the
   410           * class structure is changed (or anything else that would prevent
   411           * serialized objects being unserialized with the new class).
   412           */
   413          private static final long serialVersionUID = 1;
   414  
   415          /**
   416           * Creates a new CertificateNotTrustedException
   417           *
   418           * @param msg A description of the problem
   419           */
   420          public CertificateNotTrustedException(String msg) {
                     /* 
    P/P               *  Method: void com.dmdirc.CertificateManager$CertificateNotTrustedException(String)
                      */
   421              super(msg);
   422          }
   423  
   424      }
   425  
   426  }








SofCheck Inspector Build Version : 2.17854
CertificateManager.java 2009-Jun-25 01:54:24
CertificateManager.class 2009-Sep-02 17:04:11
CertificateManager$1.class 2009-Sep-02 17:04:11
CertificateManager$CertificateDoesntMatchHostException.class 2009-Sep-02 17:04:11
CertificateManager$CertificateNotTrustedException.class 2009-Sep-02 17:04:11