001package ca.uhn.hl7v2.hoh.sockets;
002
003import java.io.FileInputStream;
004import java.io.FileNotFoundException;
005import java.io.IOException;
006import java.net.ServerSocket;
007import java.net.Socket;
008import java.security.KeyManagementException;
009import java.security.KeyStore;
010import java.security.KeyStoreException;
011import java.security.NoSuchAlgorithmException;
012import java.security.UnrecoverableKeyException;
013import java.security.cert.CertificateException;
014
015import javax.net.ssl.KeyManager;
016import javax.net.ssl.KeyManagerFactory;
017import javax.net.ssl.SSLContext;
018import javax.net.ssl.SSLServerSocketFactory;
019import javax.net.ssl.SSLSocketFactory;
020import javax.net.ssl.TrustManager;
021import javax.net.ssl.TrustManagerFactory;
022
023/**
024 * Socket Factory which creates a TLS/SSL socket using a custom keystore and
025 * certificate.
026 */
027public class CustomCertificateTlsSocketFactory implements ISocketFactory {
028
029        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomCertificateTlsSocketFactory.class);
030        private KeyStore myKeystore;
031        private String myKeystoreFilename;
032        private String myKeystorePassphrase;
033        private String myKeystoreType = "JKS";
034        private SSLServerSocketFactory myServerSocketFactory;
035
036        private SSLSocketFactory mySocketFactory = null;
037
038        /**
039         * Constructor
040         */
041        public CustomCertificateTlsSocketFactory() {
042                super();
043        }
044
045        /**
046         * Constructor
047         * 
048         * @throws NullPointerException
049         *             If theKeystore is null
050         */
051        public CustomCertificateTlsSocketFactory(KeyStore theKeystore, String theKeystorePass) {
052                if (theKeystore == null) {
053                        throw new NullPointerException("KeyStore can not be null");
054                }
055                myKeystore = theKeystore;
056                myKeystorePassphrase = theKeystorePass;
057        }
058
059        /**
060         * Constructor
061         * 
062         * @param theKeystoreType
063         *            The keystore type, e.g. "JKS"
064         * @param theKeystoreFilename
065         *            The path to the keystore
066         * @param theKeystorePassphrase
067         *            The password for the keystore
068         */
069        public CustomCertificateTlsSocketFactory(String theKeystoreType, String theKeystoreFilename, String theKeystorePassphrase) {
070                super();
071                myKeystoreType = theKeystoreType;
072                myKeystoreFilename = theKeystoreFilename;
073                myKeystorePassphrase = theKeystorePassphrase;
074        }
075
076        /**
077         * {@inheritDoc}
078         */
079        public Socket createClientSocket() throws IOException {
080                initialize();
081                ourLog.debug("Creating client socket");
082                return mySocketFactory.createSocket();
083        }
084
085        /**
086         * {@inheritDoc}
087         */
088        public ServerSocket createServerSocket() throws IOException {
089                initialize();
090                ourLog.debug("Creating server socket");
091                return myServerSocketFactory.createServerSocket();
092        }
093
094        private void initialize() throws IOException {
095                if (mySocketFactory != null) {
096                        return;
097                }
098
099                try {
100                        char[] passphrase = myKeystorePassphrase != null ? myKeystorePassphrase.toCharArray() : null;
101                        if (myKeystore == null) {
102
103                                myKeystore = KeyStore.getInstance(myKeystoreType);
104
105                                try {
106                                        myKeystore.load(new FileInputStream(myKeystoreFilename), passphrase);
107                                } catch (IOException e) {
108                                        throw new IOException("Failed to load keystore: " + myKeystoreFilename, e);
109                                }
110                        }
111
112                        SSLContext ctx = SSLContext.getInstance("TLS");
113                        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
114                        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
115
116                        kmf.init(myKeystore, passphrase);
117                        tmf.init(myKeystore);
118                        TrustManager[] trustManagers = tmf.getTrustManagers();
119                        KeyManager[] keyManagers = kmf.getKeyManagers();
120                        ctx.init(keyManagers, trustManagers, null);
121
122                        mySocketFactory = ctx.getSocketFactory();
123                        myServerSocketFactory = ctx.getServerSocketFactory();
124
125                } catch (NoSuchAlgorithmException e) {
126                        throw new IOException("Failed to initialize socket factory: " + e.getMessage(), e);
127                } catch (CertificateException e) {
128                        throw new IOException("Failed to initialize socket factory: " + e.getMessage(), e);
129                } catch (FileNotFoundException e) {
130                        throw new IOException("Failed to initialize socket factory: " + e.getMessage(), e);
131                } catch (UnrecoverableKeyException e) {
132                        throw new IOException("Failed to initialize socket factory: " + e.getMessage(), e);
133                } catch (KeyStoreException e) {
134                        throw new IOException("Failed to initialize socket factory: " + e.getMessage(), e);
135                } catch (KeyManagementException e) {
136                        throw new IOException("Failed to initialize socket factory: " + e.getMessage(), e);
137                }
138
139        }
140
141        /**
142         * The filename to load as a keystore
143         */
144        public void setKeystoreFilename(String theKeystoreFilename) {
145                myKeystoreFilename = theKeystoreFilename;
146        }
147
148        /**
149         * The passphrase for the keystore
150         */
151        public void setKeystorePassphrase(String theKeystorePassphrase) {
152                myKeystorePassphrase = theKeystorePassphrase;
153        }
154
155        /**
156         * Sets the keystore type (e.g. JKS, JCEKS)
157         */
158        public void setKeystoreType(String theKeystoreType) {
159                myKeystoreType = theKeystoreType;
160        }
161
162}