Cleanup switch certificate chain handling
[openflowplugin.git] / openflowjava / openflow-protocol-impl / src / main / java / org / opendaylight / openflowjava / protocol / impl / core / SslContextFactory.java
1 /*
2  * Copyright (c) 2013 Pantheon Technologies s.r.o. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowjava.protocol.impl.core;
10
11 import static java.util.Objects.requireNonNull;
12
13 import java.io.IOException;
14 import java.net.Socket;
15 import java.security.KeyManagementException;
16 import java.security.KeyStore;
17 import java.security.KeyStoreException;
18 import java.security.NoSuchAlgorithmException;
19 import java.security.Security;
20 import java.security.UnrecoverableKeyException;
21 import java.security.cert.CertificateException;
22 import java.security.cert.X509Certificate;
23 import java.util.List;
24 import javax.net.ssl.KeyManagerFactory;
25 import javax.net.ssl.SSLContext;
26 import javax.net.ssl.SSLEngine;
27 import javax.net.ssl.TrustManager;
28 import javax.net.ssl.TrustManagerFactory;
29 import javax.net.ssl.X509ExtendedTrustManager;
30 import javax.net.ssl.X509TrustManager;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * Class for setting up TLS connection.
38  *
39  * @author michal.polkorab
40  */
41 public class SslContextFactory {
42     private static final Logger LOG = LoggerFactory.getLogger(SslContextFactory.class);
43
44     // "TLS" - supports some version of TLS
45     // Use "TLSv1", "TLSv1.1", "TLSv1.2" for specific TLS version
46     private static final String PROTOCOL = "TLS";
47
48     private final TlsConfiguration tlsConfig;
49
50     private volatile List<X509Certificate> switchCertificateChain;
51
52     /**
53      * Sets the TlsConfiguration.
54      *
55      * @param tlsConfig
56      *            TLS configuration object, contains keystore locations +
57      *            keystore types
58      */
59     public SslContextFactory(final TlsConfiguration tlsConfig) {
60         this.tlsConfig = requireNonNull(tlsConfig);
61     }
62
63     @Nullable List<X509Certificate> getSwitchCertificateChain() {
64         return switchCertificateChain;
65     }
66
67     void setSwitchCertificateChain(final X509Certificate[] chain) {
68         switchCertificateChain = List.of(chain);
69     }
70
71     public SSLContext getServerContext() {
72         String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
73         if (algorithm == null) {
74             algorithm = "SunX509";
75         }
76         SSLContext serverContext = null;
77         try {
78             KeyStore ks = KeyStore.getInstance(tlsConfig.getTlsKeystoreType().name());
79             ks.load(SslKeyStore.asInputStream(tlsConfig.getTlsKeystore(), tlsConfig.getTlsKeystorePathType()),
80                     tlsConfig.getKeystorePassword().toCharArray());
81             KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
82             kmf.init(ks, tlsConfig.getCertificatePassword().toCharArray());
83
84             KeyStore ts = KeyStore.getInstance(tlsConfig.getTlsTruststoreType().name());
85             ts.load(SslKeyStore.asInputStream(tlsConfig.getTlsTruststore(), tlsConfig.getTlsTruststorePathType()),
86                     tlsConfig.getTruststorePassword().toCharArray());
87             TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
88             tmf.init(ts);
89
90             serverContext = SSLContext.getInstance(PROTOCOL);
91
92             // A bit ugly: intercept trust checks to establish switch certificate
93             final TrustManager[] delegates = tmf.getTrustManagers();
94             final TrustManager[] proxies;
95             if (delegates != null) {
96                 proxies = new TrustManager[delegates.length];
97                 for (int i = 0; i < delegates.length; i++) {
98                     final TrustManager delegate = delegates[i];
99                     if (delegate instanceof X509ExtendedTrustManager) {
100                         proxies[i] = new ProxyExtendedTrustManager((X509ExtendedTrustManager) delegate);
101                     } else if (delegate instanceof X509TrustManager) {
102                         proxies[i] = new ProxyTrustManager((X509TrustManager) delegate);
103                     } else {
104                         LOG.debug("Cannot handle trust manager {}, passing through", delegate);
105                         proxies[i] = delegate;
106                     }
107                 }
108             } else {
109                 proxies = null;
110             }
111             serverContext.init(kmf.getKeyManagers(), proxies, null);
112         } catch (IOException e) {
113             LOG.warn("IOException - Failed to load keystore / truststore."
114                     + " Failed to initialize the server-side SSLContext", e);
115         } catch (NoSuchAlgorithmException e) {
116             LOG.warn("NoSuchAlgorithmException - Unsupported algorithm."
117                     + " Failed to initialize the server-side SSLContext", e);
118         } catch (CertificateException e) {
119             LOG.warn("CertificateException - Unable to access certificate (check password)."
120                     + " Failed to initialize the server-side SSLContext", e);
121         } catch (KeyManagementException | KeyStoreException | UnrecoverableKeyException e) {
122             LOG.warn("Exception - Failed to initialize the server-side SSLContext", e);
123         }
124         return serverContext;
125     }
126
127     private final class ProxyTrustManager implements X509TrustManager {
128         private final X509TrustManager delegate;
129
130         ProxyTrustManager(final X509TrustManager delegate) {
131             this.delegate = requireNonNull(delegate);
132         }
133
134         @Override
135         public void checkClientTrusted(final X509Certificate[] chain, final String authType)
136                 throws CertificateException {
137             setSwitchCertificateChain(chain);
138             delegate.checkClientTrusted(chain, authType);
139         }
140
141         @Override
142         public void checkServerTrusted(final X509Certificate[] chain, final String authType)
143                 throws CertificateException {
144             delegate.checkServerTrusted(chain, authType);
145         }
146
147         @Override
148         public X509Certificate[] getAcceptedIssuers() {
149             return delegate.getAcceptedIssuers();
150         }
151     }
152
153     private final class ProxyExtendedTrustManager extends X509ExtendedTrustManager {
154         private final X509ExtendedTrustManager delegate;
155
156         ProxyExtendedTrustManager(final X509ExtendedTrustManager trustManager) {
157             delegate = requireNonNull(trustManager);
158         }
159
160         @Override
161         public void checkClientTrusted(final X509Certificate[] chain, final String authType, final Socket socket)
162                 throws CertificateException {
163             setSwitchCertificateChain(chain);
164             delegate.checkClientTrusted(chain, authType, socket);
165         }
166
167         @Override
168         public void checkClientTrusted(final X509Certificate[] chain, final String authType)
169                 throws CertificateException {
170             setSwitchCertificateChain(chain);
171             delegate.checkClientTrusted(chain, authType);
172         }
173
174         @Override
175         public void checkClientTrusted(final X509Certificate[] chain, final String authType, final SSLEngine sslEngine)
176                 throws CertificateException {
177             setSwitchCertificateChain(chain);
178             delegate.checkClientTrusted(chain, authType, sslEngine);
179         }
180
181         @Override
182         public void checkServerTrusted(final X509Certificate[] chain, final String authType, final SSLEngine sslEngine)
183                 throws CertificateException {
184             delegate.checkServerTrusted(chain, authType, sslEngine);
185         }
186
187         @Override
188         public void checkServerTrusted(final X509Certificate[] chain, final String authType)
189                 throws CertificateException {
190             delegate.checkServerTrusted(chain, authType);
191         }
192
193         @Override
194         public void checkServerTrusted(final X509Certificate[] chain, final String authType, final Socket socket)
195                 throws CertificateException {
196             delegate.checkServerTrusted(chain, authType, socket);
197         }
198
199         @Override
200         public X509Certificate[] getAcceptedIssuers() {
201             return delegate.getAcceptedIssuers();
202         }
203     }
204 }