net.sf.sahi.ssl.SSLHelper.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.sahi.ssl.SSLHelper.java

Source

/**
 * Sahi - Web Automation and Test Tool
 *
 * Copyright  2006  V Narayan Raman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sf.sahi.ssl;

import net.sf.sahi.config.Configuration;
import net.sf.sahi.util.Utils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import javax.net.ssl.*;
import java.io.*;
import java.math.BigInteger;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;

public class SSLHelper {

    private static final SSLHelper instance = new SSLHelper();
    private static final String KEYSTORE_PASSWORD = "DUMMYPASSWORD";
    private static final Logger logger = Logger.getLogger(SSLHelper.class);
    static HashMap<String, SSLSocketFactory> sslSocketFactories = new HashMap<>();
    public final String rootCAPath = Configuration.getRootCaPath();
    private final Provider bouncyCastleProvider = new BouncyCastleProvider();
    private KeyStore keystore;
    private X509Certificate rootCA;
    private KeyStore clientCertKeystore;
    private SecureRandom secureRandom = new SecureRandom();

    public static SSLHelper getInstance() {
        return instance;
    }

    private synchronized static void executeCommand(String command) throws Exception {
        Utils.executeCommand(Utils.getCommandTokens(command));
    }

    public static TrustManager[] getAllTrustingManager() {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };
        return trustAllCerts;
    }

    private SSLSocketFactory getSSLClientSocketFactory(String domain) throws Exception {
        if (!sslSocketFactories.containsKey(domain)) {
            generateKeyAndPutIntoKeyStore(domain);
            final SSLSocketFactory socketFactory = createSocketFactory(domain);
            sslSocketFactories.put(domain, socketFactory);
            return socketFactory;
        }
        return sslSocketFactories.get(domain);
    }

    public void checkRootCA() throws Exception {
        createKeystore();
        createClientKeyStore();
        if (!new File(rootCAPath).exists() || !new File(Configuration.getRootKeyPath()).exists()) {
            createRootCA();
        } else {
            readRootCA();
        }
    }

    /**
     * Read certificate and adds it to the keystore.
     *
     * @throws IOException
     * @throws CertificateException
     * @throws KeyStoreException
     */
    private void readRootCA() throws IOException, CertificateException, KeyStoreException {
        Key _privateKey = readPrivateKey(Configuration.getRootKeyPath());
        X509CertificateHolder holder = (X509CertificateHolder) readWithPemParser(Configuration.getRootCaPath());
        rootCA = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME)
                .getCertificate(holder);
        keystore.setKeyEntry(Configuration.getRootCaName(), _privateKey, KEYSTORE_PASSWORD.toCharArray(),
                new X509Certificate[] { rootCA });
    }

    private PrivateKey readPrivateKey(String privateKeyPath) throws IOException {
        PEMKeyPair keyPair = (PEMKeyPair) new PEMParser(new FileReader(privateKeyPath)).readObject();
        return new JcaPEMKeyConverter().getKeyPair(keyPair).getPrivate();
    }

    private void createClientKeyStore() throws Exception {
        clientCertKeystore = KeyStore.getInstance(Configuration.getSSLClientKeyStoreType());
        String clientCertPath = Configuration.getSSLClientCertPath();
        FileInputStream fileInputStream = null;
        if (clientCertPath != null) {
            try {
                fileInputStream = new FileInputStream(clientCertPath);
            } catch (IOException ioe) {
                logger.error("\n----\nCertificate not found: " + clientCertPath + "\n----");
            }
        }
        String password = Configuration.getSSLClientCertPassword();
        char[] passphrase = password == null ? null : password.toCharArray();
        clientCertKeystore.load(fileInputStream, passphrase);
    }

    private Object readWithPemParser(String source) throws IOException {
        PEMParser parser = new PEMParser(new FileReader(source));
        return parser.readObject();
    }

    protected KeyStore getKeyStore() {
        return keystore;
    }

    private SSLSocketFactory createSocketFactory(String domain) throws Exception {
        SSLSocketFactory factory;
        KeyManagerFactory keyManagerFactory = getKeyManagerFactory(keystore, KEYSTORE_PASSWORD);
        SSLContext sslContext = SSLContext.getInstance("SSLv3");
        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
        keyManagers = wrapKeyManagers(keyManagers, domain);
        sslContext.init(keyManagers, getAllTrustingManager(), secureRandom);
        factory = sslContext.getSocketFactory();
        return factory;
    }

    private KeyManager[] wrapKeyManagers(KeyManager[] kma, String domain) {
        KeyManager[] kma2 = new KeyManager[kma.length];
        for (int i = 0; i < kma2.length; i++) {
            kma2[i] = new X509KeyMangerWrapper((X509KeyManager) kma[i], domain);
        }
        return kma2;
    }

    public KeyManagerFactory getKeyManagerFactoryForRemoteFetch() throws UnrecoverableKeyException,
            NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
        String fileWithPath = Configuration.getSSLClientCertPath();
        if (fileWithPath != null)
            logger.info("Using SSL Cient Cert path at: " + fileWithPath);
        return getKeyManagerFactory(clientCertKeystore, Configuration.getSSLClientCertPassword());
    }

    public KeyManagerFactory getKeyManagerFactory(KeyStore keystore, String password)
            throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException,
            UnrecoverableKeyException {
        char[] passphrase = password == null ? null : password.toCharArray();
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(Configuration.getSSLAlgorithm());

        keyManagerFactory.init(keystore, passphrase);
        return keyManagerFactory;
    }

    protected void generateKeyAndPutIntoKeyStore(final String domain) throws Exception {
        KeyPair keyPair = newKeyPair();
        X509Certificate cert = buildSignedCert(domain, keyPair);
        addCertToKeystore(cert, keyPair.getPrivate(), domain);
    }

    /**
     * Creates an empty keystore
     * @throws Exception
     */
    private void createKeystore() throws Exception {
        if (keystore == null) {
            Security.addProvider(bouncyCastleProvider);
            keystore = KeyStore.getInstance(Configuration.getSSLClientKeyStoreType(), bouncyCastleProvider);
            keystore.load(null, KEYSTORE_PASSWORD.toCharArray());
        }
    }

    /**
     * Private/Public Keys for a certificate
     * @return
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     */
    private KeyPair newKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", bouncyCastleProvider.getName());
        keyPairGenerator.initialize(2048, secureRandom);
        return keyPairGenerator.generateKeyPair();
    }

    private X500Name buildRootIssuer() {
        return buildName(Configuration.getRootCaName());
    }

    /**
     * used to write certificates and keys to a file
     * @param target
     * @param o
     * @throws IOException
     * @throws CertificateEncodingException
     */
    public void writePEMObject(String target, Object o) throws IOException, CertificateEncodingException {
        new File(target).delete();
        PEMWriter writer = new PEMWriter(new FileWriter(target));
        writer.writeObject(o);
        writer.close();
    }

    /**
     * We need a root CA as a file to add to the browser under which all certificates will be trusted.
     *
     * @throws Exception
     */
    private void createRootCA() throws Exception {
        KeyPair _keyPair = newKeyPair();
        rootCA = buildRootCert(Configuration.getRootCaName(), _keyPair);
        writePEMObject(rootCAPath, rootCA);
        writePEMObject(Configuration.getRootKeyPath(), _keyPair.getPrivate());
        keystore.setKeyEntry(Configuration.getRootCaName(), _keyPair.getPrivate(), KEYSTORE_PASSWORD.toCharArray(),
                new X509Certificate[] { rootCA });
    }

    private X509Certificate buildRootCert(String domain, KeyPair _keyPair) throws Exception {
        X509v3CertificateBuilder certificateBuilder = createX509v3CertificateBuilder(domain, _keyPair);
        certificateBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
        return createX509Certificate(certificateBuilder, _keyPair.getPrivate());
    }

    private X509Certificate buildSignedCert(String domain, KeyPair _keyPair) throws Exception {
        PrivateKey privateRootKey = (PrivateKey) keystore.getKey(Configuration.getRootCaName(),
                KEYSTORE_PASSWORD.toCharArray());
        X509v3CertificateBuilder certificateBuilder = createX509v3CertificateBuilder(domain, _keyPair);
        return createX509Certificate(certificateBuilder, privateRootKey);
    }

    private X509Certificate createX509Certificate(X509v3CertificateBuilder certificateBuilder,
            PrivateKey privateRootKey) throws OperatorCreationException, CertificateException {
        ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
                .setProvider(bouncyCastleProvider).build(privateRootKey);
        return new JcaX509CertificateConverter().setProvider(bouncyCastleProvider)
                .getCertificate(certificateBuilder.build(signer));
    }

    private X509v3CertificateBuilder createX509v3CertificateBuilder(String domain, KeyPair _keyPair) {
        X500Name issuer = buildRootIssuer();
        X500Name subject = buildName(domain);
        Date startDate = new Date();
        long aYear = (long) 1000 * 60 * 60 * 24 * 365;
        Date expiryDate = new Date(startDate.getTime() + aYear);
        SubjectPublicKeyInfo subjectPublicKeyInfo = getSubjectPublicKeyInfo(_keyPair);
        return new X509v3CertificateBuilder(issuer, BigInteger.valueOf(new Date().getTime()), new Date(),
                expiryDate, subject, subjectPublicKeyInfo);
    }

    private X500Name buildName(String domain) {
        X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
        builder.addRDN(BCStyle.O, "*** Sahi Browser Testing Dummy Certificate ***");
        builder.addRDN(BCStyle.CN, domain);
        return builder.build();
    }

    private void addCertToKeystore(X509Certificate _cert, Key _privateKey, String alias) throws KeyStoreException {
        X509Certificate[] chain = new X509Certificate[2];
        chain[0] = _cert;
        chain[1] = rootCA;
        keystore.setKeyEntry(alias, _privateKey, KEYSTORE_PASSWORD.toCharArray(), chain);
    }

    private SubjectPublicKeyInfo getSubjectPublicKeyInfo(KeyPair _keyPair) {
        byte[] encodedPublicKey = _keyPair.getPublic().getEncoded();
        return new SubjectPublicKeyInfo(ASN1Sequence.getInstance(encodedPublicKey));
    }

    public SSLSocket convertToSecureSocket(final Socket plainSocket, final String domain) throws Exception {
        try {
            return (SSLSocket) getSSLClientSocketFactory(domain).createSocket(plainSocket,
                    plainSocket.getInetAddress().getHostName(), plainSocket.getPort(), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public SSLSocket convertToSecureServerSocket(final Socket socket, final String domain) throws Exception {
        SSLSocket sslSocket = convertToSecureSocket(socket, domain);
        sslSocket.setUseClientMode(false);
        return sslSocket;
    }

    /**
     * Wrapper for the key manager to select the correct server key within a single
     * key store.
     *
     * @author Jens Wilke
     */
    public static class X509KeyMangerWrapper implements X509KeyManager {

        X509KeyManager wrapped;
        String domain;

        public X509KeyMangerWrapper(X509KeyManager wrapped, String domain) {
            this.wrapped = wrapped;
            this.domain = domain;
        }

        public String[] getClientAliases(String s, Principal[] principals) {
            return wrapped.getClientAliases(s, principals);
        }

        public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
            return wrapped.chooseClientAlias(strings, principals, socket);
        }

        public String[] getServerAliases(String s, Principal[] principals) {
            return wrapped.getServerAliases(s, principals);
        }

        /**
         * the socket factory calls this through the ssl context
         * first with EC_EC and then with RSA. The normal key store implementation
         * returns the last certificate. Return the CN for the domain we want to
         * connect. This certificate and key is also existing in the keystore.
         */
        public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
            return domain;
        }

        public X509Certificate[] getCertificateChain(String s) {
            return wrapped.getCertificateChain(s);
        }

        public PrivateKey getPrivateKey(String s) {
            return wrapped.getPrivateKey(s);
        }
    }
}