com.swisscom.ais.itext.Connect.java Source code

Java tutorial

Introduction

Here is the source code for com.swisscom.ais.itext.Connect.java

Source

/**
 * Class to connect to a server using certificates
 *
 * Created:
 * 03.12.13 KW49 14:51
 * </p>
 * Last Modification:
 * 22.01.2014 11:24
 * <p/>
 * Version:
 * 1.0.0
 * </p>
 * Copyright:
 * Copyright (C) 2013. All rights reserved.
 * </p>
 * License:
 * Licensed under the Apache License, Version 2.0 or later; see LICENSE.md
 * </p>
 * Author:
 * Swisscom (Schweiz) AG
 */

package com.swisscom.ais.itext;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collections;

public class Connect {

    /**
     * The value is used to decide if debug information should be print
     */
    boolean _debugMode = false;

    /**
     * The url of the server to make a connection
     */
    private String _url;

    /**
     * The private key
     */
    private String _privateKey;

    /**
     * Certificate of server where to connect
     */
    private String _serverCert;

    /**
     * Certificate to connect to server
     */
    private String _clientCert;

    /**
     * Connection timeout for server in milli seconds
     */
    private int _timeout;

    /**
     * Constructor to set relevant parameters and add security provider
     *
     * @param url        URL of the server where to connect
     * @param privateKey Private key of the user
     * @param serverCert Certificate of the server where to connect
     * @param clientCert Certificate to connect to the server and it should trust
     * @param timeout    Time for connection timeout in milli seconds
     * @param debug      If debug is set to true debug information will be print out. Otherwise it will not print debug information.
     */
    public Connect(@Nonnull String url, @Nonnull String privateKey, @Nonnull String serverCert,
            @Nonnull String clientCert, int timeout, boolean debug) {
        this._url = url;
        this._privateKey = privateKey;
        this._serverCert = serverCert;
        this._clientCert = clientCert;
        this._timeout = timeout;
        _debugMode = debug;

        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * Get a connection object. This will create key manager, trust manager, socket factory and set timeout.
     *
     * @return
     * @throws KeyManagementException   If SSLContext can not be initialized
     * @throws NoSuchAlgorithmException If no provider supports a TrustManagerFactorySpi implementation for the specified protocol
     * @throws IOException              If the string specifies an unknown protocol or if an I/O exception occurs
     */
    @Nullable
    public URLConnection getConnection() throws KeyManagementException, NoSuchAlgorithmException, IOException {

        KeyManager[] keyManagers = createKeyManagers();
        TrustManager[] trustManagers = createTrustManagers();
        SSLSocketFactory factory = initItAll(keyManagers, trustManagers);
        URLConnection con = createConnectionObject(_url, factory);
        con.setConnectTimeout(_timeout);

        return con;
    }

    /**
     * Creates a connection object
     *
     * @param urlString        String to parse as a URL.
     * @param sslSocketFactory The SSL socket factory
     * @return A URLConnection to the URL. If it is a https connection return type will be a HttpsURLConnection
     * @throws IOException If the string specifies an unknown protocol or if an I/O exception occurs
     */
    private URLConnection createConnectionObject(@Nonnull String urlString,
            @Nonnull SSLSocketFactory sslSocketFactory) throws IOException {

        URL url = new URL(urlString);
        URLConnection connection = url.openConnection();
        if (connection instanceof HttpsURLConnection) {
            ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);
        }
        return connection;
    }

    /**
     * Inits SSLContext and create a SocketFactoryObject
     *
     * @param keyManagers   Sources of authentication keys
     * @param trustManagers Sources of peer authentication trust decisions
     * @return A SocketFactory object
     * @throws NoSuchAlgorithmException If no provider supports a TrustManagerFactorySpi implementation for the specified protocol
     * @throws KeyManagementException   If SSLContext can not be initialized
     */
    private SSLSocketFactory initItAll(@Nonnull KeyManager[] keyManagers, @Nonnull TrustManager[] trustManagers)
            throws NoSuchAlgorithmException, KeyManagementException {

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(keyManagers, trustManagers, null);
        SSLSocketFactory socketFactory = context.getSocketFactory();
        return socketFactory;
    }

    /**
     * Create KeyManagers to handle keys
     *
     * @return Array with KeyManager objects
     */
    private KeyManager[] createKeyManagers() {

        KeyManager[] managers = new KeyManager[] {
                new Connect(_url, _privateKey, _serverCert, _clientCert, _timeout, _debugMode).new AliasKeyManager(
                        _clientCert, _privateKey, _serverCert, _debugMode) };

        return managers;
    }

    /**
     * Create TrustManagers to check validation of server certificates. Here TrustManagers will not check if a client is trusted
     *
     * @return Array with Trustmanager object
     */
    @Nullable
    private TrustManager[] createTrustManagers() {

        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

            X509Certificate[] trustedIssuers = null;

            @Override
            public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
                //not relevant here
            }

            @Override
            public void checkServerTrusted(final X509Certificate[] chain, final String authType)
                    throws CertificateException {

                if (chain == null || chain.length < 2) {
                    throw new CertificateException("Error when validating server certificate");
                }

                X509Certificate certToVerify = chain[0];
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                CertPath cp = cf.generateCertPath(Arrays.asList(new X509Certificate[] { certToVerify }));

                TrustAnchor trustAnchor = new TrustAnchor(chain[1], null);

                CertPathValidator cpv = null;
                try {
                    cpv = CertPathValidator.getInstance("PKIX");

                    PKIXParameters pkixParams = new PKIXParameters(Collections.singleton(trustAnchor));
                    pkixParams.setRevocationEnabled(false);

                    CertPathValidatorResult validated = cpv.validate(cp, pkixParams);

                    if (validated == null) {
                        throw new CertificateException("Error when validating server certificate");
                    }

                    trustedIssuers = chain;

                } catch (Exception e) {
                    throw new CertificateException("Error when validating server certificate");
                }
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return trustedIssuers;
            }
        } };

        return trustAllCerts;
    }

    /**
     * Instances manage which certificate-based key pairs are used to authenticate the local side of a secure socket.
     */
    private class AliasKeyManager implements X509KeyManager {

        /**
         * Path of certificate for a client to authenticate to a server
         */
        private String clientCert;

        /**
         * Path of private key
         */
        private String _privateKeyName;

        /**
         * Path of certificate from server to authenticate it
         */
        private String _serverCert;

        /**
         * If value is set to true debug information will be print out otherwise not
         */
        private boolean _debugMode;

        /**
         * Constructor for this class. Sets variables
         *
         * @param clientCert     Path of certificate to authenticate to a server
         * @param privateKeyName Path of private key
         * @param serverCert     Path of certificate from server to authenticate it
         * @param debugMode      If set to true debug information will be print out otherwise not
         */
        public AliasKeyManager(@Nonnull String clientCert, @Nonnull String privateKeyName,
                @Nonnull String serverCert, boolean debugMode) {

            this.clientCert = clientCert;
            this._privateKeyName = privateKeyName;
            this._serverCert = serverCert;
            this._debugMode = debugMode;
        }

        /**
         * Return path of client certificate to authenticate to a server
         *
         * @param str       Set to null. Is only given because class implements interface
         * @param principal Set to null. Is only given because class implements interface
         * @param socket    Set to null. Is only given because class implements interface
         * @return path of client certificate
         */
        public String chooseClientAlias(@Nullable String[] str, @Nullable Principal[] principal,
                @Nullable Socket socket) {
            return clientCert;
        }

        /**
         * Return path of server certificate to authenticate the server
         *
         * @param str       Set to null. Is only given because class implements interface
         * @param principal Set to null. Is only given because class implements interface
         * @param socket    Set to null. Is only given because class implements interface
         * @return path of server certificate
         */
        public String chooseServerAlias(@Nullable String str, @Nullable Principal[] principal,
                @Nullable Socket socket) {
            return _serverCert;
        }

        /**
         * Create X509 certificate chain from client certificate to authenticate against a server
         *
         * @param clientCertFilePath Path of client certificate
         * @return Array with X509 certificates. Null if chain can not be created of client certificate
         */
        @Nullable
        public X509Certificate[] getCertificateChain(String clientCertFilePath) {

            try {
                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                Certificate certificate = certificateFactory
                        .generateCertificate(new FileInputStream(clientCertFilePath));
                return new X509Certificate[] { (X509Certificate) certificate };
            } catch (Exception e) {
                if (_debugMode)
                    e.printStackTrace();
                return null;
            }
        }

        /**
         * Return path of client certificate to authenticate against a server
         *
         * @param str       Set to null. Is only given because class implements interface
         * @param principal Set to null. Is only given because class implements interface
         * @return Array with only entry. This is the path of client certificate
         */
        public String[] getClientAliases(@Nullable String str, @Nullable Principal[] principal) {
            return new String[] { clientCert };
        }

        /**
         * Return path of client certificate to authenticate against a server
         *
         * @param str       Set to null. Is only given because class implements interface
         * @param principal Set to null. Is only given because class implements interface
         * @return Array with only entry. This is the path of client certificate
         */
        public String[] getServerAliases(String str, Principal[] principal) {
            return new String[] { clientCert };
        }

        /**
         * Create private key from file. Creating the key depends on the type. An existing X509 key file can be immediately
         * convert to a PrivateKey object. If source file is a RSA key it is necessary to create a PEMKeyPair first and
         * afterwards convert to PrivateKey object
         *
         * @param privateKeyPath Path of private key
         * @return PrivateKey if key can be generated otherwise null
         */
        @Nullable
        public PrivateKey getPrivateKey(String privateKeyPath) {

            try {
                BufferedReader br = new BufferedReader(new FileReader(_privateKeyName));

                //if we read a X509 key we will get immediately PrivatekeyInfo if key is a RSA key it is necessary to
                //create a PEMKeyPair first
                PrivateKeyInfo privateKeyInfo = null;
                PEMParser pemParser = null;
                try {
                    pemParser = new PEMParser(br);
                    privateKeyInfo = (PrivateKeyInfo) pemParser.readObject();
                } catch (Exception e) {
                    br.close();
                    br = new BufferedReader(new FileReader(_privateKeyName));
                    pemParser = new PEMParser(br);
                    PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
                    privateKeyInfo = pemKeyPair.getPrivateKeyInfo();
                }

                pemParser.close();
                br.close();

                JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
                java.security.PrivateKey privateKey = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);

                return privateKey;

            } catch (Exception e) {
                if (_debugMode)
                    e.printStackTrace();

                return null;
            }
        }
    }

}