package com.ausregistry.jtoolkit2.session;
import javax.net.ssl.*;
import javax.security.auth.x500.X500Principal;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.*;
import java.util.Enumeration;
import java.util.logging.Logger;
import com.ausregistry.jtoolkit2.ErrorPkg;
/**
* <p>
* This defines the operations or actions for establishing a TLS Version 1 based context.
* </p>
*
* <p>
* Uses the maintenance, debug, support and user level loggers.
* </p>
*/
public class TLSContext {
private static final String TLS = "TLSv1";
private static final String[] ENABLED_PROTOCOLS = {TLS};
private static final String TMF_ALGORITHM = TrustManagerFactory.getDefaultAlgorithm();
private SSLContext ctx;
private String commonName;
private final Logger userLogger;
private final Logger supportLogger;
private final Logger maintLogger;
private final Logger debugLogger;
/**
* Instantiates a new TLS context.
*
* @param keystore
* the filename of the key store to be used for the context
* @param keypass
* the password applied to the key store
* @param truststore
* the filename of the trust store that is to be used for the key store
* @param trustpass
* the password used in the trust store
* @param type
* the key store type
* @param algorithm
* the algorithm
* @throws KeyStoreNotFoundException
* the key store not found exception
* @throws KeyStoreReadException
* the key store read exception
* @throws KeyStoreException
* the key store exception
* @throws CertificateException
* the certificate exception
* @throws UnrecoverableKeyException
* the unrecoverable key exception
* @throws NoSuchAlgorithmException
* the no such algorithm exception
* @throws KeyManagementException
* the key management exception
*/
public TLSContext(String keystore, String keypass, String truststore, String trustpass, String type,
String algorithm) throws KeyStoreNotFoundException, KeyStoreReadException,
KeyStoreException, CertificateException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
String pname = getClass().getPackage().getName();
userLogger = Logger.getLogger(pname + ".user");
supportLogger = Logger.getLogger(pname + ".support");
maintLogger = Logger.getLogger(pname + ".maint");
debugLogger = Logger.getLogger(pname + ".debug");
try {
TrustManager[] trustManagers = loadTrustManagers(truststore, trustpass);
KeyManager[] keyManagers = null;
if (keystore != null) {
keyManagers = loadKeyManagers(keystore, keypass, type, algorithm);
}
ctx = SSLContext.getInstance(TLS);
ctx.init(keyManagers, trustManagers, null);
} catch (UnrecoverableKeyException uke) {
// the given passphrase is incorrect.
userLogger.severe(uke.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.keystore.unrecoverable_key"));
// a key in the keystore couldn't be recovered -> useless keystore
throw uke;
} catch (KeyStoreException kse) {
userLogger.severe(kse.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.keystore.initfail"));
throw kse;
} catch (NoSuchAlgorithmException nsae) {
// should not be reachable, unless provider libraries were lost
// after call to loadKeystore().
userLogger.severe(nsae.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.context.nsae"));
throw nsae;
} catch (KeyManagementException kme) {
userLogger.severe(kme.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.context.kme"));
throw kme;
}
}
/**
* Instantiates a new TLS context.
*
* @param truststore
* the filename of the trust store that is to be used for the key store
* @param trustpass
* the password used in the trust store
*
* @throws KeyStoreNotFoundException
* the key store not found exception
* @throws KeyStoreReadException
* the key store read exception
* @throws KeyStoreException
* the key store exception
* @throws CertificateException
* the certificate exception
* @throws UnrecoverableKeyException
* the unrecoverable key exception
* @throws NoSuchAlgorithmException
* the no such algorithm exception
* @throws KeyManagementException
* the key management exception
*/
public TLSContext(String truststore, String trustpass) throws CertificateException,
UnrecoverableKeyException,
NoSuchAlgorithmException,
KeyStoreException,
KeyManagementException,
KeyStoreReadException,
KeyStoreNotFoundException {
this(null, null, truststore, trustpass, null, null);
}
public String getCertificateCommonName() {
return commonName;
}
/**
* Creates an SSL Socket to be used in a session.
*
* @param host
* the host
* @param port
* the port
* @param soTimeout
* the socket timeout
* @return the SSL enabled socket
* @throws IOException
* Signals that an I/O exception has occurred.
* @throws SSLHandshakeException
* the SSL handshake exception
*/
public SSLSocket createSocket(String host, int port, int soTimeout) throws IOException, SSLHandshakeException {
SSLSocket socket = null;
Socket tcpSocket = new Socket();
boolean autoCloseTcpSocket = true;
tcpSocket.connect(new InetSocketAddress(host, port), soTimeout);
try {
socket = (SSLSocket) ctx.getSocketFactory().createSocket(tcpSocket, host, port, autoCloseTcpSocket);
} catch (IOException ioe) {
userLogger.severe(ErrorPkg.getMessage("net.socket.open.fail", new String[] {"<<port>>", "<<host>>" },
new String[] {String.valueOf(port), host }));
userLogger.severe(ioe.getMessage());
throw ioe;
}
socket.setEnabledProtocols(ENABLED_PROTOCOLS);
socket.setSoTimeout(soTimeout);
try {
socket.startHandshake();
} catch (SSLHandshakeException sslhe) {
userLogger.severe(sslhe.getMessage());
String msg = ErrorPkg.getMessage("TLSContext.createSocket.0", "<<java.home>>",
System.getProperty("java.home", "java.home"));
userLogger.severe(msg);
throw sslhe;
} catch (SocketException se) {
userLogger.severe(se.getMessage());
userLogger.severe(ErrorPkg.getMessage("net.socket.open.fail", new String[] {"<<port>>", "<<host>>" },
new String[] {String.valueOf(port), host }));
throw se;
}
socket.getSession().invalidate();
return socket;
}
/**
* Creates a Https connection to the specified URL connection.
*
* @param url the URL to establish a HTTPS connection to
* @param connectionTimeout connection timeout value to be used in milliseconds
* @return HTTPS URL connection
* @throws IOException in case of a malformed URL or an IO exception occurring while trying to open a connection
*/
public HttpsURLConnection createHttpsUrlConnection(String url, Integer connectionTimeout) throws IOException {
HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier() {
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
if (hostname.equals("localhost")) {
return true;
}
return false;
}
});
HttpsURLConnection urlConnection = (HttpsURLConnection) (new URL(url)).openConnection();
urlConnection.setSSLSocketFactory(ctx.getSocketFactory());
urlConnection.setConnectTimeout(connectionTimeout);
return urlConnection;
}
private KeyManager[] loadKeyManagers(String filename, String password, String type, String algorithm)
throws CertificateException, CertificateExpiredException, CertificateNotYetValidException,
KeyManagementException, KeyStoreException, KeyStoreNotFoundException, KeyStoreReadException,
NoSuchAlgorithmException, UnrecoverableKeyException {
char[] passphrase = password.toCharArray();
KeyStore keyStore = loadKeystore(filename, passphrase, type);
KeyManagerFactory kmf = null;
try {
kmf = KeyManagerFactory.getInstance(algorithm);
} catch (NoSuchAlgorithmException nsae) {
// the algorithm specified for the KeyManagerFactory isn't
// available
try {
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
} catch (NoSuchAlgorithmException ex) {
// default algorithm should be available
ex.printStackTrace();
throw ex;
}
}
kmf.init(keyStore, passphrase);
return kmf.getKeyManagers();
}
private TrustManager[] loadTrustManagers(String filename, String password) {
char[] passphrase = password.toCharArray();
try {
KeyStore trustStore = loadKeystore(filename, passphrase, KeyStore.getDefaultType());
TrustManagerFactory tmf = null;
tmf = TrustManagerFactory.getInstance(TMF_ALGORITHM);
tmf.init(trustStore);
return tmf.getTrustManagers();
} catch (Exception e) {
debugLogger.warning("Failed to load user trust store");
return null;
}
}
private KeyStore loadKeystore(String filename, char[] passphrase, String type) throws KeyStoreException,
CertificateException, KeyStoreNotFoundException, KeyStoreReadException, CertificateExpiredException,
CertificateNotYetValidException, NoSuchAlgorithmException {
KeyStore store = null;
try {
store = KeyStore.getInstance(type);
} catch (KeyStoreException kse) {
if (type.equals(KeyStore.getDefaultType())) {
kse.printStackTrace();
throw kse;
} else {
try {
store = KeyStore.getInstance(KeyStore.getDefaultType());
} catch (KeyStoreException ksx) {
ksx.printStackTrace();
throw ksx;
}
}
}
InputStream in = null;
try {
in = getClass().getClassLoader().getResourceAsStream(filename);
if (in == null) {
throw new FileNotFoundException(filename);
}
store.load(in, passphrase);
} catch (CertificateException ce) {
throw ce;
} catch (FileNotFoundException fnfe) {
throw new KeyStoreNotFoundException(fnfe);
} catch (IOException ioe) {
throw new KeyStoreReadException(ioe);
} catch (NullPointerException npe) {
// fatal, keystore instance creation failure even with default type
maintLogger.severe(npe.getMessage());
userLogger.severe(npe.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.keystore.npe"));
npe.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ioe) {
maintLogger.info(ioe.getMessage());
}
}
}
try {
debugLogger.fine("Loaded KeyStore");
Enumeration<String> aliases = store.aliases();
while (aliases.hasMoreElements()) {
Certificate cert = store.getCertificate(aliases.nextElement());
if (!(cert instanceof X509Certificate)) {
continue;
}
X509Certificate x509cert = (X509Certificate) cert;
x509cert.checkValidity();
X500Principal subjectDN = x509cert.getSubjectX500Principal();
String dn = subjectDN.getName(X500Principal.RFC2253);
String[] dnParts = dn.split(",");
for (String part : dnParts) {
String[] pair = part.split("=");
if (pair[0].equals("CN")) {
commonName = pair[1];
debugLogger.fine("Common name: " + commonName);
}
}
}
} catch (CertificateExpiredException cee) {
supportLogger.severe(cee.getMessage());
userLogger.severe(cee.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.cert.validity.expired"));
throw cee;
} catch (CertificateNotYetValidException cnyve) {
supportLogger.severe(cnyve.getMessage());
userLogger.severe(cnyve.getMessage());
userLogger.severe(ErrorPkg.getMessage("ssl.cert.validity.notyet"));
throw cnyve;
}
return store;
}
}