usn.net.ssl.util.InstallCert Maven / Gradle / Ivy
Show all versions of install-cert Show documentation
/*
* Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Sun Microsystems nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package usn.net.ssl.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.SocketException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* A program to obtain SSL certificate(s) from a host and save them to a
* keystore and optionally install them in local JSSE storage; the program
* collects SSL/TLS certificates from plain SSL/TLS hosts, and also from hosts
* that operate with STARTTLS extension for LDAP, SMTP, POP3 and IMAP.
*
* Original article: http://blogs.sun.com/andreas/entry/no_more_unable_to_find
* Original source: http://blogs.sun.com/andreas/resource/InstallCert.java
* Author: Andreas Sterbenz, 2006
*
* Currently available at: https://java.net/projects/javamail/pages/InstallCert
* Current Google Code branch as web page: http://code.google.com/p/java-use-examples/source/browse/trunk/src/com/aw/ad/util/InstallCert.java
* Current Google Code branch as Java code: http://java-use-examples.googlecode.com/svn/trunk/src/com/aw/ad/util/InstallCert.java
* Source path in Google Code repository: svn/ trunk/ src/ com/
* aw/ ad/ util/ InstallCert.java
*
* Approach to STARTTLS with JavaMail: Eugen Kuleshov and Dmitry I.
* Platonoff, JavaWorld.com, August 31, 2001
* Java Tip 115:
* Secure JavaMail with JSSE
*
* Merged together by: Sergey Ushakov (usn), 2012–2013
*
* Use without STARTTLS extension for SMTP, POP3 and IMAP protocols:
* java -jar installcert-usn-....jar {@literal [:]
* []}
* Default port is 443.
* Default truststore password is "changeit" as per JSSE convention.
* The program uses a keystore file named "extracerts" in the current directory
* to store the new certificates, and also attempts to add them to the standard
* system keystore jssecacerts
, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager.
*
* Example:
* java -jar installcert-usn-20140115.jar
* ecc.fedora.redhat.com
*
* Use with STARTTLS extension for SMTP, POP3 and IMAP protocols:
* on Windows:
* java -cp installcert-usn-....jar;.../javax.mail.jar
* usn.net.ssl.util.InstallCert {@literal [:]
* []}
* on *ix:
* java -cp installcert-usn-....jar:.../javax.mail.jar
* usn.net.ssl.util.InstallCert {@literal [:]
* []}
* Be sure to provide the real path to your local copy of
* javax.mail.jar
:)
*
* See Oracle Notes for use of SSL with JavaMail.
*/
public class InstallCert {
private static final Logger LOG = LoggerFactory.getLogger(InstallCert.class.getName());
public static final char[] DEFAULT = "changeit".toCharArray();
//this is also the only variable that prevents this class from being thread safe.
// this one is needed here to allow being shared with embedded classes
private static SSLContext context;
public static MessageDigest sha1 = null;
public static MessageDigest md5 = null;
static {
try {
sha1 = MessageDigest.getInstance("SHA1");
} catch (NoSuchAlgorithmException ex) {
LOG.info("SHA1 not available " + ex.getMessage());
}
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ex) {
LOG.info("MD5 not available " + ex.getMessage());
}
}
private static void saveCerts(Set certsToSave, String host) throws Exception {
for (X509Certificate cert : certsToSave) {
String alias = host + " - " + KeyStoreUtilities.getCommonName(cert);
alias = alias.replaceAll("[^a-zA-Z0-9\\.\\-]", "_");
File file = null;
file = new File(alias + ".crt");
int i = 0;
while (file.exists()) {
file = new File(alias + "-" + i + ".crt");
}
FileWriter fw = new FileWriter(file);
fw.write(KeyStoreUtilities.certToString(cert));
fw.close();
LOG.info("Cert saved to: " + file.getAbsolutePath());
}
}
public static SSLContext getContext() {
return context;
}
/**
* Run the program from command line.
*
* @param args command line arguments as: {@literal [:]
* []}
* @throws Exception
*/
public static void main(final String[] args)
throws Exception {
Options opts = new Options();
opts.addOption("host", true, "The host:port of the server to pull a ssl cert chain from. If not specified, 443 will be used.");
opts.addOption("truststore", true, "if specified, this trust store will be used, otherwise JAVA_HOME/cacerts will be used");
opts.addOption("truststoreExtra", true, "if specified, this trust store will also be used");
opts.addOption("password", true, "if specified, your value will be used for the trust store password. if not specified the default jre password will be used");
opts.addOption("passwordExtra", true, "if specified, password for the extra trust store");
opts.addOption("noimport", false, "if specified, no changes will be made to trust stores");
opts.addOption("file", false, "if specified, untrusted certificates will be stored to individial .crt files");
opts.addOption("danger", false, "don't prompt for confirmation, all certificates returned will be auto trusted");
opts.addOption("skipDisco", false, "skip automatic JRE trust store detection");
opts.addOption("connectTimeout", true, "Time in millsecinds for connection attempts. Default is 10 seconds");
opts.addOption("overallTimeout", true, "Time in millsecinds for a connection attempt for specific use cases. Default is 15 seconds");
/*
* useful for when the current JRE trust's something, but the target JRE that needs to be
* updated does not
*/
opts.addOption("exclude", false, "Exclues trustworthiness from all trust stores");
CommandLineParser parser = new DefaultParser();
CommandLine inputs = parser.parse(opts, args);
if (inputs.hasOption("connectTimeout")) {
TimeoutSettings.setConnectionTimeout(Integer.parseInt(inputs.getOptionValue("connectTimeout")));
}
if (inputs.hasOption("overallTimeout")) {
TimeoutSettings.setOverallTimeout(Integer.parseInt(inputs.getOptionValue("overallTimeout")));
}
if (!inputs.hasOption("host")) {
new HelpFormatter().printHelp("java -jar install-cert--jar-with-dependencies.jar", opts);
return;
}
InstallCert ref = new InstallCert();
// handle command line arguments
String host = null;
int port = 0;
char[] password = null;
char[] pwd2 = null;
File storeLocation2 = null;
// handle standard arguments
String[] c = inputs.getOptionValue("host").split(":");
host = c[0];
port = (c.length < 2) ? 443 : Integer.parseInt(c[1]);
if (inputs.hasOption("password")) {
password = inputs.getOptionValue("password").toCharArray();
} else {
password = DEFAULT;
}
if (inputs.hasOption("passwordExtra")) {
pwd2 = inputs.getOptionValue("passwordExtra").toCharArray();
} else {
pwd2 = DEFAULT;
}
//option for no trust store discovery
if (!inputs.hasOption("skipDisco")) {
ref.trustStoresToModify.addAll(KeyStoreUtilities.findTrustStores());
}
if (inputs.hasOption("truststore")) {
ref.addTrustStore(new File(inputs.getOptionValue("truststore")), password);
}
if (inputs.hasOption("truststoreExtra")) {
storeLocation2 = new File(inputs.getOptionValue("truststoreExtra"));
ref.addTrustStore(storeLocation2, pwd2);
}
if (inputs.hasOption("exclude")) {
ref.setExcludeAllTrustStates(true);
}
Set untrustedCerts = ref.getCerts(host, port);
Set certsToSave = new HashSet();
// save the new certificates approved by the user
if (!untrustedCerts.isEmpty()) {
// assign aliases using host name and certificate common name
for (X509Certificate cert : untrustedCerts) {
LOG.info(KeyStoreUtilities.prettyPrintCertificate(cert, "\n"));
if (inputs.hasOption("danger")) {
certsToSave.add(cert);
} else {
System.out.print("Do you want to trust this certifcate (y/n)? > ");
String answer = System.console().readLine();
if ("y".equalsIgnoreCase(answer)) {
certsToSave.add(cert);
}
}
}
}
// save the new certificates approved by the user
if (!certsToSave.isEmpty()) {
if (inputs.hasOption("file")) {
saveCerts(certsToSave, host);
}
if (inputs.hasOption("noimport")) {
LOG.info("Skipping JKS import due to -noimport flag");
} else {
ref.applyChanges(certsToSave, host);
}
} else {
LOG.info("No new certificates found to be added.");
}
} // main
protected static String ask(String prompt)
throws IOException {
System.out.print(prompt);
BufferedReader stdinReader
= new BufferedReader(new InputStreamReader(System.in));
String line = stdinReader.readLine().trim();
return line;
} // ask
private boolean excludeAllTrustStates = false;
private Set trustStoresToModify = new HashSet();
public InstallCert() {
}
public List getTrustStores() {
List files = new ArrayList();
for (KeyStoreWrapper wrapper : trustStoresToModify) {
files.add(wrapper.getKeyStoreLocation());
}
return files;
}
/**
* if true, and provided trust store, including the built in JRE/JDK trust stores will
* be ignored with processing. meaning you'll get a set of all certificates in the trust chain
* for the remote server, whether or not it's trusted
* @return true/false
*/
public boolean isExcludeAllTrustStates() {
return excludeAllTrustStates;
}
public void setExcludeAllTrustStates(boolean excludeAllTrustStates) {
this.excludeAllTrustStates = excludeAllTrustStates;
}
/**
* adds a trust store to use for both connecting to a server and for applying changes
* @param file
* @param password
* @throws Exception
*/
public void addTrustStore(File file, char[] password) throws Exception {
KeyStoreWrapper wrapper = new KeyStoreWrapper();
wrapper.setKeyStoreLocation(file);
wrapper.setKeyStorePassword(password);
wrapper.setStore(KeyStoreUtilities.getKeyStore(file, password));
trustStoresToModify.add(wrapper);
}
public void addAll(Set set) {
this.trustStoresToModify.addAll(set);
}
/**
* returns a potentially empty list of certificates that are not trusted
*
* @param host
* @param port
* @return
*/
public synchronized Set getCerts(String host, int port) throws Exception {
//if (trustStoresToModify.isEmpty()) {
// throw new Exception("must initialize the trust store(s) first");
//}
// obtain an instance of a TLS SSLContext
context = SSLContext.getInstance("TLS");
// obtain a TrustManagerFactory instance
TrustManagerFactory tmf
= TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
try {
tmf.init((KeyStore) null);
} catch (Exception ex) {
LOG.warn("failed to null trust store ", ex);
}
// initialize it with known certificate data
for (KeyStoreWrapper wrapper : trustStoresToModify) {
try {
tmf.init(wrapper.getStore());
} catch (Exception ex) {
LOG.warn("failed to apply trust store " + wrapper.getKeyStoreLocation().getAbsolutePath(), ex);
}
}
// obtain default TrustManager instance
X509TrustManager defaultTrustManager
= (X509TrustManager) tmf.getTrustManagers()[0];
if (excludeAllTrustStates) {
defaultTrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
throw new CertificateException(string);
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
throw new CertificateException(string);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
SavingTrustManager tm
= new SavingTrustManager(defaultTrustManager, trustStoresToModify);
context.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory factory = context.getSocketFactory();
/*
* Set up a socket to do tunneling through the proxy.
* Start it off as a regular socket, then layer SSL
* over the top of it.
*/
String tunnelHost = System.getProperty("https.proxyHost");
String tunnelPortStr = System.getProperty("https.proxyPort");
int tunnelPort = 0;
if ((tunnelPortStr != null) && (!tunnelPortStr.trim().isEmpty())) {
// Integer tunnelPortInteger = Integer.getInteger(tunnelPortStr);
// tunnelPort = (tunnelPortInteger != null) ? tunnelPortInteger.intValue() : 0;
tunnelPort = Integer.parseInt(tunnelPortStr);
}
Socket tunnel = null;
if ((tunnelHost != null) && (!tunnelHost.trim().isEmpty())) {
LOG.info("Opening socket to proxy " + tunnelHost + ":" + tunnelPort + "...");
tunnel = new Socket(tunnelHost, tunnelPort);
doTunnelHandshake(tunnel, host, port);
}
LOG.info("Opening connection to " + host + ":" + port + "...");
LOG.info("... opening connection to " + host + ":" + port
+ " ...");
SSLSocket sslSocket = null;
try {
if (tunnel != null) {
LOG.info("Using proxy configuration. proxy: " + tunnelHost + ":" + tunnelPort);
sslSocket = (SSLSocket) factory.createSocket(tunnel, host, port, true);
} else {
sslSocket = (SSLSocket) factory.createSocket(host, port);
}
sslSocket.setSoTimeout(TimeoutSettings.getConnectionTimeout());
LOG.info("... starting SSL handshake ...");
sslSocket.startHandshake();
LOG.info("No errors, certificate is already trusted.");
} // SMTP/STARTTLS and IMAP/STARTTLS servers seem tending to yield an
// SSLException with
// "Unrecognized SSL message, plaintext connection?" message.
// LDAP/STARTTLS servers seem tending to yield an
// SSLHandshakeException with nested EOFException or a
// SocketException with "Connection reset" message.
// Thus three distinct cases for considering a STARTTLS extension below
catch (SSLHandshakeException e) {
if (e.getCause() != null
&& e.getCause().getClass().getSimpleName().equals("ValidatorException")
&& e.getCause().getCause() != null
&& e.getCause().getCause().getClass().getSimpleName().equals("SunCertPathBuilderException")) {
// this is the standard case: looks like we just got a
// previously unknown certificate, so report it and go
// ahead...
LOG.info(e.getMessage());
} else if (e.getCause() != null
&& e.getCause().getClass().getName().equals("java.io.EOFException")) // "Remote host closed connection during handshake"
{
// close the unsuccessful SSL socket
if (sslSocket != null) {
sslSocket.close();
}
// consider trying STARTTLS extension over ordinary socket
if (!Starttls.consider(host, port, tunnel)) {
// Starttls.consider () is expected to have reported
// everything except the final good-bye...
LOG.info(e.getMessage());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
} else {
LOG.info(e.toString());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
} catch (SSLException e) {
if (e.getMessage().equals("Unrecognized SSL message, plaintext connection?")) {
LOG.info("ERROR on SSL handshake: "
+ e.toString());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
if (sslSocket != null) {
sslSocket.close();
}
// consider trying STARTTLS extension over ordinary socket
if (!Starttls.consider(host, port, tunnel)) {
// Starttls.consider () is expected to have reported
// everything except the final good-bye...
LOG.info(e.getMessage());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
} else {
LOG.info(e.getMessage());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
} catch (SocketException e) {
if (e.getMessage().equals("Connection reset")) {
LOG.info("ERROR on SSL handshake: "
+ e.toString());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
if (sslSocket != null) {
sslSocket.close();
}
// consider trying STARTTLS extension over ordinary socket
if (!Starttls.consider(host, port, tunnel)) {
// Starttls.consider () is expected to have reported
// everything except the final good-bye...
LOG.info(e.getMessage());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
} else {
LOG.info(e.getMessage());
if (LOG.isDebugEnabled()) {
LOG.debug(e.getMessage(), e);
}
}
} finally {
if (sslSocket != null && !sslSocket.isClosed()) {
sslSocket.close();
}
}
// get the full set of new accumulated certificates as an array
X509Certificate[] chain
= tm.newCerts.toArray(new X509Certificate[0]);
// an empty set for certificates to be selected for saving
Set certsToSave = new HashSet();
// display the list of obtained certificates, inspect them and
// interrogate the user whether to save them
if (chain.length > 0) {
LOG.info("Server sent " + chain.length
+ " certificate(s):");
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = chain[i];
boolean trusted = false;
for (KeyStoreWrapper wrapper : trustStoresToModify) {
if (wrapper.getStore().getCertificateAlias(cert) != null) {
LOG.info("Certificate already known to the"
+ " truststore: " + wrapper.getKeyStoreLocation().getAbsolutePath());
trusted = true;
break;
}
}
if (!trusted) {
certsToSave.add(cert);
}
}
}
return certsToSave;
}
/**
* clears all settings and nullifies are cached passwords. This should be
* called when this object is no longer needed
*/
public void close() {
for (KeyStoreWrapper wrapper : trustStoresToModify) {
wrapper.clear();
}
trustStoresToModify.clear();
}
/**
* Proxy support
* see https://github.com/escline/InstallCert/issues/9
*
* @author vpablos@github
* @param tunnel
* @param host
* @param port
* @throws IOException
*/
private void doTunnelHandshake(Socket tunnel, String host, int port)
throws IOException {
OutputStream out = tunnel.getOutputStream();
String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
+ "User-Agent: "
+ sun.net.www.protocol.http.HttpURLConnection.userAgent
+ "\r\n\r\n";
byte b[];
try {
/*
* We really do want ASCII7 -- the http protocol doesn't change
* with locale.
*/
b = msg.getBytes("ASCII7");
} catch (UnsupportedEncodingException ignored) {
/*
* If ASCII7 isn't there, something serious is wrong, but
* Paranoia Is Good (tm)
*/
b = msg.getBytes();
}
out.write(b);
out.flush();
/*
* We need to store the reply so we can create a detailed
* error message to the user.
*/
byte reply[] = new byte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone = false;
/* Done on first newline */
InputStream in = tunnel.getInputStream();
boolean error = false;
while (newlinesSeen < 2) {
int i = in.read();
if (i < 0) {
throw new IOException("Unexpected EOF from proxy");
}
if (i == '\n') {
headerDone = true;
++newlinesSeen;
} else if (i != '\r') {
newlinesSeen = 0;
if (!headerDone && replyLen < reply.length) {
reply[replyLen++] = (byte) i;
}
}
}
/*
* Converting the byte array to a string is slightly wasteful
* in the case where the connection was successful, but it's
* insignificant compared to the network overhead.
*/
String replyStr;
try {
replyStr = new String(reply, 0, replyLen, "ASCII7");
} catch (UnsupportedEncodingException ignored) {
replyStr = new String(reply, 0, replyLen);
}
/* We check for Connection Established because our proxy returns
* HTTP/1.1 instead of 1.0 */
//if (!replyStr.startsWith("HTTP/1.0 200")) {
if (replyStr.toLowerCase().indexOf(
"200 connection established") == -1) {
throw new IOException("Unable to tunnel through "
+ host + ":" + port
+ ". Proxy returns \"" + replyStr + "\"");
}
/* tunneling Handshake was successful! */
}
public void applyChanges(Set certsToSave, String host) throws Exception {
if (trustStoresToModify.isEmpty()) {
throw new Exception("must initialize a trust store");
}
for (KeyStoreWrapper wrapper : trustStoresToModify) {
LOG.info("Applying changes to " + wrapper.getKeyStoreLocation().getAbsolutePath());
// assign aliases using host name and certificate common name
for (X509Certificate cert : certsToSave) {
String alias = host + " - " + KeyStoreUtilities.getCommonName(cert);
wrapper.getStore().setCertificateEntry(alias, cert);
}
OutputStream out = new FileOutputStream(wrapper.getKeyStoreLocation());
wrapper.getStore().store(out, wrapper.getKeyStorePassword());
out.close();
}
}
// -- class SavingTrustManager ---------------------------------------------
/**
* An {@link X509TrustManager} subclass that accumulates unknown
* certificates in order to allow saving them afterwards.
*/
protected static class SavingTrustManager implements X509TrustManager {
protected X509TrustManager parentTm;
protected Set allAccumulatedCerts
= new HashSet();
protected Set newCerts
= new HashSet();
/**
* The constructor.
*
* @param parentTm an {@link X509TrustManager} instance to do the
* standard part of certificates validation job
* @param ksExtra a {@link KeyStore} instance that contains previously
* accumulated certificates
* @throws KeyStoreException
*/
SavingTrustManager(X509TrustManager parentTm,
Set ksExtra)
throws KeyStoreException {
if (parentTm == null) {
throw new IllegalArgumentException("Parent trust manager cannot be null.");
} else {
this.parentTm = parentTm;
}
if (ksExtra != null) {
for (KeyStoreWrapper wrapper : ksExtra) {
Enumeration ksAliases = wrapper.getStore().aliases();
while (ksAliases.hasMoreElements()) {
String alias = ksAliases.nextElement();
try {
this.allAccumulatedCerts.add((X509Certificate) wrapper.getStore().getCertificate(alias));
} catch (Exception ex) {
LOG.info(ex.getMessage());
if (LOG.isDebugEnabled()) {
LOG.debug(ex.getMessage(), ex);
}
}
}
}
}
} // SavingTrustManager
// .. javax.net.ssl.X509TrustManager methods ...........................
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
throw new UnsupportedOperationException();
} // checkClientTrusted
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
CertificateException exceptionToRethrow = null;
// check the certificate chain against the system truststore
try {
parentTm.checkServerTrusted(chain, authType);
} catch (CertificateException e) // the certificate chain was found not trusted
{
// check if the first certificate in the chain is not known yet
// to the local certificate storage
if (!this.allAccumulatedCerts.contains(chain[0])) {
// save the exception to be re-thrown later if not known
exceptionToRethrow = e;
// save the full chain to both local accumulators
for (X509Certificate cert : chain) {
this.allAccumulatedCerts.add(cert);
this.newCerts.add(cert);
}
}
}
// check and re-throw the exception if any
if (exceptionToRethrow != null) {
throw exceptionToRethrow;
}
} // checkServerTrusted
@Override
public X509Certificate[] getAcceptedIssuers() {
return this.parentTm.getAcceptedIssuers();
} // getAcceptedIssuers
} // class SavingTrustManager
} // class InstallCert