package de.geeksfactory.opacclient.networking;
//Based on
//http://stackoverflow.com/a/6378872/336784
//and
//http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/SSERefGuide.html#X509TrustManager
//and
//https://github.com/nelenkov/custom-cert-https
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* Allows you to trust certificates from additional KeyStores in addition to the default KeyStore
*/
public class AdditionalKeyStoresSSLSocketFactory {
/**
* Creates a customized keystore
*
* @param keyStore The keystore object that should be used in addition to the environments
* default key store.
* @param socketFactory The class that should be used to instantiate a new socket factory, must
* be a subclass of {@link SSLConnectionSocketFactory}.
* @return a new {@link SSLConnectionSocketFactory}
*/
public static SSLConnectionSocketFactory create(KeyStore keyStore, Class<?> socketFactory)
throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)},
null);
if (socketFactory != null) {
try {
return (SSLConnectionSocketFactory) socketFactory
.getDeclaredConstructor(SSLContext.class).newInstance
(sslContext);
} catch (Exception e) {
// Fall back to default
e.printStackTrace();
}
}
return new SSLConnectionSocketFactory(sslContext);
}
public static class AdditionalKeyStoresTrustManager implements
X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
private X509Certificate[] acceptedIssuers;
public AdditionalKeyStoresTrustManager(KeyStore localKeyStore) {
try {
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
defaultTrustManager = findX509TrustManager(tmf);
if (defaultTrustManager == null) {
throw new IllegalStateException(
"Couldn't find X509TrustManager");
}
localTrustManager = new LocalStoreX509TrustManager(
localKeyStore);
List<X509Certificate> allIssuers = new ArrayList<>();
Collections.addAll(allIssuers, defaultTrustManager
.getAcceptedIssuers());
Collections.addAll(allIssuers, localTrustManager
.getAcceptedIssuers());
acceptedIssuers = allIssuers
.toArray(new X509Certificate[allIssuers.size()]);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
static X509TrustManager findX509TrustManager(TrustManagerFactory tmf) {
TrustManager tms[] = tmf.getTrustManagers();
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
return null;
}
public static String getThumbPrint(X509Certificate cert)
throws NoSuchAlgorithmException, CertificateEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] der = cert.getEncoded();
md.update(der);
byte[] digest = md.digest();
return hexify(digest);
}
public static String hexify(byte bytes[]) {
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f'};
StringBuilder buf = new StringBuilder(bytes.length * 2);
for (byte aByte : bytes) {
buf.append(hexDigits[(aByte & 0xf0) >> 4]);
buf.append(hexDigits[aByte & 0x0f]);
}
return buf.toString();
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkClientTrusted(chain, authType);
}
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return acceptedIssuers;
}
static class LocalStoreX509TrustManager implements X509TrustManager {
private X509TrustManager trustManager;
LocalStoreX509TrustManager(KeyStore localTrustStore) {
try {
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
tmf.init(localTrustStore);
trustManager = findX509TrustManager(tmf);
if (trustManager == null) {
throw new IllegalStateException(
"Couldn't find X509TrustManager");
}
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
trustManager.checkServerTrusted(chain, authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
}
}
}