View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.common.crypto.ssl;
21  
22  import io.netty.channel.Channel;
23  import io.netty.channel.socket.SocketChannel;
24  import io.netty.handler.ssl.SslContext;
25  import io.netty.handler.ssl.SslHandler;
26  import org.waarp.common.logging.WaarpLogger;
27  import org.waarp.common.logging.WaarpLoggerFactory;
28  
29  import java.net.InetSocketAddress;
30  import java.security.cert.X509Certificate;
31  import java.util.List;
32  
33  import static org.waarp.common.digest.WaarpBC.*;
34  
35  /**
36   * SSL ContextFactory for Netty.
37   */
38  public class WaarpSslContextFactory {
39  
40    /**
41     * Internal Logger
42     */
43    private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(WaarpSslContextFactory.class);
44  
45    private static final long DEFAULT_HANDSHAKE_TIMEOUT = 10000;
46    public static final String HAS_TRUST_MANAGER_IS_SERVER_MODE = "Has TrustManager? {} Is ServerMode? {}";
47  
48    static {
49      initializedTlsContext();
50    }
51  
52    /**
53     *
54     */
55    private final SslContext serverContext;
56    private final SslContext serverContextStartTls;
57  
58    /**
59     *
60     */
61    private final SslContext clientContext;
62  
63    private boolean needClientAuthentication;
64  
65    /**
66     * Create both CONTEXT
67     *
68     * @param ggSecureKeyStore
69     */
70    public WaarpSslContextFactory(final WaarpSecureKeyStore ggSecureKeyStore) {
71      // Both construct Client and Server mode
72      serverContext = initSslContextFactory(ggSecureKeyStore, true, false, null);
73      serverContextStartTls = initSslContextFactory(ggSecureKeyStore, true, true, null);
74      clientContext = initSslContextFactory(ggSecureKeyStore, false, false, null);
75    }
76  
77    /**
78     * Create both CONTEXT with ciphers and/or protocols
79     *
80     * @param ggSecureKeyStore
81     * @param ciphers
82     * @param protocols
83     */
84  
85    public WaarpSslContextFactory(final WaarpSecureKeyStore ggSecureKeyStore, final List<String> ciphers,
86                                  final String... protocols) {
87      // Both construct Client and Server mode
88      serverContext = initSslContextFactory(ggSecureKeyStore, true, false, ciphers, protocols);
89      serverContextStartTls = initSslContextFactory(ggSecureKeyStore, true, true, ciphers, protocols);
90      clientContext = initSslContextFactory(ggSecureKeyStore, false, false, ciphers, protocols);
91    }
92  
93    /**
94     * Create only one of the CONTEXT
95     *
96     * @param ggSecureKeyStore
97     * @param serverMode
98     */
99    public WaarpSslContextFactory(final WaarpSecureKeyStore ggSecureKeyStore, final boolean serverMode) {
100     if (serverMode) {
101       serverContext = initSslContextFactory(ggSecureKeyStore, true, false, null);
102       serverContextStartTls = initSslContextFactory(ggSecureKeyStore, true, true, null);
103       clientContext = null;
104     } else {
105       clientContext = initSslContextFactory(ggSecureKeyStore, false, false, null);
106       serverContext = null;
107       serverContextStartTls = null;
108     }
109   }
110 
111   /**
112    * Create only one of the CONTEXT with ciphers and/or protocols
113    *
114    * @param ggSecureKeyStore
115    * @param serverMode
116    * @param ciphers
117    * @param protocols
118    */
119   public WaarpSslContextFactory(final WaarpSecureKeyStore ggSecureKeyStore, final boolean serverMode,
120                                 final List<String> ciphers, final String... protocols) {
121     if (serverMode) {
122       serverContext = initSslContextFactory(ggSecureKeyStore, true, false, ciphers, protocols);
123       serverContextStartTls = initSslContextFactory(ggSecureKeyStore, true, true, ciphers, protocols);
124       clientContext = null;
125     } else {
126       clientContext = initSslContextFactory(ggSecureKeyStore, false, false, ciphers, protocols);
127       serverContext = null;
128       serverContextStartTls = null;
129     }
130   }
131 
132   /**
133    * @param ggSecureKeyStore
134    * @param serverMode
135    *
136    * @return the SSLContext
137    */
138   private SslContext initSslContextFactory(final WaarpSecureKeyStore ggSecureKeyStore,
139                                            final boolean serverMode, final boolean startTls,
140                                            final List<String> ciphers, final String... protocols) {
141     // Initialize the SSLContext to work with our key managers.
142     final WaarpSecureTrustManagerFactory secureTrustManagerFactory =
143         ggSecureKeyStore.getSecureTrustManagerFactory();
144     needClientAuthentication = secureTrustManagerFactory.needAuthentication();
145     X509Certificate[] certificates = null;
146     if (secureTrustManagerFactory.hasTrustStore()) {
147       logger.debug("Has TrustManager");
148       certificates = secureTrustManagerFactory.getX509Certificates();
149     } else {
150       logger.debug("No TrustManager");
151     }
152     if (serverMode) {
153       try {
154         return getInstanceForServer(ggSecureKeyStore.getKeyManagerFactory(), certificates,
155                                     needClientAuthentication, startTls, ciphers, protocols);
156       } catch (final Throwable e) {//NOSONAR
157         logger.error("Failed to initialize the server-side SSLContext {}", e.getMessage());
158         throw new Error("Failed to initialize the server-side SSLContext", e);//NOSONAR
159       }
160     } else {
161       try {
162         return getInstanceForClient(ggSecureKeyStore.getKeyManagerFactory(), certificates);
163       } catch (final Throwable e) {//NOSONAR
164         logger.error("Failed to initialize the client-side SSLContext {}", e.getMessage());
165         throw new Error("Failed to initialize the client-side SSLContext", e);//NOSONAR
166       }
167     }
168   }
169 
170   /**
171    * @return the Server Context
172    */
173   public final SslContext getServerContext() {
174     return serverContext;
175   }
176 
177   /**
178    * @return the Client Context
179    */
180   public final SslContext getClientContext() {
181     return clientContext;
182   }
183 
184   /**
185    * To be called before adding as first entry in the Initializer as<br>
186    * pipeline.addLast("ssl", sslhandler);<br>
187    *
188    * @param needClientAuth True if the client needs to be
189    *     authenticated
190    *     (only if serverMode is True)
191    * @param channel the channel needing the SslHandler
192    *
193    * @return the sslhandler
194    */
195   public final SslHandler createHandlerServer(final boolean needClientAuth, final Channel channel) {
196     logger.debug(HAS_TRUST_MANAGER_IS_SERVER_MODE, needClientAuth, true);
197     channel.config().setAutoRead(true);
198     final SslHandler sslHandler = getServerContext().newHandler(channel.alloc());
199     sslHandler.setHandshakeTimeoutMillis(DEFAULT_HANDSHAKE_TIMEOUT);
200     return sslHandler;
201   }
202 
203   /**
204    * To be called before adding as first entry in the Initializer as<br>
205    * pipeline.addLast("ssl", sslhandler);<br>
206    *
207    * @param needClientAuth True if the client needs to be
208    *     authenticated
209    *     (only if serverMode is True)
210    * @param channel the channel needing the SslHandler
211    *
212    * @return the sslhandler
213    */
214   public final SslHandler createHandlerServer(final boolean needClientAuth, final boolean startTls,
215                                               final Channel channel) {
216     logger.debug(HAS_TRUST_MANAGER_IS_SERVER_MODE, needClientAuth, true);
217     channel.config().setAutoRead(true);
218     final SslHandler sslHandler;
219     if (startTls) {
220       sslHandler = serverContextStartTls.newHandler(channel.alloc());
221     } else {
222       sslHandler = getServerContext().newHandler(channel.alloc());
223     }
224     sslHandler.setHandshakeTimeoutMillis(DEFAULT_HANDSHAKE_TIMEOUT);
225     return sslHandler;
226   }
227 
228   /**
229    * To be called before adding as first entry in the Initializer as<br>
230    * pipeline.addLast("ssl", sslhandler);<br>
231    *
232    * @param channel the channel needing the SslHandler
233    *
234    * @return the sslhandler
235    */
236   public final SslHandler createHandlerClient(final SocketChannel channel) {
237     logger.debug(HAS_TRUST_MANAGER_IS_SERVER_MODE, false, false);
238     channel.config().setAutoRead(true);
239     final InetSocketAddress socketAddress = channel.remoteAddress();
240     final SslHandler sslHandler;
241     if (socketAddress != null) {
242       logger.debug("socket {} {}", socketAddress.getHostName(), socketAddress.getPort());
243       sslHandler = getClientContext().newHandler(channel.alloc(), socketAddress.getHostName(),
244                                                  socketAddress.getPort());
245     } else {
246       sslHandler = getClientContext().newHandler(channel.alloc());
247     }
248     sslHandler.setHandshakeTimeoutMillis(DEFAULT_HANDSHAKE_TIMEOUT);
249     return sslHandler;
250   }
251 
252   /**
253    * @return True if the associated KeyStore has a TrustStore
254    */
255   public final boolean needClientAuthentication() {
256     return needClientAuthentication;
257   }
258 }