All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.safehaus.chop.api.ChopUtils Maven / Gradle / Ivy

Go to download

Common API for Chop entities shared across environments and across various modules of the project.

The newest version!
package org.safehaus.chop.api;


import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;

import static org.safehaus.chop.api.Constants.RUNNER_WAR;


/**
 * General useful utility methods used in the Chop System.
 */
public class ChopUtils {

    static {
        System.setProperty( "javax.net.ssl.trustStore", "jssecacerts" );
    }

    private static final Logger LOG = LoggerFactory.getLogger( ChopUtils.class );
    private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
    private static final Set trustedHosts = new HashSet();
    private static final Object lock = new Object();
    private static File certStore;


    /**
     * Calculates the testBase: the portion of the key or the path to the test's
     * runner.war but not including it. This usually has the 'tests'
     * container/folder in it followed by the shortened version UUID: for
     * example a project whose war is tests/70a4673b/runner.war will have
     * the testBase of tests/70a4673b/. The last '/' will always be included.
     *
     * @param project the project who's testBase to calculate
     * @return the testBase of the project
     * @throws NullPointerException if the project is null or it's loadKey property is null
     */
    public static String getTestBase( Project project ) {
        Preconditions.checkNotNull( project, "The project cannot be null." );
        return getTestBase( project.getLoadKey() );
    }


    /**
     * Calculates the testBase: the portion of the key or the path to the test's
     * runner.war but not including it. This usually has the 'tests'
     * container/folder in it followed by the shortened version UUID: for
     * example a project whose war is 'tests/70a4673b/runner.war' will have
     * the testBase of tests/70a4673b/. The last '/' will always be included.
     *
     * @param loadKey the loadKey of a project: i.e. 'tests/70a4673b/runner.war'
     * @return the testBase of the project
     * @throws NullPointerException if the loadKey is null
     */
    public static String getTestBase( String loadKey ) {
        Preconditions.checkNotNull( loadKey, "The loadKey argument cannot be null." );
        return loadKey.substring( 0, loadKey.length() - RUNNER_WAR.length() );
    }


    public static boolean isTrusted( String hostname ) {
        synchronized ( lock ) {
            return trustedHosts.contains( hostname );
        }
    }


    public static boolean isTrusted( Runner runner ) {
        synchronized ( lock ) {
            return trustedHosts.contains( runner.getHostname() );
        }
    }


    public static boolean isStoreInitialized() {
        return certStore != null;
    }

    public static void installRunnerKey( char[] passphrase, Runner... runners ) throws Exception {
    }


    public static void installRunnerKey( char[] passphrase, String... hostnames ) throws Exception {
        if ( passphrase == null ) {
            passphrase = "changeit".toCharArray();
        }

        File file;
        if ( certStore != null ) {
            file = certStore;
        }
        else {
            file = new File( "jssecacerts" );
        }

        if ( ! file.isFile() ) {
            char SEP = File.separatorChar;
            File dir = new File( System.getProperty( "java.home" ) + SEP + "lib" + SEP + "security" );
            file = new File( dir, "jssecacerts" );
            if ( !file.isFile() ) {
                file = new File( dir, "cacerts" );
            }
        }

        certStore = file;

        LOG.debug( "Loading KeyStore {}", file );

        InputStream in = new FileInputStream( file );
        KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
        ks.load( in, passphrase );
        in.close();

        CertificateFactory cf = CertificateFactory.getInstance( "X.509" );
        Certificate cert =  cf.generateCertificate( getCertificateStream() );

        for ( String hostname : hostnames ) {
            ks.setCertificateEntry( hostname, cert );
            LOG.debug( "Added certificate to keystore 'jssecacerts' using alias '" + hostname + "'" );
        }

        OutputStream out = new FileOutputStream( "jssecacerts" );
        ks.store( out, passphrase );
        out.close();

        synchronized ( lock ) {
            Collections.addAll( trustedHosts, hostnames );
        }
    }


    private static InputStream getCertificateStream () throws IOException {
        InputStream in = ChopUtils.class.getClassLoader().getResourceAsStream( "runner.cer" );
        DataInputStream dis = new DataInputStream( in );
        byte[] bytes = new byte[ dis.available() ];
        dis.readFully( bytes );
        return new ByteArrayInputStream( bytes );
    }



    /**
     * Installs a certificate from the server into a local certificate store.
     *
     * @param host the HTTPS base server host to get the certificate from
     * @param port the port of the server
     * @param passphrase the passphrase to access/set the cert store if it does not
     * exist, defaults to "changeit" if null is provided
     * @throws Exception if something goes wrong
     */
    public static void installCert( String host, int port, char[] passphrase ) throws Exception {

        if ( passphrase == null ) {
            passphrase = "changeit".toCharArray();
        }

        File file;
        if ( certStore != null ) {
            file = certStore;
        }
        else {
            file = new File( "jssecacerts" );
        }

        if ( ! file.isFile() ) {
            char SEP = File.separatorChar;
            File dir = new File( System.getProperty( "java.home" ) + SEP + "lib" + SEP + "security" );
            file = new File( dir, "jssecacerts" );
            if ( !file.isFile() ) {
                file = new File( dir, "cacerts" );
            }
        }

        certStore = file;

        LOG.debug( "Loading KeyStore {}", file );
        InputStream in = new FileInputStream( file );
        KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
        ks.load( in, passphrase );
        in.close();

        SSLContext context = SSLContext.getInstance( "TLS" );
        TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
        tmf.init( ks );
        X509TrustManager defaultTrustManager = ( X509TrustManager ) tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager( defaultTrustManager );
        context.init( null, new TrustManager[] { tm }, null );
        SSLSocketFactory factory = context.getSocketFactory();

        // Try to reconnect in case there are newly launched instances and they're not fully up yet
        SSLSocket socket = null;
        int trial = 0;
        boolean success = false;
        ConnectException connectException = null;
        do {
            try {
                LOG.info( "Opening connection to {}:{}", host, port );
                socket = ( SSLSocket ) factory.createSocket( host, port );
                socket.setSoTimeout( 10000 );
                success = true;
            }
            catch ( ConnectException e ) {
                connectException = e;
                Thread.sleep( 1500 );
            }
        }
        while ( !success && trial++ < 10 );

        if( !success ) {
            throw connectException;
        }

        try {
            LOG.debug( "Starting SSL handshake..." );
            socket.startHandshake();
            socket.close();
            LOG.debug( "No errors, certificate is already trusted" );
        }
        catch ( SSLException e ) {
            LOG.debug( "Cert is NOT trusted: {}", e.getMessage() );
        }

        X509Certificate[] chain = tm.chain;
        if ( chain == null ) {
            LOG.warn( "Could not obtain server certificate chain" );
            return;
        }

        LOG.debug( "Server sent " + chain.length + " certificate(s):" );
        MessageDigest sha1 = MessageDigest.getInstance( "SHA1" );
        MessageDigest md5 = MessageDigest.getInstance( "MD5" );
        for ( int i = 0; i < chain.length; i++ ) {
            X509Certificate cert = chain[i];
            LOG.debug( " " + ( i + 1 ) + " Subject " + cert.getSubjectDN() );
            LOG.debug( "   Issuer  " + cert.getIssuerDN() );
            sha1.update( cert.getEncoded() );
            LOG.debug( "   sha1    " + toHexString( sha1.digest() ) );
            md5.update( cert.getEncoded() );
            LOG.debug( "   md5     " + toHexString( md5.digest() ) );
        }

        int k = 0;

        X509Certificate cert = chain[k];
        // now just using the hostname instead of : String alias = host + "-" + ( k + 1 );
        ks.setCertificateEntry( host, cert );

        OutputStream out = new FileOutputStream( "jssecacerts" );
        ks.store( out, passphrase );
        out.close();

        LOG.debug( "cert = {}", cert );
        LOG.debug( "Added certificate to keystore 'jssecacerts' using alias '" + host + "'" );
    }


    private static String toHexString( byte[] bytes ) {
        StringBuilder sb = new StringBuilder( bytes.length * 3 );
        for ( int b : bytes ) {
            b &= 0xff;
            sb.append( HEX_DIGITS[b >> 4] );
            sb.append( HEX_DIGITS[b & 15] );
            sb.append( ' ' );
        }
        return sb.toString();
    }


    private static class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;
        private X509Certificate[] chain;


        SavingTrustManager( X509TrustManager tm ) {
            this.tm = tm;
        }


        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }


        public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException {
            throw new UnsupportedOperationException();
        }


        public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted( chain, authType );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy