2727import java .security .Security ;
2828import java .security .cert .PKIXBuilderParameters ;
2929import java .security .cert .X509CertSelector ;
30+ import java .util .ArrayList ;
3031import java .util .Arrays ;
32+ import java .util .List ;
3133import java .util .Objects ;
34+ import java .util .Set ;
3235import java .util .concurrent .atomic .AtomicReference ;
3336import javax .net .ssl .CertPathTrustManagerParameters ;
3437import javax .net .ssl .KeyManager ;
4952import org .slf4j .LoggerFactory ;
5053
5154import org .apache .hbase .thirdparty .com .google .common .collect .ObjectArrays ;
55+ import org .apache .hbase .thirdparty .io .netty .handler .ssl .OpenSsl ;
5256import org .apache .hbase .thirdparty .io .netty .handler .ssl .SslContext ;
5357import org .apache .hbase .thirdparty .io .netty .handler .ssl .SslContextBuilder ;
58+ import org .apache .hbase .thirdparty .io .netty .handler .ssl .SslProvider ;
5459
5560/**
5661 * Utility code for X509 handling Default cipher suites: Performance testing done by Facebook
@@ -83,9 +88,10 @@ public final class X509Util {
8388 public static final String TLS_CONFIG_OCSP = CONFIG_PREFIX + "ocsp" ;
8489 public static final String TLS_CONFIG_REVERSE_DNS_LOOKUP_ENABLED =
8590 CONFIG_PREFIX + "host-verification.reverse-dns.enabled" ;
86- private static final String TLS_ENABLED_PROTOCOLS = CONFIG_PREFIX + "enabledProtocols" ;
87- private static final String TLS_CIPHER_SUITES = CONFIG_PREFIX + "ciphersuites" ;
91+ public static final String TLS_ENABLED_PROTOCOLS = CONFIG_PREFIX + "enabledProtocols" ;
92+ public static final String TLS_CIPHER_SUITES = CONFIG_PREFIX + "ciphersuites" ;
8893 public static final String TLS_CERT_RELOAD = CONFIG_PREFIX + "certReload" ;
94+ public static final String TLS_USE_OPENSSL = CONFIG_PREFIX + "useOpenSsl" ;
8995 public static final String DEFAULT_PROTOCOL = "TLSv1.2" ;
9096
9197 //
@@ -131,6 +137,34 @@ private static String[] getCBCCiphers() {
131137 private static final String [] DEFAULT_CIPHERS_JAVA9 =
132138 ObjectArrays .concat (getGCMCiphers (), getCBCCiphers (), String .class );
133139
140+ private static final String [] DEFAULT_CIPHERS_OPENSSL = getOpenSslFilteredDefaultCiphers ();
141+
142+ /**
143+ * Not all of our default ciphers are available in OpenSSL. Takes our default cipher lists and
144+ * filters them to only those available in OpenSsl. Does GCM first, then CBC because GCM tends to
145+ * be better and faster, and we don't need to worry about the java8 vs 9 performance issue if
146+ * OpenSSL is handling it.
147+ */
148+ private static String [] getOpenSslFilteredDefaultCiphers () {
149+ if (!OpenSsl .isAvailable ()) {
150+ return new String [0 ];
151+ }
152+
153+ Set <String > openSslSuites = OpenSsl .availableJavaCipherSuites ();
154+ List <String > defaultSuites = new ArrayList <>();
155+ for (String cipher : getGCMCiphers ()) {
156+ if (openSslSuites .contains (cipher )) {
157+ defaultSuites .add (cipher );
158+ }
159+ }
160+ for (String cipher : getCBCCiphers ()) {
161+ if (openSslSuites .contains (cipher )) {
162+ defaultSuites .add (cipher );
163+ }
164+ }
165+ return defaultSuites .toArray (new String [0 ]);
166+ }
167+
134168 /**
135169 * Enum specifying the client auth requirement of server-side TLS sockets created by this
136170 * X509Util.
@@ -176,7 +210,10 @@ private X509Util() {
176210 // disabled
177211 }
178212
179- static String [] getDefaultCipherSuites () {
213+ static String [] getDefaultCipherSuites (boolean useOpenSsl ) {
214+ if (useOpenSsl ) {
215+ return DEFAULT_CIPHERS_OPENSSL ;
216+ }
180217 return getDefaultCipherSuitesForJavaVersion (System .getProperty ("java.specification.version" ));
181218 }
182219
@@ -202,6 +239,7 @@ public static SslContext createSslContextForClient(Configuration config)
202239
203240 SslContextBuilder sslContextBuilder = SslContextBuilder .forClient ();
204241
242+ boolean useOpenSsl = configureOpenSslIfAvailable (sslContextBuilder , config );
205243 String keyStoreLocation = config .get (TLS_CONFIG_KEYSTORE_LOCATION , "" );
206244 char [] keyStorePassword = config .getPassword (TLS_CONFIG_KEYSTORE_PASSWORD );
207245 String keyStoreType = config .get (TLS_CONFIG_KEYSTORE_TYPE , "" );
@@ -234,11 +272,33 @@ public static SslContext createSslContextForClient(Configuration config)
234272
235273 sslContextBuilder .enableOcsp (sslOcspEnabled );
236274 sslContextBuilder .protocols (getEnabledProtocols (config ));
237- sslContextBuilder .ciphers (Arrays .asList (getCipherSuites (config )));
275+ sslContextBuilder .ciphers (Arrays .asList (getCipherSuites (config , useOpenSsl )));
238276
239277 return sslContextBuilder .build ();
240278 }
241279
280+ /**
281+ * Adds SslProvider.OPENSSL if OpenSsl is available and enabled. In order to make it available,
282+ * one must ensure that a properly shaded netty-tcnative is on the classpath. Properly shaded
283+ * means relocated to be prefixed with "org.apache.hbase.thirdparty" like the rest of the netty
284+ * classes.
285+ */
286+ private static boolean configureOpenSslIfAvailable (SslContextBuilder sslContextBuilder ,
287+ Configuration conf ) {
288+ if (OpenSsl .isAvailable () && conf .getBoolean (TLS_USE_OPENSSL , true )) {
289+ LOG .debug ("Using netty-tcnative to accelerate SSL handling" );
290+ sslContextBuilder .sslProvider (SslProvider .OPENSSL );
291+ return true ;
292+ } else {
293+ if (LOG .isDebugEnabled ()) {
294+ LOG .debug ("Using default JDK SSL provider because netty-tcnative is not {}" ,
295+ OpenSsl .isAvailable () ? "enabled" : "available" );
296+ }
297+ sslContextBuilder .sslProvider (SslProvider .JDK );
298+ return false ;
299+ }
300+ }
301+
242302 public static SslContext createSslContextForServer (Configuration config )
243303 throws X509Exception , IOException {
244304 String keyStoreLocation = config .get (TLS_CONFIG_KEYSTORE_LOCATION , "" );
@@ -254,6 +314,7 @@ public static SslContext createSslContextForServer(Configuration config)
254314 sslContextBuilder = SslContextBuilder
255315 .forServer (createKeyManager (keyStoreLocation , keyStorePassword , keyStoreType ));
256316
317+ boolean useOpenSsl = configureOpenSslIfAvailable (sslContextBuilder , config );
257318 String trustStoreLocation = config .get (TLS_CONFIG_TRUSTSTORE_LOCATION , "" );
258319 char [] trustStorePassword = config .getPassword (TLS_CONFIG_TRUSTSTORE_PASSWORD );
259320 String trustStoreType = config .get (TLS_CONFIG_TRUSTSTORE_TYPE , "" );
@@ -277,7 +338,7 @@ public static SslContext createSslContextForServer(Configuration config)
277338
278339 sslContextBuilder .enableOcsp (sslOcspEnabled );
279340 sslContextBuilder .protocols (getEnabledProtocols (config ));
280- sslContextBuilder .ciphers (Arrays .asList (getCipherSuites (config )));
341+ sslContextBuilder .ciphers (Arrays .asList (getCipherSuites (config , useOpenSsl )));
281342 sslContextBuilder .clientAuth (clientAuth .toNettyClientAuth ());
282343
283344 return sslContextBuilder .build ();
@@ -393,10 +454,10 @@ private static String[] getEnabledProtocols(Configuration config) {
393454 return enabledProtocolsInput .split ("," );
394455 }
395456
396- private static String [] getCipherSuites (Configuration config ) {
457+ private static String [] getCipherSuites (Configuration config , boolean useOpenSsl ) {
397458 String cipherSuitesInput = config .get (TLS_CIPHER_SUITES );
398459 if (cipherSuitesInput == null ) {
399- return getDefaultCipherSuites ();
460+ return getDefaultCipherSuites (useOpenSsl );
400461 } else {
401462 return cipherSuitesInput .split ("," );
402463 }
0 commit comments