All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.omid.tls.X509Util Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.omid.tls;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import org.apache.omid.tls.X509Exception;
import org.apache.omid.tls.X509Exception.KeyManagerException;
import org.apache.omid.tls.X509Exception.SSLContextException;
import org.apache.omid.tls.X509Exception.TrustManagerException;
import org.apache.phoenix.thirdparty.com.google.common.collect.ObjectArrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.util.Arrays;
import java.util.Objects;


/**
 * Utility code for X509 handling Default cipher suites: Performance testing done by Facebook
 * engineers shows that on Intel x86_64 machines, Java9 performs better with GCM and Java8 performs
 * better with CBC, so these seem like reasonable defaults.
 * 

* This file has is based on the one in HBase project. * @see Base * revision */ public final class X509Util { private static final Logger LOG = LoggerFactory.getLogger(X509Util.class); private static final char[] EMPTY_CHAR_ARRAY = new char[0]; // Config public static final int DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS = 5000; public static final String DEFAULT_PROTOCOL = "TLSv1.2"; private static String[] getGCMCiphers() { return new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" }; } private static String[] getCBCCiphers() { return new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" }; } // On Java 8, prefer CBC ciphers since AES-NI support is lacking and GCM is slower than CBC. private static final String[] DEFAULT_CIPHERS_JAVA8 = ObjectArrays.concat(getCBCCiphers(), getGCMCiphers(), String.class); // On Java 9 and later, prefer GCM ciphers due to improved AES-NI support. // Note that this performance assumption might not hold true for architectures other than x86_64. private static final String[] DEFAULT_CIPHERS_JAVA9 = ObjectArrays.concat(getGCMCiphers(), getCBCCiphers(), String.class); private X509Util() { // disabled } static String[] getDefaultCipherSuites() { return getDefaultCipherSuitesForJavaVersion(System.getProperty("java.specification.version")); } static String[] getDefaultCipherSuitesForJavaVersion(String javaVersion) { Objects.requireNonNull(javaVersion); if (javaVersion.matches("\\d+")) { // Must be Java 9 or later LOG.debug("Using Java9+ optimized cipher suites for Java version {}", javaVersion); return DEFAULT_CIPHERS_JAVA9; } else if (javaVersion.startsWith("1.")) { // Must be Java 1.8 or earlier LOG.debug("Using Java8 optimized cipher suites for Java version {}", javaVersion); return DEFAULT_CIPHERS_JAVA8; } else { LOG.debug("Could not parse java version {}, using Java8 optimized cipher suites", javaVersion); return DEFAULT_CIPHERS_JAVA8; } } public static SslContext createSslContextForClient(String keyStoreLocation, char[] keyStorePassword, String keyStoreType, String trustStoreLocation, char[] trustStorePassword, String trustStoreType, boolean sslCrlEnabled, boolean sslOcspEnabled, String enabledProtocols, String cipherSuites, String tlsConfigProtocols) throws X509Exception, IOException { SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); if (keyStoreLocation.isEmpty()) { LOG.warn("keyStoreLocation is not specified"); } else { sslContextBuilder .keyManager(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); } if (trustStoreLocation.isEmpty()) { LOG.warn("trustStoreLocation is not specified"); } else { sslContextBuilder.trustManager(createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, sslCrlEnabled, sslOcspEnabled)); } sslContextBuilder.enableOcsp(sslOcspEnabled); sslContextBuilder.protocols(getEnabledProtocols(enabledProtocols, tlsConfigProtocols)); sslContextBuilder.ciphers(Arrays.asList(getCipherSuites(cipherSuites))); return sslContextBuilder.build(); } public static SslContext createSslContextForServer(String keyStoreLocation, char[] keyStorePassword, String keyStoreType, String trustStoreLocation, char[] trustStorePassword, String trustStoreType, boolean sslCrlEnabled, boolean sslOcspEnabled, String enabledProtocols, String cipherSuites, String tlsConfigProtocols) throws X509Exception, IOException { if (keyStoreLocation.isEmpty()) { throw new SSLContextException( "keyStoreLocation is required for SSL server: "); } SslContextBuilder sslContextBuilder; sslContextBuilder = SslContextBuilder .forServer(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); if (trustStoreLocation.isEmpty()) { LOG.warn("trustStoreLocation is not specified"); } else { sslContextBuilder.trustManager(createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, sslCrlEnabled, sslOcspEnabled)); } sslContextBuilder.enableOcsp(sslOcspEnabled); sslContextBuilder.protocols(getEnabledProtocols(enabledProtocols, tlsConfigProtocols)); sslContextBuilder.ciphers(Arrays.asList(getCipherSuites(cipherSuites))); return sslContextBuilder.build(); } /** * Creates a key manager by loading the key store from the given file of the given type, * optionally decrypting it using the given password. * @param keyStoreLocation the location of the key store file. * @param keyStorePassword optional password to decrypt the key store. If empty, assumes the key * store is not encrypted. * @param keyStoreType must be JKS, PEM, PKCS12, BCFKS or null. If null, attempts to * autodetect the key store type from the file extension (e.g. .jks / * .pem). * @return the key manager. * @throws KeyManagerException if something goes wrong. */ static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStorePassword, String keyStoreType) throws KeyManagerException { if (keyStoreType == null) { keyStoreType = "jks"; } if (keyStorePassword == null) { keyStorePassword = EMPTY_CHAR_ARRAY; } try { KeyStore ks = KeyStore.getInstance(keyStoreType); try (InputStream inputStream = Files.newInputStream(new File(keyStoreLocation).toPath())) { ks.load(inputStream, keyStorePassword); } KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(ks, keyStorePassword); for (KeyManager km : kmf.getKeyManagers()) { if (km instanceof X509KeyManager) { return (X509KeyManager) km; } } throw new KeyManagerException("Couldn't find X509KeyManager"); } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { throw new KeyManagerException(e); } } /** * Creates a trust manager by loading the trust store from the given file of the given type, * optionally decrypting it using the given password. * @param trustStoreLocation the location of the trust store file. * @param trustStorePassword optional password to decrypt the trust store (only applies to JKS * trust stores). If empty, assumes the trust store is not encrypted. * @param trustStoreType must be JKS, PEM, PKCS12, BCFKS or null. If null, attempts to * autodetect the trust store type from the file extension (e.g. .jks / * .pem). * @param crlEnabled enable CRL (certificate revocation list) checks. * @param ocspEnabled enable OCSP (online certificate status protocol) checks. * @return the trust manager. * @throws TrustManagerException if something goes wrong. */ static X509TrustManager createTrustManager(String trustStoreLocation, char[] trustStorePassword, String trustStoreType, boolean crlEnabled, boolean ocspEnabled) throws TrustManagerException { if (trustStoreType == null) { trustStoreType = "jks"; } if (trustStorePassword == null) { trustStorePassword = EMPTY_CHAR_ARRAY; } try { KeyStore ts = KeyStore.getInstance(trustStoreType); try (InputStream inputStream = Files.newInputStream(new File(trustStoreLocation).toPath())) { ts.load(inputStream, trustStorePassword); } PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); if (crlEnabled || ocspEnabled) { pbParams.setRevocationEnabled(true); System.setProperty("com.sun.net.ssl.checkRevocation", "true"); if (crlEnabled) { System.setProperty("com.sun.security.enableCRLDP", "true"); } if (ocspEnabled) { Security.setProperty("ocsp.enable", "true"); } } else { pbParams.setRevocationEnabled(false); } // Revocation checking is only supported with the PKIX algorithm TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(new CertPathTrustManagerParameters(pbParams)); for (final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509ExtendedTrustManager) { return (X509ExtendedTrustManager) tm; } } throw new TrustManagerException("Couldn't find X509TrustManager"); } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { throw new TrustManagerException(e); } } private static String[] getEnabledProtocols(String enabledProtocolsInput, String tlsConfigProtocols) { if (enabledProtocolsInput == null) { return new String[] {tlsConfigProtocols}; } return enabledProtocolsInput.split(","); } private static String[] getCipherSuites(String cipherSuitesInput) { if (cipherSuitesInput == null) { return getDefaultCipherSuites(); } else { return cipherSuitesInput.split(","); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy