From c60f3b8d7ca0575bba15682a329dced786f898da Mon Sep 17 00:00:00 2001 From: Mat Booth Date: Mon, 7 Sep 2020 13:41:44 +0100 Subject: [PATCH 4/5] Remove optional dep tcnative --- handler/pom.xml | 6 - .../handler/ssl/CipherSuiteConverter.java | 494 ---- .../ssl/DefaultOpenSslKeyMaterial.java | 126 - .../java/io/netty/handler/ssl/OpenSsl.java | 610 ---- .../OpenSslCachingKeyMaterialProvider.java | 79 - .../OpenSslCachingX509KeyManagerFactory.java | 81 - .../ssl/OpenSslCertificateException.java | 81 - .../handler/ssl/OpenSslClientContext.java | 208 -- .../io/netty/handler/ssl/OpenSslContext.java | 58 - .../io/netty/handler/ssl/OpenSslEngine.java | 41 - .../netty/handler/ssl/OpenSslEngineMap.java | 35 - .../ssl/OpenSslKeyMaterialManager.java | 127 - .../ssl/OpenSslKeyMaterialProvider.java | 154 - .../netty/handler/ssl/OpenSslPrivateKey.java | 191 -- .../handler/ssl/OpenSslPrivateKeyMethod.java | 62 - .../handler/ssl/OpenSslServerContext.java | 367 --- .../ssl/OpenSslServerSessionContext.java | 124 - .../handler/ssl/OpenSslSessionContext.java | 158 -- .../handler/ssl/OpenSslSessionStats.java | 253 -- .../handler/ssl/OpenSslSessionTicketKey.java | 78 - ...OpenSslTlsv13X509ExtendedTrustManager.java | 240 -- .../ssl/OpenSslX509KeyManagerFactory.java | 413 --- .../ReferenceCountedOpenSslClientContext.java | 343 --- .../ssl/ReferenceCountedOpenSslContext.java | 968 ------- .../ssl/ReferenceCountedOpenSslEngine.java | 2467 ----------------- .../ReferenceCountedOpenSslServerContext.java | 286 -- .../java/io/netty/handler/ssl/SslContext.java | 30 +- .../java/io/netty/handler/ssl/SslHandler.java | 49 +- .../handler/ssl/SslMasterKeyHandler.java | 3 - .../io/netty/handler/ssl/SslProvider.java | 14 +- .../handler/ssl/ocsp/OcspClientHandler.java | 61 - .../netty/handler/ssl/ocsp/package-info.java | 23 - pom.xml | 10 - 33 files changed, 3 insertions(+), 8237 deletions(-) delete mode 100644 handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSsl.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java delete mode 100644 handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java diff --git a/handler/pom.xml b/handler/pom.xml index be8206dc89..e28cc6f7af 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -60,12 +60,6 @@ netty-codec ${project.version} - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.classifier} - true - org.bouncycastle bcpkix-jdk15on diff --git a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java deleted file mode 100644 index 94e951f7f5..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.util.Collections.singletonMap; - -/** - * Converts a Java cipher suite string to an OpenSSL cipher suite string and vice versa. - * - * @see Wikipedia page about cipher suite - */ -final class CipherSuiteConverter { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(CipherSuiteConverter.class); - - /** - * A_B_WITH_C_D, where: - * - * A - TLS or SSL (protocol) - * B - handshake algorithm (key exchange and authentication algorithms to be precise) - * C - bulk cipher - * D - HMAC algorithm - * - * This regular expression assumes that: - * - * 1) A is always TLS or SSL, and - * 2) D is always a single word. - */ - private static final Pattern JAVA_CIPHERSUITE_PATTERN = - Pattern.compile("^(?:TLS|SSL)_((?:(?!_WITH_).)+)_WITH_(.*)_(.*)$"); - - /** - * A-B-C, where: - * - * A - handshake algorithm (key exchange and authentication algorithms to be precise) - * B - bulk cipher - * C - HMAC algorithm - * - * This regular expression assumes that: - * - * 1) A has some deterministic pattern as shown below, and - * 2) C is always a single word - */ - private static final Pattern OPENSSL_CIPHERSUITE_PATTERN = - // Be very careful not to break the indentation while editing. - Pattern.compile( - "^(?:(" + // BEGIN handshake algorithm - "(?:(?:EXP-)?" + - "(?:" + - "(?:DHE|EDH|ECDH|ECDHE|SRP|RSA)-(?:DSS|RSA|ECDSA|PSK)|" + - "(?:ADH|AECDH|KRB5|PSK|SRP)" + - ')' + - ")|" + - "EXP" + - ")-)?" + // END handshake algorithm - "(.*)-(.*)$"); - - private static final Pattern JAVA_AES_CBC_PATTERN = Pattern.compile("^(AES)_([0-9]+)_CBC$"); - private static final Pattern JAVA_AES_PATTERN = Pattern.compile("^(AES)_([0-9]+)_(.*)$"); - private static final Pattern OPENSSL_AES_CBC_PATTERN = Pattern.compile("^(AES)([0-9]+)$"); - private static final Pattern OPENSSL_AES_PATTERN = Pattern.compile("^(AES)([0-9]+)-(.*)$"); - - /** - * Java-to-OpenSSL cipher suite conversion map - * Note that the Java cipher suite has the protocol prefix (TLS_, SSL_) - */ - private static final ConcurrentMap j2o = PlatformDependent.newConcurrentHashMap(); - - /** - * OpenSSL-to-Java cipher suite conversion map. - * Note that one OpenSSL cipher suite can be converted to more than one Java cipher suites because - * a Java cipher suite has the protocol name prefix (TLS_, SSL_) - */ - private static final ConcurrentMap> o2j = PlatformDependent.newConcurrentHashMap(); - - private static final Map j2oTls13; - private static final Map> o2jTls13; - - static { - Map j2oTls13Map = new HashMap(); - j2oTls13Map.put("TLS_AES_128_GCM_SHA256", "AEAD-AES128-GCM-SHA256"); - j2oTls13Map.put("TLS_AES_256_GCM_SHA384", "AEAD-AES256-GCM-SHA384"); - j2oTls13Map.put("TLS_CHACHA20_POLY1305_SHA256", "AEAD-CHACHA20-POLY1305-SHA256"); - j2oTls13 = Collections.unmodifiableMap(j2oTls13Map); - - Map> o2jTls13Map = new HashMap>(); - o2jTls13Map.put("TLS_AES_128_GCM_SHA256", singletonMap("TLS", "TLS_AES_128_GCM_SHA256")); - o2jTls13Map.put("TLS_AES_256_GCM_SHA384", singletonMap("TLS", "TLS_AES_256_GCM_SHA384")); - o2jTls13Map.put("TLS_CHACHA20_POLY1305_SHA256", singletonMap("TLS", "TLS_CHACHA20_POLY1305_SHA256")); - o2jTls13Map.put("AEAD-AES128-GCM-SHA256", singletonMap("TLS", "TLS_AES_128_GCM_SHA256")); - o2jTls13Map.put("AEAD-AES256-GCM-SHA384", singletonMap("TLS", "TLS_AES_256_GCM_SHA384")); - o2jTls13Map.put("AEAD-CHACHA20-POLY1305-SHA256", singletonMap("TLS", "TLS_CHACHA20_POLY1305_SHA256")); - o2jTls13 = Collections.unmodifiableMap(o2jTls13Map); - } - - /** - * Clears the cache for testing purpose. - */ - static void clearCache() { - j2o.clear(); - o2j.clear(); - } - - /** - * Tests if the specified key-value pair has been cached in Java-to-OpenSSL cache. - */ - static boolean isJ2OCached(String key, String value) { - return value.equals(j2o.get(key)); - } - - /** - * Tests if the specified key-value pair has been cached in OpenSSL-to-Java cache. - */ - static boolean isO2JCached(String key, String protocol, String value) { - Map p2j = o2j.get(key); - if (p2j == null) { - return false; - } else { - return value.equals(p2j.get(protocol)); - } - } - - /** - * Converts the specified Java cipher suite to its corresponding OpenSSL cipher suite name. - * - * @return {@code null} if the conversion has failed - */ - static String toOpenSsl(String javaCipherSuite, boolean boringSSL) { - String converted = j2o.get(javaCipherSuite); - if (converted != null) { - return converted; - } - return cacheFromJava(javaCipherSuite, boringSSL); - } - - private static String cacheFromJava(String javaCipherSuite, boolean boringSSL) { - String converted = j2oTls13.get(javaCipherSuite); - if (converted != null) { - return boringSSL ? converted : javaCipherSuite; - } - - String openSslCipherSuite = toOpenSslUncached(javaCipherSuite, boringSSL); - if (openSslCipherSuite == null) { - return null; - } - - // Cache the mapping. - j2o.putIfAbsent(javaCipherSuite, openSslCipherSuite); - - // Cache the reverse mapping after stripping the protocol prefix (TLS_ or SSL_) - final String javaCipherSuiteSuffix = javaCipherSuite.substring(4); - Map p2j = new HashMap(4); - p2j.put("", javaCipherSuiteSuffix); - p2j.put("SSL", "SSL_" + javaCipherSuiteSuffix); - p2j.put("TLS", "TLS_" + javaCipherSuiteSuffix); - o2j.put(openSslCipherSuite, p2j); - - logger.debug("Cipher suite mapping: {} => {}", javaCipherSuite, openSslCipherSuite); - - return openSslCipherSuite; - } - - static String toOpenSslUncached(String javaCipherSuite, boolean boringSSL) { - String converted = j2oTls13.get(javaCipherSuite); - if (converted != null) { - return boringSSL ? converted : javaCipherSuite; - } - - Matcher m = JAVA_CIPHERSUITE_PATTERN.matcher(javaCipherSuite); - if (!m.matches()) { - return null; - } - - String handshakeAlgo = toOpenSslHandshakeAlgo(m.group(1)); - String bulkCipher = toOpenSslBulkCipher(m.group(2)); - String hmacAlgo = toOpenSslHmacAlgo(m.group(3)); - if (handshakeAlgo.isEmpty()) { - return bulkCipher + '-' + hmacAlgo; - } else if (bulkCipher.contains("CHACHA20")) { - return handshakeAlgo + '-' + bulkCipher; - } else { - return handshakeAlgo + '-' + bulkCipher + '-' + hmacAlgo; - } - } - - private static String toOpenSslHandshakeAlgo(String handshakeAlgo) { - final boolean export = handshakeAlgo.endsWith("_EXPORT"); - if (export) { - handshakeAlgo = handshakeAlgo.substring(0, handshakeAlgo.length() - 7); - } - - if ("RSA".equals(handshakeAlgo)) { - handshakeAlgo = ""; - } else if (handshakeAlgo.endsWith("_anon")) { - handshakeAlgo = 'A' + handshakeAlgo.substring(0, handshakeAlgo.length() - 5); - } - - if (export) { - if (handshakeAlgo.isEmpty()) { - handshakeAlgo = "EXP"; - } else { - handshakeAlgo = "EXP-" + handshakeAlgo; - } - } - - return handshakeAlgo.replace('_', '-'); - } - - private static String toOpenSslBulkCipher(String bulkCipher) { - if (bulkCipher.startsWith("AES_")) { - Matcher m = JAVA_AES_CBC_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1$2"); - } - - m = JAVA_AES_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1$2-$3"); - } - } - - if ("3DES_EDE_CBC".equals(bulkCipher)) { - return "DES-CBC3"; - } - - if ("RC4_128".equals(bulkCipher) || "RC4_40".equals(bulkCipher)) { - return "RC4"; - } - - if ("DES40_CBC".equals(bulkCipher) || "DES_CBC_40".equals(bulkCipher)) { - return "DES-CBC"; - } - - if ("RC2_CBC_40".equals(bulkCipher)) { - return "RC2-CBC"; - } - - return bulkCipher.replace('_', '-'); - } - - private static String toOpenSslHmacAlgo(String hmacAlgo) { - // Java and OpenSSL use the same algorithm names for: - // - // * SHA - // * SHA256 - // * MD5 - // - return hmacAlgo; - } - - /** - * Convert from OpenSSL cipher suite name convention to java cipher suite name convention. - * @param openSslCipherSuite An OpenSSL cipher suite name. - * @param protocol The cryptographic protocol (i.e. SSL, TLS, ...). - * @return The translated cipher suite name according to java conventions. This will not be {@code null}. - */ - static String toJava(String openSslCipherSuite, String protocol) { - Map p2j = o2j.get(openSslCipherSuite); - if (p2j == null) { - p2j = cacheFromOpenSsl(openSslCipherSuite); - // This may happen if this method is queried when OpenSSL doesn't yet have a cipher setup. It will return - // "(NONE)" in this case. - if (p2j == null) { - return null; - } - } - - String javaCipherSuite = p2j.get(protocol); - if (javaCipherSuite == null) { - String cipher = p2j.get(""); - if (cipher == null) { - return null; - } - javaCipherSuite = protocol + '_' + cipher; - } - - return javaCipherSuite; - } - - private static Map cacheFromOpenSsl(String openSslCipherSuite) { - Map converted = o2jTls13.get(openSslCipherSuite); - if (converted != null) { - return converted; - } - - String javaCipherSuiteSuffix = toJavaUncached0(openSslCipherSuite, false); - if (javaCipherSuiteSuffix == null) { - return null; - } - - final String javaCipherSuiteSsl = "SSL_" + javaCipherSuiteSuffix; - final String javaCipherSuiteTls = "TLS_" + javaCipherSuiteSuffix; - - // Cache the mapping. - final Map p2j = new HashMap(4); - p2j.put("", javaCipherSuiteSuffix); - p2j.put("SSL", javaCipherSuiteSsl); - p2j.put("TLS", javaCipherSuiteTls); - o2j.putIfAbsent(openSslCipherSuite, p2j); - - // Cache the reverse mapping after adding the protocol prefix (TLS_ or SSL_) - j2o.putIfAbsent(javaCipherSuiteTls, openSslCipherSuite); - j2o.putIfAbsent(javaCipherSuiteSsl, openSslCipherSuite); - - logger.debug("Cipher suite mapping: {} => {}", javaCipherSuiteTls, openSslCipherSuite); - logger.debug("Cipher suite mapping: {} => {}", javaCipherSuiteSsl, openSslCipherSuite); - - return p2j; - } - - static String toJavaUncached(String openSslCipherSuite) { - return toJavaUncached0(openSslCipherSuite, true); - } - - private static String toJavaUncached0(String openSslCipherSuite, boolean checkTls13) { - if (checkTls13) { - Map converted = o2jTls13.get(openSslCipherSuite); - if (converted != null) { - return converted.get("TLS"); - } - } - - Matcher m = OPENSSL_CIPHERSUITE_PATTERN.matcher(openSslCipherSuite); - if (!m.matches()) { - return null; - } - - String handshakeAlgo = m.group(1); - final boolean export; - if (handshakeAlgo == null) { - handshakeAlgo = ""; - export = false; - } else if (handshakeAlgo.startsWith("EXP-")) { - handshakeAlgo = handshakeAlgo.substring(4); - export = true; - } else if ("EXP".equals(handshakeAlgo)) { - handshakeAlgo = ""; - export = true; - } else { - export = false; - } - - handshakeAlgo = toJavaHandshakeAlgo(handshakeAlgo, export); - String bulkCipher = toJavaBulkCipher(m.group(2), export); - String hmacAlgo = toJavaHmacAlgo(m.group(3)); - - String javaCipherSuite = handshakeAlgo + "_WITH_" + bulkCipher + '_' + hmacAlgo; - // For historical reasons the CHACHA20 ciphers do not follow OpenSSL's custom naming convention and omits the - // HMAC algorithm portion of the name. There is currently no way to derive this information because it is - // omitted from the OpenSSL cipher name, but they currently all use SHA256 for HMAC [1]. - // [1] https://www.openssl.org/docs/man1.1.0/apps/ciphers.html - return bulkCipher.contains("CHACHA20") ? javaCipherSuite + "_SHA256" : javaCipherSuite; - } - - private static String toJavaHandshakeAlgo(String handshakeAlgo, boolean export) { - if (handshakeAlgo.isEmpty()) { - handshakeAlgo = "RSA"; - } else if ("ADH".equals(handshakeAlgo)) { - handshakeAlgo = "DH_anon"; - } else if ("AECDH".equals(handshakeAlgo)) { - handshakeAlgo = "ECDH_anon"; - } - - handshakeAlgo = handshakeAlgo.replace('-', '_'); - if (export) { - return handshakeAlgo + "_EXPORT"; - } else { - return handshakeAlgo; - } - } - - private static String toJavaBulkCipher(String bulkCipher, boolean export) { - if (bulkCipher.startsWith("AES")) { - Matcher m = OPENSSL_AES_CBC_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1_$2_CBC"); - } - - m = OPENSSL_AES_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1_$2_$3"); - } - } - - if ("DES-CBC3".equals(bulkCipher)) { - return "3DES_EDE_CBC"; - } - - if ("RC4".equals(bulkCipher)) { - if (export) { - return "RC4_40"; - } else { - return "RC4_128"; - } - } - - if ("DES-CBC".equals(bulkCipher)) { - if (export) { - return "DES_CBC_40"; - } else { - return "DES_CBC"; - } - } - - if ("RC2-CBC".equals(bulkCipher)) { - if (export) { - return "RC2_CBC_40"; - } else { - return "RC2_CBC"; - } - } - - return bulkCipher.replace('-', '_'); - } - - private static String toJavaHmacAlgo(String hmacAlgo) { - // Java and OpenSSL use the same algorithm names for: - // - // * SHA - // * SHA256 - // * MD5 - // - return hmacAlgo; - } - - /** - * Convert the given ciphers if needed to OpenSSL format and append them to the correct {@link StringBuilder} - * depending on if its a TLSv1.3 cipher or not. If this methods returns without throwing an exception its - * guaranteed that at least one of the {@link StringBuilder}s contain some ciphers that can be used to configure - * OpenSSL. - */ - static void convertToCipherStrings(Iterable cipherSuites, StringBuilder cipherBuilder, - StringBuilder cipherTLSv13Builder, boolean boringSSL) { - for (String c: cipherSuites) { - if (c == null) { - break; - } - - String converted = toOpenSsl(c, boringSSL); - if (converted == null) { - converted = c; - } - - if (!OpenSsl.isCipherSuiteAvailable(converted)) { - throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')'); - } - - if (SslUtils.isTLSv13Cipher(converted) || SslUtils.isTLSv13Cipher(c)) { - cipherTLSv13Builder.append(converted); - cipherTLSv13Builder.append(':'); - } else { - cipherBuilder.append(converted); - cipherBuilder.append(':'); - } - } - - if (cipherBuilder.length() == 0 && cipherTLSv13Builder.length() == 0) { - throw new IllegalArgumentException("empty cipher suites"); - } - if (cipherBuilder.length() > 0) { - cipherBuilder.setLength(cipherBuilder.length() - 1); - } - if (cipherTLSv13Builder.length() > 0) { - cipherTLSv13Builder.setLength(cipherTLSv13Builder.length() - 1); - } - } - - private CipherSuiteConverter() { } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java deleted file mode 100644 index fcea5266f2..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; - -import java.security.cert.X509Certificate; - -final class DefaultOpenSslKeyMaterial extends AbstractReferenceCounted implements OpenSslKeyMaterial { - - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(DefaultOpenSslKeyMaterial.class); - private final ResourceLeakTracker leak; - private final X509Certificate[] x509CertificateChain; - private long chain; - private long privateKey; - - DefaultOpenSslKeyMaterial(long chain, long privateKey, X509Certificate[] x509CertificateChain) { - this.chain = chain; - this.privateKey = privateKey; - this.x509CertificateChain = x509CertificateChain; - leak = leakDetector.track(this); - } - - @Override - public X509Certificate[] certificateChain() { - return x509CertificateChain.clone(); - } - - @Override - public long certificateChainAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return chain; - } - - @Override - public long privateKeyAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return privateKey; - } - - @Override - protected void deallocate() { - SSL.freeX509Chain(chain); - chain = 0; - SSL.freePrivateKey(privateKey); - privateKey = 0; - if (leak != null) { - boolean closed = leak.close(this); - assert closed; - } - } - - @Override - public DefaultOpenSslKeyMaterial retain() { - if (leak != null) { - leak.record(); - } - super.retain(); - return this; - } - - @Override - public DefaultOpenSslKeyMaterial retain(int increment) { - if (leak != null) { - leak.record(); - } - super.retain(increment); - return this; - } - - @Override - public DefaultOpenSslKeyMaterial touch() { - if (leak != null) { - leak.record(); - } - super.touch(); - return this; - } - - @Override - public DefaultOpenSslKeyMaterial touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - return this; - } - - @Override - public boolean release() { - if (leak != null) { - leak.record(); - } - return super.release(); - } - - @Override - public boolean release(int decrement) { - if (leak != null) { - leak.record(); - } - return super.release(decrement); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java deleted file mode 100644 index d7d44cf3e7..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.Buffer; -import io.netty.internal.tcnative.Library; -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.NativeLibraryLoader; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.ByteArrayInputStream; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import static io.netty.handler.ssl.SslUtils.*; - -/** - * Tells if {@code netty-tcnative} and its OpenSSL support - * are available. - */ -public final class OpenSsl { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class); - private static final Throwable UNAVAILABILITY_CAUSE; - - static final List DEFAULT_CIPHERS; - static final Set AVAILABLE_CIPHER_SUITES; - private static final Set AVAILABLE_OPENSSL_CIPHER_SUITES; - private static final Set AVAILABLE_JAVA_CIPHER_SUITES; - private static final boolean SUPPORTS_KEYMANAGER_FACTORY; - private static final boolean USE_KEYMANAGER_FACTORY; - private static final boolean SUPPORTS_OCSP; - private static final boolean TLSV13_SUPPORTED; - private static final boolean IS_BORINGSSL; - static final Set SUPPORTED_PROTOCOLS_SET; - static final String[] EXTRA_SUPPORTED_TLS_1_3_CIPHERS; - - // self-signed certificate for netty.io and the matching private-key - private static final String CERT = "-----BEGIN CERTIFICATE-----\n" + - "MIICrjCCAZagAwIBAgIIdSvQPv1QAZQwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAxMLZXhhbXBs\n" + - "ZS5jb20wIBcNMTgwNDA2MjIwNjU5WhgPOTk5OTEyMzEyMzU5NTlaMBYxFDASBgNVBAMTC2V4YW1w\n" + - "bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAggbWsmDQ6zNzRZ5AW8E3eoGl\n" + - "qWvOBDb5Fs1oBRrVQHuYmVAoaqwDzXYJ0LOwa293AgWEQ1jpcbZ2hpoYQzqEZBTLnFhMrhRFlH6K\n" + - "bJND8Y33kZ/iSVBBDuGbdSbJShlM+4WwQ9IAso4MZ4vW3S1iv5fGGpLgbtXRmBf/RU8omN0Gijlv\n" + - "WlLWHWijLN8xQtySFuBQ7ssW8RcKAary3pUm6UUQB+Co6lnfti0Tzag8PgjhAJq2Z3wbsGRnP2YS\n" + - "vYoaK6qzmHXRYlp/PxrjBAZAmkLJs4YTm/XFF+fkeYx4i9zqHbyone5yerRibsHaXZWLnUL+rFoe\n" + - "MdKvr0VS3sGmhQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQADQi441pKmXf9FvUV5EHU4v8nJT9Iq\n" + - "yqwsKwXnr7AsUlDGHBD7jGrjAXnG5rGxuNKBQ35wRxJATKrUtyaquFUL6H8O6aGQehiFTk6zmPbe\n" + - "12Gu44vqqTgIUxnv3JQJiox8S2hMxsSddpeCmSdvmalvD6WG4NthH6B9ZaBEiep1+0s0RUaBYn73\n" + - "I7CCUaAtbjfR6pcJjrFk5ei7uwdQZFSJtkP2z8r7zfeANJddAKFlkaMWn7u+OIVuB4XPooWicObk\n" + - "NAHFtP65bocUYnDpTVdiyvn8DdqyZ/EO8n1bBKBzuSLplk2msW4pdgaFgY7Vw/0wzcFXfUXmL1uy\n" + - "G8sQD/wx\n" + - "-----END CERTIFICATE-----"; - - private static final String KEY = "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCCBtayYNDrM3NFnkBbwTd6gaWp\n" + - "a84ENvkWzWgFGtVAe5iZUChqrAPNdgnQs7Brb3cCBYRDWOlxtnaGmhhDOoRkFMucWEyuFEWUfops\n" + - "k0PxjfeRn+JJUEEO4Zt1JslKGUz7hbBD0gCyjgxni9bdLWK/l8YakuBu1dGYF/9FTyiY3QaKOW9a\n" + - "UtYdaKMs3zFC3JIW4FDuyxbxFwoBqvLelSbpRRAH4KjqWd+2LRPNqDw+COEAmrZnfBuwZGc/ZhK9\n" + - "ihorqrOYddFiWn8/GuMEBkCaQsmzhhOb9cUX5+R5jHiL3OodvKid7nJ6tGJuwdpdlYudQv6sWh4x\n" + - "0q+vRVLewaaFAgMBAAECggEAP8tPJvFtTxhNJAkCloHz0D0vpDHqQBMgntlkgayqmBqLwhyb18pR\n" + - "i0qwgh7HHc7wWqOOQuSqlEnrWRrdcI6TSe8R/sErzfTQNoznKWIPYcI/hskk4sdnQ//Yn9/Jvnsv\n" + - "U/BBjOTJxtD+sQbhAl80JcA3R+5sArURQkfzzHOL/YMqzAsn5hTzp7HZCxUqBk3KaHRxV7NefeOE\n" + - "xlZuWSmxYWfbFIs4kx19/1t7h8CHQWezw+G60G2VBtSBBxDnhBWvqG6R/wpzJ3nEhPLLY9T+XIHe\n" + - "ipzdMOOOUZorfIg7M+pyYPji+ZIZxIpY5OjrOzXHciAjRtr5Y7l99K1CG1LguQKBgQDrQfIMxxtZ\n" + - "vxU/1cRmUV9l7pt5bjV5R6byXq178LxPKVYNjdZ840Q0/OpZEVqaT1xKVi35ohP1QfNjxPLlHD+K\n" + - "iDAR9z6zkwjIrbwPCnb5kuXy4lpwPcmmmkva25fI7qlpHtbcuQdoBdCfr/KkKaUCMPyY89LCXgEw\n" + - "5KTDj64UywKBgQCNfbO+eZLGzhiHhtNJurresCsIGWlInv322gL8CSfBMYl6eNfUTZvUDdFhPISL\n" + - "UljKWzXDrjw0ujFSPR0XhUGtiq89H+HUTuPPYv25gVXO+HTgBFZEPl4PpA+BUsSVZy0NddneyqLk\n" + - "42Wey9omY9Q8WsdNQS5cbUvy0uG6WFoX7wKBgQDZ1jpW8pa0x2bZsQsm4vo+3G5CRnZlUp+XlWt2\n" + - "dDcp5dC0xD1zbs1dc0NcLeGDOTDv9FSl7hok42iHXXq8AygjEm/QcuwwQ1nC2HxmQP5holAiUs4D\n" + - "WHM8PWs3wFYPzE459EBoKTxeaeP/uWAn+he8q7d5uWvSZlEcANs/6e77eQKBgD21Ar0hfFfj7mK8\n" + - "9E0FeRZBsqK3omkfnhcYgZC11Xa2SgT1yvs2Va2n0RcdM5kncr3eBZav2GYOhhAdwyBM55XuE/sO\n" + - "eokDVutNeuZ6d5fqV96TRaRBpvgfTvvRwxZ9hvKF4Vz+9wfn/JvCwANaKmegF6ejs7pvmF3whq2k\n" + - "drZVAoGAX5YxQ5XMTD0QbMAl7/6qp6S58xNoVdfCkmkj1ZLKaHKIjS/benkKGlySVQVPexPfnkZx\n" + - "p/Vv9yyphBoudiTBS9Uog66ueLYZqpgxlM/6OhYg86Gm3U2ycvMxYjBM1NFiyze21AqAhI+HX+Ot\n" + - "mraV2/guSgDgZAhukRZzeQ2RucI=\n" + - "-----END PRIVATE KEY-----"; - - static { - Throwable cause = null; - - if (SystemPropertyUtil.getBoolean("io.netty.handler.ssl.noOpenSsl", false)) { - cause = new UnsupportedOperationException( - "OpenSSL was explicit disabled with -Dio.netty.handler.ssl.noOpenSsl=true"); - - logger.debug( - "netty-tcnative explicit disabled; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable.", cause); - } else { - // Test if netty-tcnative is in the classpath first. - try { - Class.forName("io.netty.internal.tcnative.SSLContext", false, OpenSsl.class.getClassLoader()); - } catch (ClassNotFoundException t) { - cause = t; - logger.debug( - "netty-tcnative not in the classpath; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable."); - } - - // If in the classpath, try to load the native library and initialize netty-tcnative. - if (cause == null) { - try { - // The JNI library was not already loaded. Load it now. - loadTcNative(); - } catch (Throwable t) { - cause = t; - logger.debug( - "Failed to load netty-tcnative; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable, unless the " + - "application has already loaded the symbols by some other means. " + - "See https://netty.io/wiki/forked-tomcat-native.html for more information.", t); - } - - try { - String engine = SystemPropertyUtil.get("io.netty.handler.ssl.openssl.engine", null); - if (engine == null) { - logger.debug("Initialize netty-tcnative using engine: 'default'"); - } else { - logger.debug("Initialize netty-tcnative using engine: '{}'", engine); - } - initializeTcNative(engine); - - // The library was initialized successfully. If loading the library failed above, - // reset the cause now since it appears that the library was loaded by some other - // means. - cause = null; - } catch (Throwable t) { - if (cause == null) { - cause = t; - } - logger.debug( - "Failed to initialize netty-tcnative; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable. " + - "See https://netty.io/wiki/forked-tomcat-native.html for more information.", t); - } - } - } - - UNAVAILABILITY_CAUSE = cause; - - if (cause == null) { - logger.debug("netty-tcnative using native library: {}", SSL.versionString()); - - final List defaultCiphers = new ArrayList(); - final Set availableOpenSslCipherSuites = new LinkedHashSet(128); - boolean supportsKeyManagerFactory = false; - boolean useKeyManagerFactory = false; - boolean tlsv13Supported = false; - - IS_BORINGSSL = "BoringSSL".equals(versionString()); - if (IS_BORINGSSL) { - EXTRA_SUPPORTED_TLS_1_3_CIPHERS = new String [] { "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384" , - "TLS_CHACHA20_POLY1305_SHA256" }; - } else { - EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS; - } - - try { - final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); - long certBio = 0; - long keyBio = 0; - long cert = 0; - long key = 0; - try { - try { - StringBuilder tlsv13Ciphers = new StringBuilder(); - - for (String cipher: TLSV13_CIPHERS) { - String converted = CipherSuiteConverter.toOpenSsl(cipher, IS_BORINGSSL); - if (converted != null) { - tlsv13Ciphers.append(converted).append(':'); - } - } - if (tlsv13Ciphers.length() == 0) { - tlsv13Supported = false; - } else { - tlsv13Ciphers.setLength(tlsv13Ciphers.length() - 1); - SSLContext.setCipherSuite(sslCtx, tlsv13Ciphers.toString() , true); - tlsv13Supported = true; - } - - } catch (Exception ignore) { - tlsv13Supported = false; - } - - SSLContext.setCipherSuite(sslCtx, "ALL", false); - - final long ssl = SSL.newSSL(sslCtx, true); - try { - for (String c: SSL.getCiphers(ssl)) { - // Filter out bad input. - if (c == null || c.isEmpty() || availableOpenSslCipherSuites.contains(c) || - // Filter out TLSv1.3 ciphers if not supported. - !tlsv13Supported && isTLSv13Cipher(c)) { - continue; - } - availableOpenSslCipherSuites.add(c); - } - if (IS_BORINGSSL) { - // Currently BoringSSL does not include these when calling SSL.getCiphers() even when these - // are supported. - Collections.addAll(availableOpenSslCipherSuites, EXTRA_SUPPORTED_TLS_1_3_CIPHERS); - Collections.addAll(availableOpenSslCipherSuites, - "AEAD-AES128-GCM-SHA256", - "AEAD-AES256-GCM-SHA384", - "AEAD-CHACHA20-POLY1305-SHA256"); - } - - PemEncoded privateKey = PemPrivateKey.valueOf(KEY.getBytes(CharsetUtil.US_ASCII)); - try { - // Let's check if we can set a callback, which may not work if the used OpenSSL version - // is to old. - SSLContext.setCertificateCallback(sslCtx, null); - - X509Certificate certificate = selfSignedCertificate(); - certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, certificate); - cert = SSL.parseX509Chain(certBio); - - keyBio = ReferenceCountedOpenSslContext.toBIO( - UnpooledByteBufAllocator.DEFAULT, privateKey.retain()); - key = SSL.parsePrivateKey(keyBio, null); - - SSL.setKeyMaterial(ssl, cert, key); - supportsKeyManagerFactory = true; - try { - boolean propertySet = SystemPropertyUtil.contains( - "io.netty.handler.ssl.openssl.useKeyManagerFactory"); - if (!IS_BORINGSSL) { - useKeyManagerFactory = SystemPropertyUtil.getBoolean( - "io.netty.handler.ssl.openssl.useKeyManagerFactory", true); - - if (propertySet) { - logger.info("System property " + - "'io.netty.handler.ssl.openssl.useKeyManagerFactory'" + - " is deprecated and so will be ignored in the future"); - } - } else { - useKeyManagerFactory = true; - if (propertySet) { - logger.info("System property " + - "'io.netty.handler.ssl.openssl.useKeyManagerFactory'" + - " is deprecated and will be ignored when using BoringSSL"); - } - } - } catch (Throwable ignore) { - logger.debug("Failed to get useKeyManagerFactory system property."); - } - } catch (Error ignore) { - logger.debug("KeyManagerFactory not supported."); - } finally { - privateKey.release(); - } - } finally { - SSL.freeSSL(ssl); - if (certBio != 0) { - SSL.freeBIO(certBio); - } - if (keyBio != 0) { - SSL.freeBIO(keyBio); - } - if (cert != 0) { - SSL.freeX509Chain(cert); - } - if (key != 0) { - SSL.freePrivateKey(key); - } - } - } finally { - SSLContext.free(sslCtx); - } - } catch (Exception e) { - logger.warn("Failed to get the list of available OpenSSL cipher suites.", e); - } - AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.unmodifiableSet(availableOpenSslCipherSuites); - final Set availableJavaCipherSuites = new LinkedHashSet( - AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2); - for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) { - // Included converted but also openssl cipher name - if (!isTLSv13Cipher(cipher)) { - availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS")); - availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL")); - } else { - // TLSv1.3 ciphers have the correct format. - availableJavaCipherSuites.add(cipher); - } - } - - addIfSupported(availableJavaCipherSuites, defaultCiphers, DEFAULT_CIPHER_SUITES); - addIfSupported(availableJavaCipherSuites, defaultCiphers, TLSV13_CIPHER_SUITES); - - useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, availableJavaCipherSuites); - DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers); - - AVAILABLE_JAVA_CIPHER_SUITES = Collections.unmodifiableSet(availableJavaCipherSuites); - - final Set availableCipherSuites = new LinkedHashSet( - AVAILABLE_OPENSSL_CIPHER_SUITES.size() + AVAILABLE_JAVA_CIPHER_SUITES.size()); - availableCipherSuites.addAll(AVAILABLE_OPENSSL_CIPHER_SUITES); - availableCipherSuites.addAll(AVAILABLE_JAVA_CIPHER_SUITES); - - AVAILABLE_CIPHER_SUITES = availableCipherSuites; - SUPPORTS_KEYMANAGER_FACTORY = supportsKeyManagerFactory; - USE_KEYMANAGER_FACTORY = useKeyManagerFactory; - - Set protocols = new LinkedHashSet(6); - // Seems like there is no way to explicitly disable SSLv2Hello in openssl so it is always enabled - protocols.add(PROTOCOL_SSL_V2_HELLO); - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2, SSL.SSL_OP_NO_SSLv2)) { - protocols.add(PROTOCOL_SSL_V2); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3, SSL.SSL_OP_NO_SSLv3)) { - protocols.add(PROTOCOL_SSL_V3); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_OP_NO_TLSv1)) { - protocols.add(PROTOCOL_TLS_V1); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1, SSL.SSL_OP_NO_TLSv1_1)) { - protocols.add(PROTOCOL_TLS_V1_1); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_OP_NO_TLSv1_2)) { - protocols.add(PROTOCOL_TLS_V1_2); - } - - // This is only supported by java11 and later. - if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3)) { - protocols.add(PROTOCOL_TLS_V1_3); - TLSV13_SUPPORTED = true; - } else { - TLSV13_SUPPORTED = false; - } - - SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols); - SUPPORTS_OCSP = doesSupportOcsp(); - - if (logger.isDebugEnabled()) { - logger.debug("Supported protocols (OpenSSL): {} ", SUPPORTED_PROTOCOLS_SET); - logger.debug("Default cipher suites (OpenSSL): {}", DEFAULT_CIPHERS); - } - } else { - DEFAULT_CIPHERS = Collections.emptyList(); - AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet(); - AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet(); - AVAILABLE_CIPHER_SUITES = Collections.emptySet(); - SUPPORTS_KEYMANAGER_FACTORY = false; - USE_KEYMANAGER_FACTORY = false; - SUPPORTED_PROTOCOLS_SET = Collections.emptySet(); - SUPPORTS_OCSP = false; - TLSV13_SUPPORTED = false; - IS_BORINGSSL = false; - EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS; - } - } - - /** - * Returns a self-signed {@link X509Certificate} for {@code netty.io}. - */ - static X509Certificate selfSignedCertificate() throws CertificateException { - return (X509Certificate) SslContext.X509_CERT_FACTORY.generateCertificate( - new ByteArrayInputStream(CERT.getBytes(CharsetUtil.US_ASCII)) - ); - } - - private static boolean doesSupportOcsp() { - boolean supportsOcsp = false; - if (version() >= 0x10002000L) { - long sslCtx = -1; - try { - sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_MODE_SERVER); - SSLContext.enableOcsp(sslCtx, false); - supportsOcsp = true; - } catch (Exception ignore) { - // ignore - } finally { - if (sslCtx != -1) { - SSLContext.free(sslCtx); - } - } - } - return supportsOcsp; - } - private static boolean doesSupportProtocol(int protocol, int opt) { - if (opt == 0) { - // If the opt is 0 the protocol is not supported. This is for example the case with BoringSSL and SSLv2. - return false; - } - long sslCtx = -1; - try { - sslCtx = SSLContext.make(protocol, SSL.SSL_MODE_COMBINED); - return true; - } catch (Exception ignore) { - return false; - } finally { - if (sslCtx != -1) { - SSLContext.free(sslCtx); - } - } - } - - /** - * Returns {@code true} if and only if - * {@code netty-tcnative} and its OpenSSL support - * are available. - */ - public static boolean isAvailable() { - return UNAVAILABILITY_CAUSE == null; - } - - /** - * Returns {@code true} if the used version of openssl supports - * ALPN. - * - * @deprecated use {@link SslProvider#isAlpnSupported(SslProvider)} with {@link SslProvider#OPENSSL}. - */ - @Deprecated - public static boolean isAlpnSupported() { - return version() >= 0x10002000L; - } - - /** - * Returns {@code true} if the used version of OpenSSL supports OCSP stapling. - */ - public static boolean isOcspSupported() { - return SUPPORTS_OCSP; - } - - /** - * Returns the version of the used available OpenSSL library or {@code -1} if {@link #isAvailable()} - * returns {@code false}. - */ - public static int version() { - return isAvailable() ? SSL.version() : -1; - } - - /** - * Returns the version string of the used available OpenSSL library or {@code null} if {@link #isAvailable()} - * returns {@code false}. - */ - public static String versionString() { - return isAvailable() ? SSL.versionString() : null; - } - - /** - * Ensure that {@code netty-tcnative} and - * its OpenSSL support are available. - * - * @throws UnsatisfiedLinkError if unavailable - */ - public static void ensureAvailability() { - if (UNAVAILABILITY_CAUSE != null) { - throw (Error) new UnsatisfiedLinkError( - "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE); - } - } - - /** - * Returns the cause of unavailability of - * {@code netty-tcnative} and its OpenSSL support. - * - * @return the cause if unavailable. {@code null} if available. - */ - public static Throwable unavailabilityCause() { - return UNAVAILABILITY_CAUSE; - } - - /** - * @deprecated use {@link #availableOpenSslCipherSuites()} - */ - @Deprecated - public static Set availableCipherSuites() { - return availableOpenSslCipherSuites(); - } - - /** - * Returns all the available OpenSSL cipher suites. - * Please note that the returned array may include the cipher suites that are insecure or non-functional. - */ - public static Set availableOpenSslCipherSuites() { - return AVAILABLE_OPENSSL_CIPHER_SUITES; - } - - /** - * Returns all the available cipher suites (Java-style). - * Please note that the returned array may include the cipher suites that are insecure or non-functional. - */ - public static Set availableJavaCipherSuites() { - return AVAILABLE_JAVA_CIPHER_SUITES; - } - - /** - * Returns {@code true} if and only if the specified cipher suite is available in OpenSSL. - * Both Java-style cipher suite and OpenSSL-style cipher suite are accepted. - */ - public static boolean isCipherSuiteAvailable(String cipherSuite) { - String converted = CipherSuiteConverter.toOpenSsl(cipherSuite, IS_BORINGSSL); - if (converted != null) { - cipherSuite = converted; - } - return AVAILABLE_OPENSSL_CIPHER_SUITES.contains(cipherSuite); - } - - /** - * Returns {@code true} if {@link javax.net.ssl.KeyManagerFactory} is supported when using OpenSSL. - */ - public static boolean supportsKeyManagerFactory() { - return SUPPORTS_KEYMANAGER_FACTORY; - } - - /** - * Always returns {@code true} if {@link #isAvailable()} returns {@code true}. - * - * @deprecated Will be removed because hostname validation is always done by a - * {@link javax.net.ssl.TrustManager} implementation. - */ - @Deprecated - public static boolean supportsHostnameValidation() { - return isAvailable(); - } - - static boolean useKeyManagerFactory() { - return USE_KEYMANAGER_FACTORY; - } - - static long memoryAddress(ByteBuf buf) { - assert buf.isDirect(); - return buf.hasMemoryAddress() ? buf.memoryAddress() : Buffer.address(buf.nioBuffer()); - } - - private OpenSsl() { } - - private static void loadTcNative() throws Exception { - String os = PlatformDependent.normalizedOs(); - String arch = PlatformDependent.normalizedArch(); - - Set libNames = new LinkedHashSet(5); - String staticLibName = "netty_tcnative"; - - // First, try loading the platform-specific library. Platform-specific - // libraries will be available if using a tcnative uber jar. - if ("linux".equalsIgnoreCase(os)) { - Set classifiers = PlatformDependent.normalizedLinuxClassifiers(); - for (String classifier : classifiers) { - libNames.add(staticLibName + "_" + os + '_' + arch + "_" + classifier); - } - // generic arch-dependent library - libNames.add(staticLibName + "_" + os + '_' + arch); - - // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0). - // note: should already be included from the classifiers but if not, we use this as an - // additional fallback option here - libNames.add(staticLibName + "_" + os + '_' + arch + "_fedora"); - } else { - libNames.add(staticLibName + "_" + os + '_' + arch); - } - libNames.add(staticLibName + "_" + arch); - libNames.add(staticLibName); - - NativeLibraryLoader.loadFirstAvailable(SSLContext.class.getClassLoader(), - libNames.toArray(new String[0])); - } - - private static boolean initializeTcNative(String engine) throws Exception { - return Library.initialize("provided", engine); - } - - static void releaseIfNeeded(ReferenceCounted counted) { - if (counted.refCnt() > 0) { - ReferenceCountUtil.safeRelease(counted); - } - } - - static boolean isTlsv13Supported() { - return TLSV13_SUPPORTED; - } - - static boolean isBoringSSL() { - return IS_BORINGSSL; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java deleted file mode 100644 index 07b67d9fa7..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; - -import javax.net.ssl.X509KeyManager; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * {@link OpenSslKeyMaterialProvider} that will cache the {@link OpenSslKeyMaterial} to reduce the overhead - * of parsing the chain and the key for generation of the material. - */ -final class OpenSslCachingKeyMaterialProvider extends OpenSslKeyMaterialProvider { - - private final int maxCachedEntries; - private volatile boolean full; - private final ConcurrentMap cache = new ConcurrentHashMap(); - - OpenSslCachingKeyMaterialProvider(X509KeyManager keyManager, String password, int maxCachedEntries) { - super(keyManager, password); - this.maxCachedEntries = maxCachedEntries; - } - - @Override - OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { - OpenSslKeyMaterial material = cache.get(alias); - if (material == null) { - material = super.chooseKeyMaterial(allocator, alias); - if (material == null) { - // No keymaterial should be used. - return null; - } - - if (full) { - return material; - } - if (cache.size() > maxCachedEntries) { - full = true; - // Do not cache... - return material; - } - OpenSslKeyMaterial old = cache.putIfAbsent(alias, material); - if (old != null) { - material.release(); - material = old; - } - } - // We need to call retain() as we want to always have at least a refCnt() of 1 before destroy() was called. - return material.retain(); - } - - @Override - void destroy() { - // Remove and release all entries. - do { - Iterator iterator = cache.values().iterator(); - while (iterator.hasNext()) { - iterator.next().release(); - iterator.remove(); - } - } while (!cache.isEmpty()); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java deleted file mode 100644 index 7f67bc8198..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.util.internal.ObjectUtil; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.KeyManagerFactorySpi; -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509KeyManager; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.X509Certificate; - -/** - * Wraps another {@link KeyManagerFactory} and caches its chains / certs for an alias for better performance when using - * {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT}. - * - * Because of the caching its important that the wrapped {@link KeyManagerFactory}s {@link X509KeyManager}s always - * return the same {@link X509Certificate} chain and {@link PrivateKey} for the same alias. - */ -public final class OpenSslCachingX509KeyManagerFactory extends KeyManagerFactory { - - private final int maxCachedEntries; - - public OpenSslCachingX509KeyManagerFactory(final KeyManagerFactory factory) { - this(factory, 1024); - } - - public OpenSslCachingX509KeyManagerFactory(final KeyManagerFactory factory, int maxCachedEntries) { - super(new KeyManagerFactorySpi() { - @Override - protected void engineInit(KeyStore keyStore, char[] chars) - throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - factory.init(keyStore, chars); - } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) - throws InvalidAlgorithmParameterException { - factory.init(managerFactoryParameters); - } - - @Override - protected KeyManager[] engineGetKeyManagers() { - return factory.getKeyManagers(); - } - }, factory.getProvider(), factory.getAlgorithm()); - this.maxCachedEntries = ObjectUtil.checkPositive(maxCachedEntries, "maxCachedEntries"); - } - - OpenSslKeyMaterialProvider newProvider(String password) { - X509KeyManager keyManager = ReferenceCountedOpenSslContext.chooseX509KeyManager(getKeyManagers()); - if ("sun.security.ssl.X509KeyManagerImpl".equals(keyManager.getClass().getName())) { - // Don't do caching if X509KeyManagerImpl is used as the returned aliases are not stable and will change - // between invocations. - return new OpenSslKeyMaterialProvider(keyManager, password); - } - return new OpenSslCachingKeyMaterialProvider( - ReferenceCountedOpenSslContext.chooseX509KeyManager(getKeyManagers()), password, maxCachedEntries); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java deleted file mode 100644 index f20b2d3ba0..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.CertificateVerifier; - -import java.security.cert.CertificateException; - -/** - * A special {@link CertificateException} which allows to specify which error code is included in the - * SSL Record. This only work when {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} is used. - */ -public final class OpenSslCertificateException extends CertificateException { - private static final long serialVersionUID = 5542675253797129798L; - - private final int errorCode; - - /** - * Construct a new exception with the - * error code. - */ - public OpenSslCertificateException(int errorCode) { - this((String) null, errorCode); - } - - /** - * Construct a new exception with the msg and - * error code . - */ - public OpenSslCertificateException(String msg, int errorCode) { - super(msg); - this.errorCode = checkErrorCode(errorCode); - } - - /** - * Construct a new exception with the msg, cause and - * error code . - */ - public OpenSslCertificateException(String message, Throwable cause, int errorCode) { - super(message, cause); - this.errorCode = checkErrorCode(errorCode); - } - - /** - * Construct a new exception with the cause and - * error code . - */ - public OpenSslCertificateException(Throwable cause, int errorCode) { - this(null, cause, errorCode); - } - - /** - * Return the error code to use. - */ - public int errorCode() { - return errorCode; - } - - private static int checkErrorCode(int errorCode) { - // Call OpenSsl.isAvailable() to ensure we try to load the native lib as CertificateVerifier.isValid(...) - // will depend on it. If loading fails we will just skip the validation. - if (OpenSsl.isAvailable() && !CertificateVerifier.isValid(errorCode)) { - throw new IllegalArgumentException("errorCode '" + errorCode + - "' invalid, see https://www.openssl.org/docs/man1.0.2/apps/verify.html."); - } - return errorCode; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java deleted file mode 100644 index 7f9b39a8dd..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; - -import java.io.File; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; - -import static io.netty.handler.ssl.ReferenceCountedOpenSslClientContext.newSessionContext; - -/** - * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslClientContext}. - */ -public final class OpenSslClientContext extends OpenSslContext { - private final OpenSslSessionContext sessionContext; - - /** - * Creates a new instance. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext() throws SSLException { - this(null, null, null, null, null, null, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext(File certChainFile) throws SSLException { - this(certChainFile, null); - } - - /** - * Creates a new instance. - * - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException { - this(null, trustManagerFactory); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { - this(certChainFile, trustManagerFactory, null, null, null, null, null, - IdentityCipherSuiteFilter.INSTANCE, null, 0, 0); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default.. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers, - ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) - throws SSLException { - this(certChainFile, trustManagerFactory, null, null, null, null, ciphers, IdentityCipherSuiteFilter.INSTANCE, - apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default.. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(certChainFile, trustManagerFactory, null, null, null, null, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * @param trustCertCollectionFile an X.509 certificate collection file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default or the results of parsing - * {@code trustCertCollectionFile} - * @param keyCertChainFile an X.509 certificate chain file in PEM format. - * This provides the public key for mutual authentication. - * {@code null} to use the system default - * @param keyFile a PKCS#8 private key file in PEM format. - * This provides the private key for mutual authentication. - * {@code null} for no mutual authentication. - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * Ignored if {@code keyFile} is {@code null}. - * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link javax.net.ssl.KeyManager}s - * that is used to encrypt data being sent to servers. - * {@code null} to use the default or the results of parsing - * {@code keyCertChainFile} and {@code keyFile}. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Application Protocol Negotiator object. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslClientContext(File trustCertCollectionFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, - KeyManagerFactory keyManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) - throws SSLException { - this(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory, - toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword), - keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, null, sessionCacheSize, - sessionTimeout, false, KeyStore.getDefaultType()); - } - - OpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, - KeyManagerFactory keyManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols, - long sessionCacheSize, long sessionTimeout, boolean enableOcsp, String keyStore) - throws SSLException { - super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain, - ClientAuth.NONE, protocols, false, enableOcsp); - boolean success = false; - try { - OpenSslKeyMaterialProvider.validateKeyMaterialSupported(keyCertChain, key, keyPassword); - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslSessionContext sessionContext() { - return sessionContext; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java deleted file mode 100644 index f18c0643fc..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; - -import java.security.cert.Certificate; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; - -/** - * This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslContext}. - */ -public abstract class OpenSslContext extends ReferenceCountedOpenSslContext { - OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg, - long sessionCacheSize, long sessionTimeout, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp) - throws SSLException { - super(ciphers, cipherFilter, apnCfg, sessionCacheSize, sessionTimeout, mode, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, false); - } - - OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, - OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize, - long sessionTimeout, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp) throws SSLException { - super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, mode, keyCertChain, clientAuth, protocols, - startTls, enableOcsp, false); - } - - @Override - final SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) { - return new OpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode); - } - - @Override - @SuppressWarnings("FinalizeDeclaration") - protected final void finalize() throws Throwable { - super.finalize(); - OpenSsl.releaseIfNeeded(this); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java deleted file mode 100644 index a700dabf39..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; - -import javax.net.ssl.SSLEngine; - -/** - * Implements a {@link SSLEngine} using - * OpenSSL BIO abstractions. - *

- * This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslEngine}. - */ -public final class OpenSslEngine extends ReferenceCountedOpenSslEngine { - OpenSslEngine(OpenSslContext context, ByteBufAllocator alloc, String peerHost, int peerPort, - boolean jdkCompatibilityMode) { - super(context, alloc, peerHost, peerPort, jdkCompatibilityMode, false); - } - - @Override - @SuppressWarnings("FinalizeDeclaration") - protected void finalize() throws Throwable { - super.finalize(); - OpenSsl.releaseIfNeeded(this); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java deleted file mode 100644 index 02131b4b26..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -interface OpenSslEngineMap { - - /** - * Remove the {@link OpenSslEngine} with the given {@code ssl} address and - * return it. - */ - ReferenceCountedOpenSslEngine remove(long ssl); - - /** - * Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}. - */ - void add(ReferenceCountedOpenSslEngine engine); - - /** - * Get the {@link OpenSslEngine} for the given {@code ssl} address. - */ - ReferenceCountedOpenSslEngine get(long ssl); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java deleted file mode 100644 index 7acbf70cfe..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import javax.net.ssl.SSLException; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509KeyManager; -import javax.security.auth.x500.X500Principal; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - - -/** - * Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and - * {@link X509Certificate}s. - */ -final class OpenSslKeyMaterialManager { - - // Code in this class is inspired by code of conscrypts: - // - https://android.googlesource.com/platform/external/ - // conscrypt/+/master/src/main/java/org/conscrypt/OpenSSLEngineImpl.java - // - https://android.googlesource.com/platform/external/ - // conscrypt/+/master/src/main/java/org/conscrypt/SSLParametersImpl.java - // - static final String KEY_TYPE_RSA = "RSA"; - static final String KEY_TYPE_DH_RSA = "DH_RSA"; - static final String KEY_TYPE_EC = "EC"; - static final String KEY_TYPE_EC_EC = "EC_EC"; - static final String KEY_TYPE_EC_RSA = "EC_RSA"; - - // key type mappings for types. - private static final Map KEY_TYPES = new HashMap(); - static { - KEY_TYPES.put("RSA", KEY_TYPE_RSA); - KEY_TYPES.put("DHE_RSA", KEY_TYPE_RSA); - KEY_TYPES.put("ECDHE_RSA", KEY_TYPE_RSA); - KEY_TYPES.put("ECDHE_ECDSA", KEY_TYPE_EC); - KEY_TYPES.put("ECDH_RSA", KEY_TYPE_EC_RSA); - KEY_TYPES.put("ECDH_ECDSA", KEY_TYPE_EC_EC); - KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA); - } - - private final OpenSslKeyMaterialProvider provider; - - OpenSslKeyMaterialManager(OpenSslKeyMaterialProvider provider) { - this.provider = provider; - } - - void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLException { - String[] authMethods = engine.authMethods(); - if (authMethods.length == 0) { - return; - } - Set aliases = new HashSet(authMethods.length); - for (String authMethod : authMethods) { - String type = KEY_TYPES.get(authMethod); - if (type != null) { - String alias = chooseServerAlias(engine, type); - if (alias != null && aliases.add(alias)) { - if (!setKeyMaterial(engine, alias)) { - return; - } - } - } - } - } - - void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, String[] keyTypes, - X500Principal[] issuer) throws SSLException { - String alias = chooseClientAlias(engine, keyTypes, issuer); - // Only try to set the keymaterial if we have a match. This is also consistent with what OpenJDK does: - // http://hg.openjdk.java.net/jdk/jdk11/file/76072a077ee1/ - // src/java.base/share/classes/sun/security/ssl/CertificateRequest.java#l362 - if (alias != null) { - setKeyMaterial(engine, alias); - } - } - - private boolean setKeyMaterial(ReferenceCountedOpenSslEngine engine, String alias) throws SSLException { - OpenSslKeyMaterial keyMaterial = null; - try { - keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); - return keyMaterial == null || engine.setKeyMaterial(keyMaterial); - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException(e); - } finally { - if (keyMaterial != null) { - keyMaterial.release(); - } - } - } - private String chooseClientAlias(ReferenceCountedOpenSslEngine engine, - String[] keyTypes, X500Principal[] issuer) { - X509KeyManager manager = provider.keyManager(); - if (manager instanceof X509ExtendedKeyManager) { - return ((X509ExtendedKeyManager) manager).chooseEngineClientAlias(keyTypes, issuer, engine); - } - return manager.chooseClientAlias(keyTypes, issuer, null); - } - - private String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) { - X509KeyManager manager = provider.keyManager(); - if (manager instanceof X509ExtendedKeyManager) { - return ((X509ExtendedKeyManager) manager).chooseEngineServerAlias(type, null, engine); - } - return manager.chooseServerAlias(type, null, null); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java deleted file mode 100644 index f931fcfdbb..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.SSL; - -import javax.net.ssl.SSLException; -import javax.net.ssl.X509KeyManager; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO; - -/** - * Provides {@link OpenSslKeyMaterial} for a given alias. - */ -class OpenSslKeyMaterialProvider { - - private final X509KeyManager keyManager; - private final String password; - - OpenSslKeyMaterialProvider(X509KeyManager keyManager, String password) { - this.keyManager = keyManager; - this.password = password; - } - - static void validateKeyMaterialSupported(X509Certificate[] keyCertChain, PrivateKey key, String keyPassword) - throws SSLException { - validateSupported(keyCertChain); - validateSupported(key, keyPassword); - } - - private static void validateSupported(PrivateKey key, String password) throws SSLException { - if (key == null) { - return; - } - - long pkeyBio = 0; - long pkey = 0; - - try { - pkeyBio = toBIO(UnpooledByteBufAllocator.DEFAULT, key); - pkey = SSL.parsePrivateKey(pkeyBio, password); - } catch (Exception e) { - throw new SSLException("PrivateKey type not supported " + key.getFormat(), e); - } finally { - SSL.freeBIO(pkeyBio); - if (pkey != 0) { - SSL.freePrivateKey(pkey); - } - } - } - - private static void validateSupported(X509Certificate[] certificates) throws SSLException { - if (certificates == null || certificates.length == 0) { - return; - } - - long chainBio = 0; - long chain = 0; - PemEncoded encoded = null; - try { - encoded = PemX509Certificate.toPEM(UnpooledByteBufAllocator.DEFAULT, true, certificates); - chainBio = toBIO(UnpooledByteBufAllocator.DEFAULT, encoded.retain()); - chain = SSL.parseX509Chain(chainBio); - } catch (Exception e) { - throw new SSLException("Certificate type not supported", e); - } finally { - SSL.freeBIO(chainBio); - if (chain != 0) { - SSL.freeX509Chain(chain); - } - if (encoded != null) { - encoded.release(); - } - } - } - - /** - * Returns the underlying {@link X509KeyManager} that is used. - */ - X509KeyManager keyManager() { - return keyManager; - } - - /** - * Returns the {@link OpenSslKeyMaterial} or {@code null} (if none) that should be used during the handshake by - * OpenSSL. - */ - OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { - X509Certificate[] certificates = keyManager.getCertificateChain(alias); - if (certificates == null || certificates.length == 0) { - return null; - } - - PrivateKey key = keyManager.getPrivateKey(alias); - PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates); - long chainBio = 0; - long pkeyBio = 0; - long chain = 0; - long pkey = 0; - try { - chainBio = toBIO(allocator, encoded.retain()); - chain = SSL.parseX509Chain(chainBio); - - OpenSslKeyMaterial keyMaterial; - if (key instanceof OpenSslPrivateKey) { - keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates); - } else { - pkeyBio = toBIO(allocator, key); - pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password); - keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates); - } - - // See the chain and pkey to 0 so we will not release it as the ownership was - // transferred to OpenSslKeyMaterial. - chain = 0; - pkey = 0; - return keyMaterial; - } finally { - SSL.freeBIO(chainBio); - SSL.freeBIO(pkeyBio); - if (chain != 0) { - SSL.freeX509Chain(chain); - } - if (pkey != 0) { - SSL.freePrivateKey(pkey); - } - encoded.release(); - } - } - - /** - * Will be invoked once the provider should be destroyed. - */ - void destroy() { - // NOOP. - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java deleted file mode 100644 index c2e4f108b7..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.internal.EmptyArrays; - -import javax.security.auth.Destroyable; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -final class OpenSslPrivateKey extends AbstractReferenceCounted implements PrivateKey { - - private long privateKeyAddress; - - OpenSslPrivateKey(long privateKeyAddress) { - this.privateKeyAddress = privateKeyAddress; - } - - @Override - public String getAlgorithm() { - return "unknown"; - } - - @Override - public String getFormat() { - // As we do not support encoding we should return null as stated in the javadocs of PrivateKey. - return null; - } - - @Override - public byte[] getEncoded() { - return null; - } - - private long privateKeyAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return privateKeyAddress; - } - - @Override - protected void deallocate() { - SSL.freePrivateKey(privateKeyAddress); - privateKeyAddress = 0; - } - - @Override - public OpenSslPrivateKey retain() { - super.retain(); - return this; - } - - @Override - public OpenSslPrivateKey retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public OpenSslPrivateKey touch() { - super.touch(); - return this; - } - - @Override - public OpenSslPrivateKey touch(Object hint) { - return this; - } - - /** - * NOTE: This is a JDK8 interface/method. Due to backwards compatibility - * reasons it's not possible to slap the {@code @Override} annotation onto - * this method. - * - * @see Destroyable#destroy() - */ - @Override - public void destroy() { - release(refCnt()); - } - - /** - * NOTE: This is a JDK8 interface/method. Due to backwards compatibility - * reasons it's not possible to slap the {@code @Override} annotation onto - * this method. - * - * @see Destroyable#isDestroyed() - */ - @Override - public boolean isDestroyed() { - return refCnt() == 0; - } - - /** - * Create a new {@link OpenSslKeyMaterial} which uses the private key that is held by {@link OpenSslPrivateKey}. - * - * When the material is created we increment the reference count of the enclosing {@link OpenSslPrivateKey} and - * decrement it again when the reference count of the {@link OpenSslKeyMaterial} reaches {@code 0}. - */ - OpenSslKeyMaterial newKeyMaterial(long certificateChain, X509Certificate[] chain) { - return new OpenSslPrivateKeyMaterial(certificateChain, chain); - } - - // Package-private for unit-test only - final class OpenSslPrivateKeyMaterial extends AbstractReferenceCounted implements OpenSslKeyMaterial { - - // Package-private for unit-test only - long certificateChain; - private final X509Certificate[] x509CertificateChain; - - OpenSslPrivateKeyMaterial(long certificateChain, X509Certificate[] x509CertificateChain) { - this.certificateChain = certificateChain; - this.x509CertificateChain = x509CertificateChain == null ? - EmptyArrays.EMPTY_X509_CERTIFICATES : x509CertificateChain; - OpenSslPrivateKey.this.retain(); - } - - @Override - public X509Certificate[] certificateChain() { - return x509CertificateChain.clone(); - } - - @Override - public long certificateChainAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return certificateChain; - } - - @Override - public long privateKeyAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return OpenSslPrivateKey.this.privateKeyAddress(); - } - - @Override - public OpenSslKeyMaterial touch(Object hint) { - OpenSslPrivateKey.this.touch(hint); - return this; - } - - @Override - public OpenSslKeyMaterial retain() { - super.retain(); - return this; - } - - @Override - public OpenSslKeyMaterial retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public OpenSslKeyMaterial touch() { - OpenSslPrivateKey.this.touch(); - return this; - } - - @Override - protected void deallocate() { - releaseChain(); - OpenSslPrivateKey.this.release(); - } - - private void releaseChain() { - SSL.freeX509Chain(certificateChain); - certificateChain = 0; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java deleted file mode 100644 index d9fc877269..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2019 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSLPrivateKeyMethod; -import io.netty.util.internal.UnstableApi; - -import javax.net.ssl.SSLEngine; - -/** - * Allow to customize private key signing / decrypting (when using RSA). Only supported when using BoringSSL atm. - */ -@UnstableApi -public interface OpenSslPrivateKeyMethod { - int SSL_SIGN_RSA_PKCS1_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA1; - int SSL_SIGN_RSA_PKCS1_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA256; - int SSL_SIGN_RSA_PKCS1_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA384; - int SSL_SIGN_RSA_PKCS1_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA512; - int SSL_SIGN_ECDSA_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SHA1; - int SSL_SIGN_ECDSA_SECP256R1_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP256R1_SHA256; - int SSL_SIGN_ECDSA_SECP384R1_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP384R1_SHA384; - int SSL_SIGN_ECDSA_SECP521R1_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP521R1_SHA512; - int SSL_SIGN_RSA_PSS_RSAE_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256; - int SSL_SIGN_RSA_PSS_RSAE_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA384; - int SSL_SIGN_RSA_PSS_RSAE_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA512; - int SSL_SIGN_ED25519 = SSLPrivateKeyMethod.SSL_SIGN_ED25519; - int SSL_SIGN_RSA_PKCS1_MD5_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_MD5_SHA1; - - /** - * Signs the input with the given key and returns the signed bytes. - * - * @param engine the {@link SSLEngine} - * @param signatureAlgorithm the algorithm to use for signing - * @param input the digest itself - * @return the signed data (must not be {@code null}) - * @throws Exception thrown if an error is encountered during the signing - */ - byte[] sign(SSLEngine engine, int signatureAlgorithm, byte[] input) throws Exception; - - /** - * Decrypts the input with the given key and returns the decrypted bytes. - * - * @param engine the {@link SSLEngine} - * @param input the input which should be decrypted - * @return the decrypted data (must not be {@code null}) - * @throws Exception thrown if an error is encountered during the decrypting - */ - byte[] decrypt(SSLEngine engine, byte[] input) throws Exception; -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java deleted file mode 100644 index da342de0f2..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; - -import java.io.File; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; - -import static io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.newSessionContext; - -/** - * A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslServerContext}. - */ -public final class OpenSslServerContext extends OpenSslContext { - private final OpenSslServerSessionContext sessionContext; - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext(File certChainFile, File keyFile) throws SSLException { - this(certChainFile, keyFile, null); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException { - this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE, - ApplicationProtocolConfig.DISABLED, 0, 0); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(certChainFile, keyFile, keyPassword, ciphers, IdentityCipherSuiteFilter.INSTANCE, - apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param nextProtocols the application layer protocols to accept, in the order of preference. - * {@code null} to disable TLS NPN/ALPN extension. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, Iterable nextProtocols, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(certChainFile, keyFile, keyPassword, ciphers, - toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param config Application protocol config. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory, - Iterable ciphers, ApplicationProtocolConfig config, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(certChainFile, keyFile, keyPassword, trustManagerFactory, ciphers, - toNegotiator(config), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param apn Application protocol negotiator. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory, - Iterable ciphers, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(null, trustManagerFactory, certChainFile, keyFile, keyPassword, null, - ciphers, null, apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(null, null, certChainFile, keyFile, keyPassword, null, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param trustCertCollectionFile an X.509 certificate collection file in PEM format. - * This provides the certificate collection used for mutual authentication. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from clients. - * {@code null} to use the default or the results of parsing - * {@code trustCertCollectionFile}. - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s - * that is used to encrypt data being sent to clients. - * {@code null} to use the default or the results of parsing - * {@code keyCertChainFile} and {@code keyFile}. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * Only required if {@code provider} is {@link SslProvider#JDK} - * @param config Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File trustCertCollectionFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(trustCertCollectionFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory, - ciphers, cipherFilter, toNegotiator(config), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param config Application protocol config. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword, - TrustManagerFactory trustManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(null, trustManagerFactory, certChainFile, keyFile, keyPassword, null, ciphers, cipherFilter, - toNegotiator(config), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Application protocol negotiator. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder}} - */ - @Deprecated - public OpenSslServerContext( - File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(null, trustManagerFactory, certChainFile, keyFile, keyPassword, null, ciphers, cipherFilter, - apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new instance. - * - * - * @param trustCertCollectionFile an X.509 certificate collection file in PEM format. - * This provides the certificate collection used for mutual authentication. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from clients. - * {@code null} to use the default or the results of parsing - * {@code trustCertCollectionFile}. - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s - * that is used to encrypt data being sent to clients. - * {@code null} to use the default or the results of parsing - * {@code keyCertChainFile} and {@code keyFile}. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * Only required if {@code provider} is {@link SslProvider#JDK} - * @param apn Application Protocol Negotiator object - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @deprecated use {@link SslContextBuilder} - */ - @Deprecated - public OpenSslServerContext( - File trustCertCollectionFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - this(toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory, - toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword), - keyPassword, keyManagerFactory, ciphers, cipherFilter, - apn, sessionCacheSize, sessionTimeout, ClientAuth.NONE, null, false, false, KeyStore.getDefaultType()); - } - - OpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore) throws SSLException { - this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, - cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls, - enableOcsp, keyStore); - } - - @SuppressWarnings("deprecation") - private OpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore) throws SSLException { - super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain, - clientAuth, protocols, startTls, enableOcsp); - - // Create a new SSL_CTX and configure it. - boolean success = false; - try { - OpenSslKeyMaterialProvider.validateKeyMaterialSupported(keyCertChain, key, keyPassword); - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslServerSessionContext sessionContext() { - return sessionContext; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java deleted file mode 100644 index 691ee0b661..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; - -import java.util.concurrent.locks.Lock; - - -/** - * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side. - */ -public final class OpenSslServerSessionContext extends OpenSslSessionContext { - OpenSslServerSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { - super(context, provider); - } - - @Override - public void setSessionTimeout(int seconds) { - if (seconds < 0) { - throw new IllegalArgumentException(); - } - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setSessionCacheTimeout(context.ctx, seconds); - } finally { - writerLock.unlock(); - } - } - - @Override - public int getSessionTimeout() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return (int) SSLContext.getSessionCacheTimeout(context.ctx); - } finally { - readerLock.unlock(); - } - } - - @Override - public void setSessionCacheSize(int size) { - if (size < 0) { - throw new IllegalArgumentException(); - } - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setSessionCacheSize(context.ctx, size); - } finally { - writerLock.unlock(); - } - } - - @Override - public int getSessionCacheSize() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return (int) SSLContext.getSessionCacheSize(context.ctx); - } finally { - readerLock.unlock(); - } - } - - @Override - public void setSessionCacheEnabled(boolean enabled) { - long mode = enabled ? SSL.SSL_SESS_CACHE_SERVER : SSL.SSL_SESS_CACHE_OFF; - - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setSessionCacheMode(context.ctx, mode); - } finally { - writerLock.unlock(); - } - } - - @Override - public boolean isSessionCacheEnabled() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.getSessionCacheMode(context.ctx) == SSL.SSL_SESS_CACHE_SERVER; - } finally { - readerLock.unlock(); - } - } - - /** - * Set the context within which session be reused (server side only) - * See - * man SSL_CTX_set_session_id_context - * - * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name - * of the application and/or the hostname and/or service name - * @return {@code true} if success, {@code false} otherwise. - */ - public boolean setSessionIdContext(byte[] sidCtx) { - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - return SSLContext.setSessionIdContext(context.ctx, sidCtx); - } finally { - writerLock.unlock(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java deleted file mode 100644 index 5d471d34fb..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.internal.tcnative.SessionTicketKey; -import io.netty.util.internal.ObjectUtil; - -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.NoSuchElementException; -import java.util.concurrent.locks.Lock; - -/** - * OpenSSL specific {@link SSLSessionContext} implementation. - */ -public abstract class OpenSslSessionContext implements SSLSessionContext { - private static final Enumeration EMPTY = new EmptyEnumeration(); - - private final OpenSslSessionStats stats; - - // The OpenSslKeyMaterialProvider is not really used by the OpenSslSessionContext but only be stored here - // to make it easier to destroy it later because the ReferenceCountedOpenSslContext will hold a reference - // to OpenSslSessionContext. - private final OpenSslKeyMaterialProvider provider; - - final ReferenceCountedOpenSslContext context; - - // IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent - // the GC to collect OpenSslContext as this would also free the pointer and so could result in a - // segfault when the user calls any of the methods here that try to pass the pointer down to the native - // level. - OpenSslSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { - this.context = context; - this.provider = provider; - stats = new OpenSslSessionStats(context); - } - - final boolean useKeyManager() { - return provider != null; - } - - @Override - public SSLSession getSession(byte[] bytes) { - ObjectUtil.checkNotNull(bytes, "bytes"); - return null; - } - - @Override - public Enumeration getIds() { - return EMPTY; - } - - /** - * Sets the SSL session ticket keys of this context. - * @deprecated use {@link #setTicketKeys(OpenSslSessionTicketKey...)}. - */ - @Deprecated - public void setTicketKeys(byte[] keys) { - if (keys.length % SessionTicketKey.TICKET_KEY_SIZE != 0) { - throw new IllegalArgumentException("keys.length % " + SessionTicketKey.TICKET_KEY_SIZE + " != 0"); - } - SessionTicketKey[] tickets = new SessionTicketKey[keys.length / SessionTicketKey.TICKET_KEY_SIZE]; - for (int i = 0, a = 0; i < tickets.length; i++) { - byte[] name = Arrays.copyOfRange(keys, a, SessionTicketKey.NAME_SIZE); - a += SessionTicketKey.NAME_SIZE; - byte[] hmacKey = Arrays.copyOfRange(keys, a, SessionTicketKey.HMAC_KEY_SIZE); - i += SessionTicketKey.HMAC_KEY_SIZE; - byte[] aesKey = Arrays.copyOfRange(keys, a, SessionTicketKey.AES_KEY_SIZE); - a += SessionTicketKey.AES_KEY_SIZE; - tickets[i] = new SessionTicketKey(name, hmacKey, aesKey); - } - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.clearOptions(context.ctx, SSL.SSL_OP_NO_TICKET); - SSLContext.setSessionTicketKeys(context.ctx, tickets); - } finally { - writerLock.unlock(); - } - } - - /** - * Sets the SSL session ticket keys of this context. Depending on the underlying native library you may omit the - * argument or pass an empty array and so let the native library handle the key generation and rotating for you. - * If this is supported by the underlying native library should be checked in this case. For example - * - * BoringSSL is known to support this. - */ - public void setTicketKeys(OpenSslSessionTicketKey... keys) { - ObjectUtil.checkNotNull(keys, "keys"); - SessionTicketKey[] ticketKeys = new SessionTicketKey[keys.length]; - for (int i = 0; i < ticketKeys.length; i++) { - ticketKeys[i] = keys[i].key; - } - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.clearOptions(context.ctx, SSL.SSL_OP_NO_TICKET); - if (ticketKeys.length > 0) { - SSLContext.setSessionTicketKeys(context.ctx, ticketKeys); - } - } finally { - writerLock.unlock(); - } - } - - /** - * Enable or disable caching of SSL sessions. - */ - public abstract void setSessionCacheEnabled(boolean enabled); - - /** - * Return {@code true} if caching of SSL sessions is enabled, {@code false} otherwise. - */ - public abstract boolean isSessionCacheEnabled(); - - /** - * Returns the stats of this context. - */ - public OpenSslSessionStats stats() { - return stats; - } - - final void destroy() { - if (provider != null) { - provider.destroy(); - } - } - - private static final class EmptyEnumeration implements Enumeration { - @Override - public boolean hasMoreElements() { - return false; - } - - @Override - public byte[] nextElement() { - throw new NoSuchElementException(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java deleted file mode 100644 index f49b95f3ba..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSLContext; - -import java.util.concurrent.locks.Lock; - -/** - * Stats exposed by an OpenSSL session context. - * - * @see SSL_CTX_sess_number - */ -public final class OpenSslSessionStats { - - private final ReferenceCountedOpenSslContext context; - - // IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent - // the GC to collect OpenSslContext as this would also free the pointer and so could result in a - // segfault when the user calls any of the methods here that try to pass the pointer down to the native - // level. - OpenSslSessionStats(ReferenceCountedOpenSslContext context) { - this.context = context; - } - - /** - * Returns the current number of sessions in the internal session cache. - */ - public long number() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionNumber(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of started SSL/TLS handshakes in client mode. - */ - public long connect() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionConnect(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully established SSL/TLS sessions in client mode. - */ - public long connectGood() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionConnectGood(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of start renegotiations in client mode. - */ - public long connectRenegotiate() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionConnectRenegotiate(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of started SSL/TLS handshakes in server mode. - */ - public long accept() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionAccept(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully established SSL/TLS sessions in server mode. - */ - public long acceptGood() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionAcceptGood(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of start renegotiations in server mode. - */ - public long acceptRenegotiate() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionAcceptRenegotiate(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully reused sessions. In client mode, a session set with {@code SSL_set_session} - * successfully reused is counted as a hit. In server mode, a session successfully retrieved from internal or - * external cache is counted as a hit. - */ - public long hits() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionHits(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully retrieved sessions from the external session cache in server mode. - */ - public long cbHits() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionCbHits(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of sessions proposed by clients that were not found in the internal session cache - * in server mode. - */ - public long misses() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionMisses(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of sessions proposed by clients and either found in the internal or external session cache - * in server mode, but that were invalid due to timeout. These sessions are not included in the {@link #hits()} - * count. - */ - public long timeouts() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTimeouts(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of sessions that were removed because the maximum session cache size was exceeded. - */ - public long cacheFull() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionCacheFull(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client presented a ticket that did not match any key in the list. - */ - public long ticketKeyFail() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyFail(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client did not present a ticket and we issued a new one - */ - public long ticketKeyNew() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyNew(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client presented a ticket derived from an older key, - * and we upgraded to the primary key. - */ - public long ticketKeyRenew() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyRenew(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client presented a ticket derived from the primary key. - */ - public long ticketKeyResume() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyResume(context.ctx); - } finally { - readerLock.unlock(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java deleted file mode 100644 index 79f71a65a3..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.SessionTicketKey; - -/** - * Session Ticket Key - */ -public final class OpenSslSessionTicketKey { - - /** - * Size of session ticket key name - */ - public static final int NAME_SIZE = SessionTicketKey.NAME_SIZE; - /** - * Size of session ticket key HMAC key - */ - public static final int HMAC_KEY_SIZE = SessionTicketKey.HMAC_KEY_SIZE; - /** - * Size of session ticket key AES key - */ - public static final int AES_KEY_SIZE = SessionTicketKey.AES_KEY_SIZE; - /** - * Size of session ticker key - */ - public static final int TICKET_KEY_SIZE = SessionTicketKey.TICKET_KEY_SIZE; - - final SessionTicketKey key; - - /** - * Construct a OpenSslSessionTicketKey. - * - * @param name the name of the session ticket key - * @param hmacKey the HMAC key of the session ticket key - * @param aesKey the AES key of the session ticket key - */ - public OpenSslSessionTicketKey(byte[] name, byte[] hmacKey, byte[] aesKey) { - key = new SessionTicketKey(name.clone(), hmacKey.clone(), aesKey.clone()); - } - - /** - * Get name. - * @return the name of the session ticket key - */ - public byte[] name() { - return key.getName().clone(); - } - - /** - * Get HMAC key. - * @return the HMAC key of the session ticket key - */ - public byte[] hmacKey() { - return key.getHmacKey().clone(); - } - - /** - * Get AES Key. - * @return the AES key of the session ticket key - */ - public byte[] aesKey() { - return key.getAesKey().clone(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java deleted file mode 100644 index a5a84f2793..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslTlsv13X509ExtendedTrustManager.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SuppressJava6Requirement; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.X509ExtendedTrustManager; -import java.net.Socket; -import java.security.Principal; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.List; - -/** - * Provide a way to use {@code TLSv1.3} with Java versions prior to 11 by adding a - * = 7 && session instanceof ExtendedOpenSslSession) { - final ExtendedOpenSslSession extendedOpenSslSession = (ExtendedOpenSslSession) session; - return new ExtendedOpenSslSession(extendedOpenSslSession) { - @Override - public List getRequestedServerNames() { - return extendedOpenSslSession.getRequestedServerNames(); - } - - @Override - public String[] getPeerSupportedSignatureAlgorithms() { - return extendedOpenSslSession.getPeerSupportedSignatureAlgorithms(); - } - - @Override - public String getProtocol() { - return SslUtils.PROTOCOL_TLS_V1_2; - } - }; - } else { - return new SSLSession() { - @Override - public byte[] getId() { - return session.getId(); - } - - @Override - public SSLSessionContext getSessionContext() { - return session.getSessionContext(); - } - - @Override - public long getCreationTime() { - return session.getCreationTime(); - } - - @Override - public long getLastAccessedTime() { - return session.getLastAccessedTime(); - } - - @Override - public void invalidate() { - session.invalidate(); - } - - @Override - public boolean isValid() { - return session.isValid(); - } - - @Override - public void putValue(String s, Object o) { - session.putValue(s, o); - } - - @Override - public Object getValue(String s) { - return session.getValue(s); - } - - @Override - public void removeValue(String s) { - session.removeValue(s); - } - - @Override - public String[] getValueNames() { - return session.getValueNames(); - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - return session.getPeerCertificates(); - } - - @Override - public Certificate[] getLocalCertificates() { - return session.getLocalCertificates(); - } - - @Override - public javax.security.cert.X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { - return session.getPeerCertificateChain(); - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - return session.getPeerPrincipal(); - } - - @Override - public Principal getLocalPrincipal() { - return session.getLocalPrincipal(); - } - - @Override - public String getCipherSuite() { - return session.getCipherSuite(); - } - - @Override - public String getProtocol() { - return SslUtils.PROTOCOL_TLS_V1_2; - } - - @Override - public String getPeerHost() { - return session.getPeerHost(); - } - - @Override - public int getPeerPort() { - return session.getPeerPort(); - } - - @Override - public int getPacketBufferSize() { - return session.getPacketBufferSize(); - } - - @Override - public int getApplicationBufferSize() { - return session.getApplicationBufferSize(); - } - }; - } - } - }; - } - return engine; - } - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, final String s, SSLEngine sslEngine) - throws CertificateException { - tm.checkClientTrusted(x509Certificates, s, wrapEngine(sslEngine)); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) - throws CertificateException { - tm.checkServerTrusted(x509Certificates, s, wrapEngine(sslEngine)); - } - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - tm.checkClientTrusted(x509Certificates, s); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - tm.checkServerTrusted(x509Certificates, s); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return tm.getAcceptedIssuers(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java deleted file mode 100644 index 2a86332fe1..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2018 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.SSL; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.ObjectUtil; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.KeyManagerFactorySpi; -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.X509KeyManager; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.Key; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.KeyStoreSpi; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -/** - * Special {@link KeyManagerFactory} that pre-compute the keymaterial used when {@link SslProvider#OPENSSL} or - * {@link SslProvider#OPENSSL_REFCNT} is used and so will improve handshake times and its performance. - * - * - * - * Because the keymaterial is pre-computed any modification to the {@link KeyStore} is ignored after - * {@link #init(KeyStore, char[])} is called. - * - * {@link #init(ManagerFactoryParameters)} is not supported by this implementation and so a call to it will always - * result in an {@link InvalidAlgorithmParameterException}. - */ -public final class OpenSslX509KeyManagerFactory extends KeyManagerFactory { - - private final OpenSslKeyManagerFactorySpi spi; - - public OpenSslX509KeyManagerFactory() { - this(newOpenSslKeyManagerFactorySpi(null)); - } - - public OpenSslX509KeyManagerFactory(Provider provider) { - this(newOpenSslKeyManagerFactorySpi(provider)); - } - - public OpenSslX509KeyManagerFactory(String algorithm, Provider provider) throws NoSuchAlgorithmException { - this(newOpenSslKeyManagerFactorySpi(algorithm, provider)); - } - - private OpenSslX509KeyManagerFactory(OpenSslKeyManagerFactorySpi spi) { - super(spi, spi.kmf.getProvider(), spi.kmf.getAlgorithm()); - this.spi = spi; - } - - private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(Provider provider) { - try { - return newOpenSslKeyManagerFactorySpi(null, provider); - } catch (NoSuchAlgorithmException e) { - // This should never happen as we use the default algorithm. - throw new IllegalStateException(e); - } - } - - private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(String algorithm, Provider provider) - throws NoSuchAlgorithmException { - if (algorithm == null) { - algorithm = KeyManagerFactory.getDefaultAlgorithm(); - } - return new OpenSslKeyManagerFactorySpi( - provider == null ? KeyManagerFactory.getInstance(algorithm) : - KeyManagerFactory.getInstance(algorithm, provider)); - } - - OpenSslKeyMaterialProvider newProvider() { - return spi.newProvider(); - } - - private static final class OpenSslKeyManagerFactorySpi extends KeyManagerFactorySpi { - final KeyManagerFactory kmf; - private volatile ProviderFactory providerFactory; - - OpenSslKeyManagerFactorySpi(KeyManagerFactory kmf) { - this.kmf = ObjectUtil.checkNotNull(kmf, "kmf"); - } - - @Override - protected synchronized void engineInit(KeyStore keyStore, char[] chars) - throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - if (providerFactory != null) { - throw new KeyStoreException("Already initialized"); - } - if (!keyStore.aliases().hasMoreElements()) { - throw new KeyStoreException("No aliases found"); - } - - kmf.init(keyStore, chars); - providerFactory = new ProviderFactory(ReferenceCountedOpenSslContext.chooseX509KeyManager( - kmf.getKeyManagers()), password(chars), Collections.list(keyStore.aliases())); - } - - private static String password(char[] password) { - if (password == null || password.length == 0) { - return null; - } - return new String(password); - } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) - throws InvalidAlgorithmParameterException { - throw new InvalidAlgorithmParameterException("Not supported"); - } - - @Override - protected KeyManager[] engineGetKeyManagers() { - ProviderFactory providerFactory = this.providerFactory; - if (providerFactory == null) { - throw new IllegalStateException("engineInit(...) not called yet"); - } - return new KeyManager[] { providerFactory.keyManager }; - } - - OpenSslKeyMaterialProvider newProvider() { - ProviderFactory providerFactory = this.providerFactory; - if (providerFactory == null) { - throw new IllegalStateException("engineInit(...) not called yet"); - } - return providerFactory.newProvider(); - } - - private static final class ProviderFactory { - private final X509KeyManager keyManager; - private final String password; - private final Iterable aliases; - - ProviderFactory(X509KeyManager keyManager, String password, Iterable aliases) { - this.keyManager = keyManager; - this.password = password; - this.aliases = aliases; - } - - OpenSslKeyMaterialProvider newProvider() { - return new OpenSslPopulatedKeyMaterialProvider(keyManager, - password, aliases); - } - - /** - * {@link OpenSslKeyMaterialProvider} implementation that pre-compute the {@link OpenSslKeyMaterial} for - * all aliases. - */ - private static final class OpenSslPopulatedKeyMaterialProvider extends OpenSslKeyMaterialProvider { - private final Map materialMap; - - OpenSslPopulatedKeyMaterialProvider( - X509KeyManager keyManager, String password, Iterable aliases) { - super(keyManager, password); - materialMap = new HashMap(); - boolean initComplete = false; - try { - for (String alias: aliases) { - if (alias != null && !materialMap.containsKey(alias)) { - try { - materialMap.put(alias, super.chooseKeyMaterial( - UnpooledByteBufAllocator.DEFAULT, alias)); - } catch (Exception e) { - // Just store the exception and rethrow it when we try to choose the keymaterial - // for this alias later on. - materialMap.put(alias, e); - } - } - } - initComplete = true; - } finally { - if (!initComplete) { - destroy(); - } - } - if (materialMap.isEmpty()) { - throw new IllegalArgumentException("aliases must be non-empty"); - } - } - - @Override - OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception { - Object value = materialMap.get(alias); - if (value == null) { - // There is no keymaterial for the requested alias, return null - return null; - } - if (value instanceof OpenSslKeyMaterial) { - return ((OpenSslKeyMaterial) value).retain(); - } - throw (Exception) value; - } - - @Override - void destroy() { - for (Object material: materialMap.values()) { - ReferenceCountUtil.release(material); - } - materialMap.clear(); - } - } - } - } - - /** - * Create a new initialized {@link OpenSslX509KeyManagerFactory} which loads its {@link PrivateKey} directly from - * an {@code OpenSSL engine} via the - * ENGINE_load_private_key - * function. - */ - public static OpenSslX509KeyManagerFactory newEngineBased(File certificateChain, String password) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - return newEngineBased(SslContext.toX509Certificates(certificateChain), password); - } - - /** - * Create a new initialized {@link OpenSslX509KeyManagerFactory} which loads its {@link PrivateKey} directly from - * an {@code OpenSSL engine} via the - * ENGINE_load_private_key - * function. - */ - public static OpenSslX509KeyManagerFactory newEngineBased(X509Certificate[] certificateChain, String password) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - KeyStore store = new OpenSslKeyStore(certificateChain.clone(), false); - store.load(null, null); - OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory(); - factory.init(store, password == null ? null : password.toCharArray()); - return factory; - } - - /** - * See {@link OpenSslX509KeyManagerFactory#newEngineBased(X509Certificate[], String)}. - */ - public static OpenSslX509KeyManagerFactory newKeyless(File chain) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - return newKeyless(SslContext.toX509Certificates(chain)); - } - - /** - * See {@link OpenSslX509KeyManagerFactory#newEngineBased(X509Certificate[], String)}. - */ - public static OpenSslX509KeyManagerFactory newKeyless(InputStream chain) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - return newKeyless(SslContext.toX509Certificates(chain)); - } - - /** - * Returns a new initialized {@link OpenSslX509KeyManagerFactory} which will provide its private key by using the - * {@link OpenSslPrivateKeyMethod}. - */ - public static OpenSslX509KeyManagerFactory newKeyless(X509Certificate... certificateChain) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - KeyStore store = new OpenSslKeyStore(certificateChain.clone(), true); - store.load(null, null); - OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory(); - factory.init(store, null); - return factory; - } - - private static final class OpenSslKeyStore extends KeyStore { - private OpenSslKeyStore(final X509Certificate[] certificateChain, final boolean keyless) { - super(new KeyStoreSpi() { - - private final Date creationDate = new Date(); - - @Override - public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyException { - if (engineContainsAlias(alias)) { - final long privateKeyAddress; - if (keyless) { - privateKeyAddress = 0; - } else { - try { - privateKeyAddress = SSL.loadPrivateKeyFromEngine( - alias, password == null ? null : new String(password)); - } catch (Exception e) { - UnrecoverableKeyException keyException = - new UnrecoverableKeyException("Unable to load key from engine"); - keyException.initCause(e); - throw keyException; - } - } - return new OpenSslPrivateKey(privateKeyAddress); - } - return null; - } - - @Override - public Certificate[] engineGetCertificateChain(String alias) { - return engineContainsAlias(alias)? certificateChain.clone() : null; - } - - @Override - public Certificate engineGetCertificate(String alias) { - return engineContainsAlias(alias)? certificateChain[0] : null; - } - - @Override - public Date engineGetCreationDate(String alias) { - return engineContainsAlias(alias)? creationDate : null; - } - - @Override - public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) - throws KeyStoreException { - throw new KeyStoreException("Not supported"); - } - - @Override - public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { - throw new KeyStoreException("Not supported"); - } - - @Override - public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { - throw new KeyStoreException("Not supported"); - } - - @Override - public void engineDeleteEntry(String alias) throws KeyStoreException { - throw new KeyStoreException("Not supported"); - } - - @Override - public Enumeration engineAliases() { - return Collections.enumeration(Collections.singleton(SslContext.ALIAS)); - } - - @Override - public boolean engineContainsAlias(String alias) { - return SslContext.ALIAS.equals(alias); - } - - @Override - public int engineSize() { - return 1; - } - - @Override - public boolean engineIsKeyEntry(String alias) { - return engineContainsAlias(alias); - } - - @Override - public boolean engineIsCertificateEntry(String alias) { - return engineContainsAlias(alias); - } - - @Override - public String engineGetCertificateAlias(Certificate cert) { - if (cert instanceof X509Certificate) { - for (X509Certificate x509Certificate : certificateChain) { - if (x509Certificate.equals(cert)) { - return SslContext.ALIAS; - } - } - } - return null; - } - - @Override - public void engineStore(OutputStream stream, char[] password) { - throw new UnsupportedOperationException(); - } - - @Override - public void engineLoad(InputStream stream, char[] password) { - if (stream != null && password != null) { - throw new UnsupportedOperationException(); - } - } - }, null, "native"); - - OpenSsl.ensureAvailability(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java deleted file mode 100644 index 6b945506b9..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.internal.tcnative.CertificateCallback; -import io.netty.util.internal.SuppressJava6Requirement; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; - -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; -import javax.security.auth.x500.X500Principal; - -/** - * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must not be released before any {@link ReferenceCountedOpenSslEngine} - * which depends upon the instance of this class is released. Otherwise if any method of - * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash. - */ -public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ReferenceCountedOpenSslClientContext.class); - private static final Set SUPPORTED_KEY_TYPES = Collections.unmodifiableSet(new LinkedHashSet( - Arrays.asList(OpenSslKeyMaterialManager.KEY_TYPE_RSA, - OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA, - OpenSslKeyMaterialManager.KEY_TYPE_EC, - OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA, - OpenSslKeyMaterialManager.KEY_TYPE_EC_EC))); - private static final boolean ENABLE_SESSION_TICKET = - SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", false); - private final OpenSslSessionContext sessionContext; - - ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, - KeyManagerFactory keyManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - String[] protocols, long sessionCacheSize, long sessionTimeout, - boolean enableOcsp, String keyStore) throws SSLException { - super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain, - ClientAuth.NONE, protocols, false, enableOcsp, true); - boolean success = false; - try { - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore); - if (ENABLE_SESSION_TICKET) { - sessionContext.setTicketKeys(); - } - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslSessionContext sessionContext() { - return sessionContext; - } - - static OpenSslSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, - OpenSslEngineMap engineMap, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, - String keyPassword, KeyManagerFactory keyManagerFactory, - String keyStore) throws SSLException { - if (key == null && keyCertChain != null || key != null && keyCertChain == null) { - throw new IllegalArgumentException( - "Either both keyCertChain and key needs to be null or none of them"); - } - OpenSslKeyMaterialProvider keyMaterialProvider = null; - try { - try { - if (!OpenSsl.useKeyManagerFactory()) { - if (keyManagerFactory != null) { - throw new IllegalArgumentException( - "KeyManagerFactory not supported"); - } - if (keyCertChain != null/* && key != null*/) { - setKeyMaterial(ctx, keyCertChain, key, keyPassword); - } - } else { - // javadocs state that keyManagerFactory has precedent over keyCertChain - if (keyManagerFactory == null && keyCertChain != null) { - char[] keyPasswordChars = keyStorePassword(keyPassword); - KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore); - if (ks.aliases().hasMoreElements()) { - keyManagerFactory = new OpenSslX509KeyManagerFactory(); - } else { - keyManagerFactory = new OpenSslCachingX509KeyManagerFactory( - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())); - } - keyManagerFactory.init(ks, keyPasswordChars); - keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); - } else if (keyManagerFactory != null) { - keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); - } - - if (keyMaterialProvider != null) { - OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); - SSLContext.setCertificateCallback(ctx, new OpenSslClientCertificateCallback( - engineMap, materialManager)); - } - } - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } - - // On the client side we always need to use SSL_CVERIFY_OPTIONAL (which will translate to SSL_VERIFY_PEER) - // to ensure that when the TrustManager throws we will produce the correct alert back to the server. - // - // See: - // - https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html - // - https://github.com/netty/netty/issues/8942 - SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_OPTIONAL, VERIFY_DEPTH); - - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore); - } else if (trustManagerFactory == null) { - trustManagerFactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore) null); - } - final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers()); - - // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as - // otherwise the context can never be collected. This is because the JNI code holds - // a global reference to the callbacks. - // - // See https://github.com/netty/netty/issues/5372 - - setVerifyCallback(ctx, engineMap, manager); - } catch (Exception e) { - if (keyMaterialProvider != null) { - keyMaterialProvider.destroy(); - } - throw new SSLException("unable to setup trustmanager", e); - } - OpenSslClientSessionContext context = new OpenSslClientSessionContext(thiz, keyMaterialProvider); - keyMaterialProvider = null; - return context; - } finally { - if (keyMaterialProvider != null) { - keyMaterialProvider.destroy(); - } - } - } - - @SuppressJava6Requirement(reason = "Guarded by java version check") - private static void setVerifyCallback(long ctx, OpenSslEngineMap engineMap, X509TrustManager manager) { - // Use this to prevent an error when running on java < 7 - if (useExtendedTrustManager(manager)) { - SSLContext.setCertVerifyCallback(ctx, - new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager)); - } else { - SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); - } - } - - // No cache is currently supported for client side mode. - static final class OpenSslClientSessionContext extends OpenSslSessionContext { - OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { - super(context, provider); - } - - @Override - public void setSessionTimeout(int seconds) { - if (seconds < 0) { - throw new IllegalArgumentException(); - } - } - - @Override - public int getSessionTimeout() { - return 0; - } - - @Override - public void setSessionCacheSize(int size) { - if (size < 0) { - throw new IllegalArgumentException(); - } - } - - @Override - public int getSessionCacheSize() { - return 0; - } - - @Override - public void setSessionCacheEnabled(boolean enabled) { - // ignored - } - - @Override - public boolean isSessionCacheEnabled() { - return false; - } - } - - private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509TrustManager manager; - - TrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509TrustManager manager) { - super(engineMap); - this.manager = manager; - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkServerTrusted(peerCerts, auth); - } - } - - @SuppressJava6Requirement(reason = "Usage guarded by java version check") - private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509ExtendedTrustManager manager; - - ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) { - super(engineMap); - this.manager = OpenSslTlsv13X509ExtendedTrustManager.wrap(manager); - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkServerTrusted(peerCerts, auth, engine); - } - } - - private static final class OpenSslClientCertificateCallback implements CertificateCallback { - private final OpenSslEngineMap engineMap; - private final OpenSslKeyMaterialManager keyManagerHolder; - - OpenSslClientCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) { - this.engineMap = engineMap; - this.keyManagerHolder = keyManagerHolder; - } - - @Override - public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception { - final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - // May be null if it was destroyed in the meantime. - if (engine == null) { - return; - } - try { - final Set keyTypesSet = supportedClientKeyTypes(keyTypeBytes); - final String[] keyTypes = keyTypesSet.toArray(new String[0]); - final X500Principal[] issuers; - if (asn1DerEncodedPrincipals == null) { - issuers = null; - } else { - issuers = new X500Principal[asn1DerEncodedPrincipals.length]; - for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) { - issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); - } - } - keyManagerHolder.setKeyMaterialClientSide(engine, keyTypes, issuers); - } catch (Throwable cause) { - logger.debug("request of key failed", cause); - engine.initHandshakeException(cause); - } - } - - /** - * Gets the supported key types for client certificates. - * - * @param clientCertificateTypes {@code ClientCertificateType} values provided by the server. - * See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml. - * @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and - * {@code X509ExtendedKeyManager.chooseEngineClientAlias}. - */ - private static Set supportedClientKeyTypes(byte[] clientCertificateTypes) { - if (clientCertificateTypes == null) { - // Try all of the supported key types. - return SUPPORTED_KEY_TYPES; - } - Set result = new HashSet(clientCertificateTypes.length); - for (byte keyTypeCode : clientCertificateTypes) { - String keyType = clientKeyType(keyTypeCode); - if (keyType == null) { - // Unsupported client key type -- ignore - continue; - } - result.add(keyType); - } - return result; - } - - private static String clientKeyType(byte clientCertificateType) { - // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml - switch (clientCertificateType) { - case CertificateCallback.TLS_CT_RSA_SIGN: - return OpenSslKeyMaterialManager.KEY_TYPE_RSA; // RFC rsa_sign - case CertificateCallback.TLS_CT_RSA_FIXED_DH: - return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh - case CertificateCallback.TLS_CT_ECDSA_SIGN: - return OpenSslKeyMaterialManager.KEY_TYPE_EC; // RFC ecdsa_sign - case CertificateCallback.TLS_CT_RSA_FIXED_ECDH: - return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh - case CertificateCallback.TLS_CT_ECDSA_FIXED_ECDH: - return OpenSslKeyMaterialManager.KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh - default: - return null; - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java deleted file mode 100644 index 27eb43574c..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.internal.tcnative.CertificateVerifier; -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.internal.tcnative.SSLPrivateKeyMethod; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.ReferenceCounted; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SuppressJava6Requirement; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.security.PrivateKey; -import java.security.SignatureException; -import java.security.cert.CertPathValidatorException; -import java.security.cert.Certificate; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.CertificateRevokedException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; - -import static io.netty.handler.ssl.OpenSsl.DEFAULT_CIPHERS; -import static io.netty.handler.ssl.OpenSsl.availableJavaCipherSuites; -import static io.netty.util.internal.ObjectUtil.checkNotNull; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -/** - * An implementation of {@link SslContext} which works with libraries that support the - * OpenSsl C library API. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must not be released before any {@link ReferenceCountedOpenSslEngine} - * which depends upon the instance of this class is released. Otherwise if any method of - * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash. - */ -public abstract class ReferenceCountedOpenSslContext extends SslContext implements ReferenceCounted { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ReferenceCountedOpenSslContext.class); - - private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE = Math.max(1, - SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize", - 2048)); - static final boolean USE_TASKS = - SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.useTasks", false); - private static final Integer DH_KEY_LENGTH; - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class); - - // TODO: Maybe make configurable ? - protected static final int VERIFY_DEPTH = 10; - - /** - * The OpenSSL SSL_CTX object. - * - * {@link #ctxLock} must be hold while using ctx! - */ - protected long ctx; - private final List unmodifiableCiphers; - private final long sessionCacheSize; - private final long sessionTimeout; - private final OpenSslApplicationProtocolNegotiator apn; - private final int mode; - - // Reference Counting - private final ResourceLeakTracker leak; - private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() { - @Override - public ReferenceCounted touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - - return ReferenceCountedOpenSslContext.this; - } - - @Override - protected void deallocate() { - destroy(); - if (leak != null) { - boolean closed = leak.close(ReferenceCountedOpenSslContext.this); - assert closed; - } - } - }; - - final Certificate[] keyCertChain; - final ClientAuth clientAuth; - final String[] protocols; - final boolean enableOcsp; - final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap(); - final ReadWriteLock ctxLock = new ReentrantReadWriteLock(); - - private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE; - - @SuppressWarnings("deprecation") - static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR = - new OpenSslApplicationProtocolNegotiator() { - @Override - public ApplicationProtocolConfig.Protocol protocol() { - return ApplicationProtocolConfig.Protocol.NONE; - } - - @Override - public List protocols() { - return Collections.emptyList(); - } - - @Override - public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() { - return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL; - } - - @Override - public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() { - return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT; - } - }; - - static { - Integer dhLen = null; - - try { - String dhKeySize = SystemPropertyUtil.get("jdk.tls.ephemeralDHKeySize"); - if (dhKeySize != null) { - try { - dhLen = Integer.valueOf(dhKeySize); - } catch (NumberFormatException e) { - logger.debug("ReferenceCountedOpenSslContext supports -Djdk.tls.ephemeralDHKeySize={int}, but got: " - + dhKeySize); - } - } - } catch (Throwable ignore) { - // ignore - } - DH_KEY_LENGTH = dhLen; - } - - ReferenceCountedOpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, - ApplicationProtocolConfig apnCfg, long sessionCacheSize, long sessionTimeout, - int mode, Certificate[] keyCertChain, ClientAuth clientAuth, String[] protocols, - boolean startTls, boolean enableOcsp, boolean leakDetection) throws SSLException { - this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, leakDetection); - } - - ReferenceCountedOpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, - OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize, - long sessionTimeout, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, - boolean leakDetection) throws SSLException { - super(startTls); - - OpenSsl.ensureAvailability(); - - if (enableOcsp && !OpenSsl.isOcspSupported()) { - throw new IllegalStateException("OCSP is not supported."); - } - - if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) { - throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT"); - } - leak = leakDetection ? leakDetector.track(this) : null; - this.mode = mode; - this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE; - this.protocols = protocols; - this.enableOcsp = enableOcsp; - - this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone(); - - unmodifiableCiphers = Arrays.asList(checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites( - ciphers, DEFAULT_CIPHERS, availableJavaCipherSuites())); - - this.apn = checkNotNull(apn, "apn"); - - // Create a new SSL_CTX and configure it. - boolean success = false; - try { - try { - int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 | - SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2; - if (OpenSsl.isTlsv13Supported()) { - protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3; - } - ctx = SSLContext.make(protocolOpts, mode); - } catch (Exception e) { - throw new SSLException("failed to create an SSL_CTX", e); - } - - boolean tlsv13Supported = OpenSsl.isTlsv13Supported(); - StringBuilder cipherBuilder = new StringBuilder(); - StringBuilder cipherTLSv13Builder = new StringBuilder(); - - /* List the ciphers that are permitted to negotiate. */ - try { - if (unmodifiableCiphers.isEmpty()) { - // Set non TLSv1.3 ciphers. - SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, false); - if (tlsv13Supported) { - // Set TLSv1.3 ciphers. - SSLContext.setCipherSuite(ctx, StringUtil.EMPTY_STRING, true); - } - } else { - CipherSuiteConverter.convertToCipherStrings( - unmodifiableCiphers, cipherBuilder, cipherTLSv13Builder, OpenSsl.isBoringSSL()); - - // Set non TLSv1.3 ciphers. - SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false); - if (tlsv13Supported) { - // Set TLSv1.3 ciphers. - SSLContext.setCipherSuite(ctx, cipherTLSv13Builder.toString(), true); - } - } - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e); - } - - int options = SSLContext.getOptions(ctx) | - SSL.SSL_OP_NO_SSLv2 | - SSL.SSL_OP_NO_SSLv3 | - // Disable TLSv1.3 by default for now. Even if TLSv1.3 is not supported this will - // work fine as in this case SSL_OP_NO_TLSv1_3 will be 0. - SSL.SSL_OP_NO_TLSv1_3 | - - SSL.SSL_OP_CIPHER_SERVER_PREFERENCE | - - // We do not support compression at the moment so we should explicitly disable it. - SSL.SSL_OP_NO_COMPRESSION | - - // Disable ticket support by default to be more inline with SSLEngineImpl of the JDK. - // This also let SSLSession.getId() work the same way for the JDK implementation and the - // OpenSSLEngine. If tickets are supported SSLSession.getId() will only return an ID on the - // server-side if it could make use of tickets. - SSL.SSL_OP_NO_TICKET; - - if (cipherBuilder.length() == 0) { - // No ciphers that are compatible with SSLv2 / SSLv3 / TLSv1 / TLSv1.1 / TLSv1.2 - options |= SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1 - | SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2; - } - - SSLContext.setOptions(ctx, options); - - // We need to enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as the memory address may change between - // calling OpenSSLEngine.wrap(...). - // See https://github.com/netty/netty-tcnative/issues/100 - SSLContext.setMode(ctx, SSLContext.getMode(ctx) | SSL.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - if (DH_KEY_LENGTH != null) { - SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH); - } - - List nextProtoList = apn.protocols(); - /* Set next protocols for next protocol negotiation extension, if specified */ - if (!nextProtoList.isEmpty()) { - String[] appProtocols = nextProtoList.toArray(new String[0]); - int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior()); - - switch (apn.protocol()) { - case NPN: - SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior); - break; - case ALPN: - SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior); - break; - case NPN_AND_ALPN: - SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior); - SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior); - break; - default: - throw new Error(); - } - } - - /* Set session cache size, if specified */ - if (sessionCacheSize <= 0) { - // Get the default session cache size using SSLContext.setSessionCacheSize() - sessionCacheSize = SSLContext.setSessionCacheSize(ctx, 20480); - } - this.sessionCacheSize = sessionCacheSize; - SSLContext.setSessionCacheSize(ctx, sessionCacheSize); - - /* Set session timeout, if specified */ - if (sessionTimeout <= 0) { - // Get the default session timeout using SSLContext.setSessionCacheTimeout() - sessionTimeout = SSLContext.setSessionCacheTimeout(ctx, 300); - } - this.sessionTimeout = sessionTimeout; - SSLContext.setSessionCacheTimeout(ctx, sessionTimeout); - - if (enableOcsp) { - SSLContext.enableOcsp(ctx, isClient()); - } - - SSLContext.setUseTasks(ctx, USE_TASKS); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - private static int opensslSelectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior behavior) { - switch (behavior) { - case NO_ADVERTISE: - return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE; - case CHOOSE_MY_LAST_PROTOCOL: - return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL; - default: - throw new Error(); - } - } - - @Override - public final List cipherSuites() { - return unmodifiableCiphers; - } - - @Override - public final long sessionCacheSize() { - return sessionCacheSize; - } - - @Override - public final long sessionTimeout() { - return sessionTimeout; - } - - @Override - public ApplicationProtocolNegotiator applicationProtocolNegotiator() { - return apn; - } - - @Override - public final boolean isClient() { - return mode == SSL.SSL_MODE_CLIENT; - } - - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { - return newEngine0(alloc, peerHost, peerPort, true); - } - - @Override - protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) { - return new SslHandler(newEngine0(alloc, null, -1, false), startTls); - } - - @Override - protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) { - return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), startTls); - } - - @Override - protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) { - return new SslHandler(newEngine0(alloc, null, -1, false), startTls, executor); - } - - @Override - protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, - boolean startTls, Executor executor) { - return new SslHandler(newEngine0(alloc, peerHost, peerPort, false), executor); - } - - SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) { - return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode, true); - } - - /** - * Returns a new server-side {@link SSLEngine} with the current configuration. - */ - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc) { - return newEngine(alloc, null, -1); - } - - /** - * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}. - * Be aware that it is freed as soon as the {@link #finalize()} method is called. - * At this point {@code 0} will be returned. - * - * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it! - */ - @Deprecated - public final long context() { - return sslCtxPointer(); - } - - /** - * Returns the stats of this context. - * - * @deprecated use {@link #sessionContext#stats()} - */ - @Deprecated - public final OpenSslSessionStats stats() { - return sessionContext().stats(); - } - - /** - * {@deprecated Renegotiation is not supported} - * Specify if remote initiated renegotiation is supported or not. If not supported and the remote side tries - * to initiate a renegotiation a {@link SSLHandshakeException} will be thrown during decoding. - */ - @Deprecated - public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) { - if (!rejectRemoteInitiatedRenegotiation) { - throw new UnsupportedOperationException("Renegotiation is not supported"); - } - } - - /** - * {@deprecated Renegotiation is not supported} - * @return {@code true} because renegotiation is not supported. - */ - @Deprecated - public boolean getRejectRemoteInitiatedRenegotiation() { - return true; - } - - /** - * Set the size of the buffer used by the BIO for non-application based writes - * (e.g. handshake, renegotiation, etc...). - */ - public void setBioNonApplicationBufferSize(int bioNonApplicationBufferSize) { - this.bioNonApplicationBufferSize = - checkPositiveOrZero(bioNonApplicationBufferSize, "bioNonApplicationBufferSize"); - } - - /** - * Returns the size of the buffer used by the BIO for non-application based writes - */ - public int getBioNonApplicationBufferSize() { - return bioNonApplicationBufferSize; - } - - /** - * Sets the SSL session ticket keys of this context. - * - * @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])} - */ - @Deprecated - public final void setTicketKeys(byte[] keys) { - sessionContext().setTicketKeys(keys); - } - - @Override - public abstract OpenSslSessionContext sessionContext(); - - /** - * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}. - * Be aware that it is freed as soon as the {@link #release()} method is called. - * At this point {@code 0} will be returned. - * - * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it! - */ - @Deprecated - public final long sslCtxPointer() { - Lock readerLock = ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.getSslCtx(ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Set the {@link OpenSslPrivateKeyMethod} to use. This allows to offload private-key operations - * if needed. - * - * This method is currently only supported when {@code BoringSSL} is used. - * - * @param method method to use. - */ - @UnstableApi - public final void setPrivateKeyMethod(OpenSslPrivateKeyMethod method) { - ObjectUtil.checkNotNull(method, "method"); - Lock writerLock = ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, method)); - } finally { - writerLock.unlock(); - } - } - - public final void setUseTasks(boolean useTasks) { - Lock writerLock = ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setUseTasks(ctx, useTasks); - } finally { - writerLock.unlock(); - } - } - - // IMPORTANT: This method must only be called from either the constructor or the finalizer as a user MUST never - // get access to an OpenSslSessionContext after this method was called to prevent the user from - // producing a segfault. - private void destroy() { - Lock writerLock = ctxLock.writeLock(); - writerLock.lock(); - try { - if (ctx != 0) { - if (enableOcsp) { - SSLContext.disableOcsp(ctx); - } - - SSLContext.free(ctx); - ctx = 0; - - OpenSslSessionContext context = sessionContext(); - if (context != null) { - context.destroy(); - } - } - } finally { - writerLock.unlock(); - } - } - - protected static X509Certificate[] certificates(byte[][] chain) { - X509Certificate[] peerCerts = new X509Certificate[chain.length]; - for (int i = 0; i < peerCerts.length; i++) { - peerCerts[i] = new OpenSslX509Certificate(chain[i]); - } - return peerCerts; - } - - protected static X509TrustManager chooseTrustManager(TrustManager[] managers) { - for (TrustManager m : managers) { - if (m instanceof X509TrustManager) { - if (PlatformDependent.javaVersion() >= 7) { - return OpenSslX509TrustManagerWrapper.wrapIfNeeded((X509TrustManager) m); - } - return (X509TrustManager) m; - } - } - throw new IllegalStateException("no X509TrustManager found"); - } - - protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) { - for (KeyManager km : kms) { - if (km instanceof X509KeyManager) { - return (X509KeyManager) km; - } - } - throw new IllegalStateException("no X509KeyManager found"); - } - - /** - * Translate a {@link ApplicationProtocolConfig} object to a - * {@link OpenSslApplicationProtocolNegotiator} object. - * - * @param config The configuration which defines the translation - * @return The results of the translation - */ - @SuppressWarnings("deprecation") - static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) { - if (config == null) { - return NONE_PROTOCOL_NEGOTIATOR; - } - - switch (config.protocol()) { - case NONE: - return NONE_PROTOCOL_NEGOTIATOR; - case ALPN: - case NPN: - case NPN_AND_ALPN: - switch (config.selectedListenerFailureBehavior()) { - case CHOOSE_MY_LAST_PROTOCOL: - case ACCEPT: - switch (config.selectorFailureBehavior()) { - case CHOOSE_MY_LAST_PROTOCOL: - case NO_ADVERTISE: - return new OpenSslDefaultApplicationProtocolNegotiator( - config); - default: - throw new UnsupportedOperationException( - new StringBuilder("OpenSSL provider does not support ") - .append(config.selectorFailureBehavior()) - .append(" behavior").toString()); - } - default: - throw new UnsupportedOperationException( - new StringBuilder("OpenSSL provider does not support ") - .append(config.selectedListenerFailureBehavior()) - .append(" behavior").toString()); - } - default: - throw new Error(); - } - } - - @SuppressJava6Requirement(reason = "Guarded by java version check") - static boolean useExtendedTrustManager(X509TrustManager trustManager) { - return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager; - } - - @Override - public final int refCnt() { - return refCnt.refCnt(); - } - - @Override - public final ReferenceCounted retain() { - refCnt.retain(); - return this; - } - - @Override - public final ReferenceCounted retain(int increment) { - refCnt.retain(increment); - return this; - } - - @Override - public final ReferenceCounted touch() { - refCnt.touch(); - return this; - } - - @Override - public final ReferenceCounted touch(Object hint) { - refCnt.touch(hint); - return this; - } - - @Override - public final boolean release() { - return refCnt.release(); - } - - @Override - public final boolean release(int decrement) { - return refCnt.release(decrement); - } - - abstract static class AbstractCertificateVerifier extends CertificateVerifier { - private final OpenSslEngineMap engineMap; - - AbstractCertificateVerifier(OpenSslEngineMap engineMap) { - this.engineMap = engineMap; - } - - @Override - public final int verify(long ssl, byte[][] chain, String auth) { - final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - // May be null if it was destroyed in the meantime. - return CertificateVerifier.X509_V_ERR_UNSPECIFIED; - } - X509Certificate[] peerCerts = certificates(chain); - try { - verify(engine, peerCerts, auth); - return CertificateVerifier.X509_V_OK; - } catch (Throwable cause) { - logger.debug("verification of certificate failed", cause); - engine.initHandshakeException(cause); - - // Try to extract the correct error code that should be used. - if (cause instanceof OpenSslCertificateException) { - // This will never return a negative error code as its validated when constructing the - // OpenSslCertificateException. - return ((OpenSslCertificateException) cause).errorCode(); - } - if (cause instanceof CertificateExpiredException) { - return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED; - } - if (cause instanceof CertificateNotYetValidException) { - return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID; - } - if (PlatformDependent.javaVersion() >= 7) { - return translateToError(cause); - } - - // Could not detect a specific error code to use, so fallback to a default code. - return CertificateVerifier.X509_V_ERR_UNSPECIFIED; - } - } - - @SuppressJava6Requirement(reason = "Usage guarded by java version check") - private static int translateToError(Throwable cause) { - if (cause instanceof CertificateRevokedException) { - return CertificateVerifier.X509_V_ERR_CERT_REVOKED; - } - - // The X509TrustManagerImpl uses a Validator which wraps a CertPathValidatorException into - // an CertificateException. So we need to handle the wrapped CertPathValidatorException to be - // able to send the correct alert. - Throwable wrapped = cause.getCause(); - while (wrapped != null) { - if (wrapped instanceof CertPathValidatorException) { - CertPathValidatorException ex = (CertPathValidatorException) wrapped; - CertPathValidatorException.Reason reason = ex.getReason(); - if (reason == CertPathValidatorException.BasicReason.EXPIRED) { - return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED; - } - if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) { - return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID; - } - if (reason == CertPathValidatorException.BasicReason.REVOKED) { - return CertificateVerifier.X509_V_ERR_CERT_REVOKED; - } - } - wrapped = wrapped.getCause(); - } - return CertificateVerifier.X509_V_ERR_UNSPECIFIED; - } - - abstract void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, - String auth) throws Exception; - } - - private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap { - private final Map engines = PlatformDependent.newConcurrentHashMap(); - - @Override - public ReferenceCountedOpenSslEngine remove(long ssl) { - return engines.remove(ssl); - } - - @Override - public void add(ReferenceCountedOpenSslEngine engine) { - engines.put(engine.sslPointer(), engine); - } - - @Override - public ReferenceCountedOpenSslEngine get(long ssl) { - return engines.get(ssl); - } - } - - static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword) - throws SSLException { - /* Load the certificate file and private key. */ - long keyBio = 0; - long keyCertChainBio = 0; - long keyCertChainBio2 = 0; - PemEncoded encoded = null; - try { - // Only encode one time - encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, keyCertChain); - keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); - keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); - - if (key != null) { - keyBio = toBIO(ByteBufAllocator.DEFAULT, key); - } - - SSLContext.setCertificateBio( - ctx, keyCertChainBio, keyBio, - keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword); - // We may have more then one cert in the chain so add all of them now. - SSLContext.setCertificateChainBio(ctx, keyCertChainBio2, true); - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } finally { - freeBio(keyBio); - freeBio(keyCertChainBio); - freeBio(keyCertChainBio2); - if (encoded != null) { - encoded.release(); - } - } - } - - static void freeBio(long bio) { - if (bio != 0) { - SSL.freeBIO(bio); - } - } - - /** - * Return the pointer to a in-memory BIO - * or {@code 0} if the {@code key} is {@code null}. The BIO contains the content of the {@code key}. - */ - static long toBIO(ByteBufAllocator allocator, PrivateKey key) throws Exception { - if (key == null) { - return 0; - } - - PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key); - try { - return toBIO(allocator, pem.retain()); - } finally { - pem.release(); - } - } - - /** - * Return the pointer to a in-memory BIO - * or {@code 0} if the {@code certChain} is {@code null}. The BIO contains the content of the {@code certChain}. - */ - static long toBIO(ByteBufAllocator allocator, X509Certificate... certChain) throws Exception { - if (certChain == null) { - return 0; - } - - if (certChain.length == 0) { - throw new IllegalArgumentException("certChain can't be empty"); - } - - PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain); - try { - return toBIO(allocator, pem.retain()); - } finally { - pem.release(); - } - } - - static long toBIO(ByteBufAllocator allocator, PemEncoded pem) throws Exception { - try { - // We can turn direct buffers straight into BIOs. No need to - // make a yet another copy. - ByteBuf content = pem.content(); - - if (content.isDirect()) { - return newBIO(content.retainedSlice()); - } - - ByteBuf buffer = allocator.directBuffer(content.readableBytes()); - try { - buffer.writeBytes(content, content.readerIndex(), content.readableBytes()); - return newBIO(buffer.retainedSlice()); - } finally { - try { - // If the contents of the ByteBuf is sensitive (e.g. a PrivateKey) we - // need to zero out the bytes of the copy before we're releasing it. - if (pem.isSensitive()) { - SslUtils.zeroout(buffer); - } - } finally { - buffer.release(); - } - } - } finally { - pem.release(); - } - } - - private static long newBIO(ByteBuf buffer) throws Exception { - try { - long bio = SSL.newMemBIO(); - int readable = buffer.readableBytes(); - if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) { - SSL.freeBIO(bio); - throw new IllegalStateException("Could not write data to memory BIO"); - } - return bio; - } finally { - buffer.release(); - } - } - - /** - * Returns the {@link OpenSslKeyMaterialProvider} that should be used for OpenSSL. Depending on the given - * {@link KeyManagerFactory} this may cache the {@link OpenSslKeyMaterial} for better performance if it can - * ensure that the same material is always returned for the same alias. - */ - static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) { - if (factory instanceof OpenSslX509KeyManagerFactory) { - return ((OpenSslX509KeyManagerFactory) factory).newProvider(); - } - - if (factory instanceof OpenSslCachingX509KeyManagerFactory) { - // The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache. - return ((OpenSslCachingX509KeyManagerFactory) factory).newProvider(password); - } - // We can not be sure if the material may change at runtime so we will not cache it. - return new OpenSslKeyMaterialProvider(chooseX509KeyManager(factory.getKeyManagers()), password); - } - - private static final class PrivateKeyMethod implements SSLPrivateKeyMethod { - - private final OpenSslEngineMap engineMap; - private final OpenSslPrivateKeyMethod keyMethod; - PrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslPrivateKeyMethod keyMethod) { - this.engineMap = engineMap; - this.keyMethod = keyMethod; - } - - private ReferenceCountedOpenSslEngine retrieveEngine(long ssl) throws SSLException { - ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - throw new SSLException("Could not find a " + - StringUtil.simpleClassName(ReferenceCountedOpenSslEngine.class) + " for sslPointer " + ssl); - } - return engine; - } - - @Override - public byte[] sign(long ssl, int signatureAlgorithm, byte[] digest) throws Exception { - ReferenceCountedOpenSslEngine engine = retrieveEngine(ssl); - try { - return verifyResult(keyMethod.sign(engine, signatureAlgorithm, digest)); - } catch (Exception e) { - engine.initHandshakeException(e); - throw e; - } - } - - @Override - public byte[] decrypt(long ssl, byte[] input) throws Exception { - ReferenceCountedOpenSslEngine engine = retrieveEngine(ssl); - try { - return verifyResult(keyMethod.decrypt(engine, input)); - } catch (Exception e) { - engine.initHandshakeException(e); - throw e; - } - } - - private static byte[] verifyResult(byte[] result) throws SignatureException { - if (result == null) { - throw new SignatureException(); - } - return result; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java deleted file mode 100644 index b404a1076b..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ /dev/null @@ -1,2467 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.internal.tcnative.Buffer; -import io.netty.internal.tcnative.SSL; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SuppressJava6Requirement; -import io.netty.util.internal.ThrowableUtil; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.ByteBuffer; -import java.nio.ReadOnlyBufferException; -import java.security.Principal; -import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.Lock; - -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionBindingEvent; -import javax.net.ssl.SSLSessionBindingListener; -import javax.net.ssl.SSLSessionContext; -import javax.security.cert.X509Certificate; - -import static io.netty.handler.ssl.OpenSsl.memoryAddress; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V2_HELLO; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_SSL_V3; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_1; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_2; -import static io.netty.handler.ssl.SslUtils.PROTOCOL_TLS_V1_3; -import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; -import static io.netty.util.internal.ObjectUtil.checkNotNull; -import static java.lang.Integer.MAX_VALUE; -import static java.lang.Math.min; -import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED; -import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_TASK; -import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP; -import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_WRAP; -import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; -import static javax.net.ssl.SSLEngineResult.Status.BUFFER_OVERFLOW; -import static javax.net.ssl.SSLEngineResult.Status.BUFFER_UNDERFLOW; -import static javax.net.ssl.SSLEngineResult.Status.CLOSED; -import static javax.net.ssl.SSLEngineResult.Status.OK; - -/** - * Implements a {@link SSLEngine} using - * OpenSSL BIO abstractions. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must be released before the {@link ReferenceCountedOpenSslContext} - * the instance depends upon are released. Otherwise if any method of this class is called which uses the - * the {@link ReferenceCountedOpenSslContext} JNI resources the JVM may crash. - */ -public class ReferenceCountedOpenSslEngine extends SSLEngine implements ReferenceCounted, ApplicationProtocolAccessor { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(ReferenceCountedOpenSslEngine.class); - - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslEngine.class); - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2 = 0; - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3 = 1; - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1 = 2; - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1 = 3; - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2 = 4; - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3 = 5; - private static final int[] OPENSSL_OP_NO_PROTOCOLS = { - SSL.SSL_OP_NO_SSLv2, - SSL.SSL_OP_NO_SSLv3, - SSL.SSL_OP_NO_TLSv1, - SSL.SSL_OP_NO_TLSv1_1, - SSL.SSL_OP_NO_TLSv1_2, - SSL.SSL_OP_NO_TLSv1_3 - }; - - /** - * Depends upon tcnative ... only use if tcnative is available! - */ - static final int MAX_PLAINTEXT_LENGTH = SSL.SSL_MAX_PLAINTEXT_LENGTH; - /** - * Depends upon tcnative ... only use if tcnative is available! - */ - private static final int MAX_RECORD_SIZE = SSL.SSL_MAX_RECORD_LENGTH; - - private static final SSLEngineResult NEED_UNWRAP_OK = new SSLEngineResult(OK, NEED_UNWRAP, 0, 0); - private static final SSLEngineResult NEED_UNWRAP_CLOSED = new SSLEngineResult(CLOSED, NEED_UNWRAP, 0, 0); - private static final SSLEngineResult NEED_WRAP_OK = new SSLEngineResult(OK, NEED_WRAP, 0, 0); - private static final SSLEngineResult NEED_WRAP_CLOSED = new SSLEngineResult(CLOSED, NEED_WRAP, 0, 0); - private static final SSLEngineResult CLOSED_NOT_HANDSHAKING = new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0); - - // OpenSSL state - private long ssl; - private long networkBIO; - - private enum HandshakeState { - /** - * Not started yet. - */ - NOT_STARTED, - /** - * Started via unwrap/wrap. - */ - STARTED_IMPLICITLY, - /** - * Started via {@link #beginHandshake()}. - */ - STARTED_EXPLICITLY, - /** - * Handshake is finished. - */ - FINISHED - } - - private HandshakeState handshakeState = HandshakeState.NOT_STARTED; - private boolean receivedShutdown; - private volatile boolean destroyed; - private volatile String applicationProtocol; - private volatile boolean needTask; - - // Reference Counting - private final ResourceLeakTracker leak; - private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() { - @Override - public ReferenceCounted touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - - return ReferenceCountedOpenSslEngine.this; - } - - @Override - protected void deallocate() { - shutdown(); - if (leak != null) { - boolean closed = leak.close(ReferenceCountedOpenSslEngine.this); - assert closed; - } - parentContext.release(); - } - }; - - private volatile ClientAuth clientAuth = ClientAuth.NONE; - private volatile Certificate[] localCertificateChain; - - // Updated once a new handshake is started and so the SSLSession reused. - private volatile long lastAccessed = -1; - - private String endPointIdentificationAlgorithm; - // Store as object as AlgorithmConstraints only exists since java 7. - private Object algorithmConstraints; - private List sniHostNames; - - // Mark as volatile as accessed by checkSniHostnameMatch(...) and also not specify the SNIMatcher type to allow us - // using it with java7. - private volatile Collection matchers; - - // SSL Engine status variables - private boolean isInboundDone; - private boolean outboundClosed; - - final boolean jdkCompatibilityMode; - private final boolean clientMode; - final ByteBufAllocator alloc; - private final OpenSslEngineMap engineMap; - private final OpenSslApplicationProtocolNegotiator apn; - private final ReferenceCountedOpenSslContext parentContext; - private final OpenSslSession session; - private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1]; - private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1]; - private final boolean enableOcsp; - private int maxWrapOverhead; - private int maxWrapBufferSize; - private Throwable pendingException; - - /** - * Create a new instance. - * @param context Reference count release responsibility is not transferred! The callee still owns this object. - * @param alloc The allocator to use. - * @param peerHost The peer host name. - * @param peerPort The peer port. - * @param jdkCompatibilityMode {@code true} to behave like described in - * https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html. - * {@code false} allows for partial and/or multiple packets to be process in a single - * wrap or unwrap call. - * @param leakDetection {@code true} to enable leak detection of this object. - */ - ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, final ByteBufAllocator alloc, String peerHost, - int peerPort, boolean jdkCompatibilityMode, boolean leakDetection) { - super(peerHost, peerPort); - OpenSsl.ensureAvailability(); - this.alloc = checkNotNull(alloc, "alloc"); - apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator(); - clientMode = context.isClient(); - if (PlatformDependent.javaVersion() >= 7) { - session = new ExtendedOpenSslSession(new DefaultOpenSslSession(context.sessionContext())) { - private String[] peerSupportedSignatureAlgorithms; - private List requestedServerNames; - - @Override - public List getRequestedServerNames() { - if (clientMode) { - return Java8SslUtils.getSniHostNames(sniHostNames); - } else { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (requestedServerNames == null) { - if (isDestroyed()) { - requestedServerNames = Collections.emptyList(); - } else { - String name = SSL.getSniHostname(ssl); - if (name == null) { - requestedServerNames = Collections.emptyList(); - } else { - // Convert to bytes as we do not want to do any strict validation of the - // SNIHostName while creating it. - requestedServerNames = - Java8SslUtils.getSniHostName( - SSL.getSniHostname(ssl).getBytes(CharsetUtil.UTF_8)); - } - } - } - return requestedServerNames; - } - } - } - - @Override - public String[] getPeerSupportedSignatureAlgorithms() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (peerSupportedSignatureAlgorithms == null) { - if (isDestroyed()) { - peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS; - } else { - String[] algs = SSL.getSigAlgs(ssl); - if (algs == null) { - peerSupportedSignatureAlgorithms = EmptyArrays.EMPTY_STRINGS; - } else { - Set algorithmList = new LinkedHashSet(algs.length); - for (String alg: algs) { - String converted = SignatureAlgorithmConverter.toJavaName(alg); - - if (converted != null) { - algorithmList.add(converted); - } - } - peerSupportedSignatureAlgorithms = algorithmList.toArray(new String[0]); - } - } - } - return peerSupportedSignatureAlgorithms.clone(); - } - } - - @Override - public List getStatusResponses() { - byte[] ocspResponse = null; - if (enableOcsp && clientMode) { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - ocspResponse = SSL.getOcspResponse(ssl); - } - } - } - return ocspResponse == null ? - Collections.emptyList() : Collections.singletonList(ocspResponse); - } - }; - } else { - session = new DefaultOpenSslSession(context.sessionContext()); - } - engineMap = context.engineMap; - enableOcsp = context.enableOcsp; - if (!context.sessionContext().useKeyManager()) { - // If we do not use the KeyManagerFactory we need to set localCertificateChain now. - // When we use a KeyManagerFactory it will be set during setKeyMaterial(...). - localCertificateChain = context.keyCertChain; - } - - this.jdkCompatibilityMode = jdkCompatibilityMode; - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - final long finalSsl; - try { - finalSsl = SSL.newSSL(context.ctx, !context.isClient()); - } finally { - readerLock.unlock(); - } - synchronized (this) { - ssl = finalSsl; - try { - networkBIO = SSL.bioNewByteBuffer(ssl, context.getBioNonApplicationBufferSize()); - - // Set the client auth mode, this needs to be done via setClientAuth(...) method so we actually call the - // needed JNI methods. - setClientAuth(clientMode ? ClientAuth.NONE : context.clientAuth); - - if (context.protocols != null) { - setEnabledProtocols(context.protocols); - } - - // Use SNI if peerHost was specified and a valid hostname - // See https://github.com/netty/netty/issues/4746 - if (clientMode && SslUtils.isValidHostNameForSNI(peerHost)) { - SSL.setTlsExtHostName(ssl, peerHost); - sniHostNames = Collections.singletonList(peerHost); - } - - if (enableOcsp) { - SSL.enableOcsp(ssl); - } - - if (!jdkCompatibilityMode) { - SSL.setMode(ssl, SSL.getMode(ssl) | SSL.SSL_MODE_ENABLE_PARTIAL_WRITE); - } - - // setMode may impact the overhead. - calculateMaxWrapOverhead(); - } catch (Throwable cause) { - // Call shutdown so we are sure we correctly release all native memory and also guard against the - // case when shutdown() will be called by the finalizer again. - shutdown(); - - PlatformDependent.throwException(cause); - } - } - - // Now that everything looks good and we're going to successfully return the - // object so we need to retain a reference to the parent context. - parentContext = context; - parentContext.retain(); - - // Only create the leak after everything else was executed and so ensure we don't produce a false-positive for - // the ResourceLeakDetector. - leak = leakDetection ? leakDetector.track(this) : null; - } - - final synchronized String[] authMethods() { - if (isDestroyed()) { - return EmptyArrays.EMPTY_STRINGS; - } - return SSL.authenticationMethods(ssl); - } - - final boolean setKeyMaterial(OpenSslKeyMaterial keyMaterial) throws Exception { - synchronized (this) { - if (isDestroyed()) { - return false; - } - SSL.setKeyMaterial(ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); - } - localCertificateChain = keyMaterial.certificateChain(); - return true; - } - - final synchronized SecretKeySpec masterKey() { - if (isDestroyed()) { - return null; - } - return new SecretKeySpec(SSL.getMasterKey(ssl), "AES"); - } - - /** - * Sets the OCSP response. - */ - @UnstableApi - public void setOcspResponse(byte[] response) { - if (!enableOcsp) { - throw new IllegalStateException("OCSP stapling is not enabled"); - } - - if (clientMode) { - throw new IllegalStateException("Not a server SSLEngine"); - } - - synchronized (this) { - if (!isDestroyed()) { - SSL.setOcspResponse(ssl, response); - } - } - } - - /** - * Returns the OCSP response or {@code null} if the server didn't provide a stapled OCSP response. - */ - @UnstableApi - public byte[] getOcspResponse() { - if (!enableOcsp) { - throw new IllegalStateException("OCSP stapling is not enabled"); - } - - if (!clientMode) { - throw new IllegalStateException("Not a client SSLEngine"); - } - - synchronized (this) { - if (isDestroyed()) { - return EmptyArrays.EMPTY_BYTES; - } - return SSL.getOcspResponse(ssl); - } - } - - @Override - public final int refCnt() { - return refCnt.refCnt(); - } - - @Override - public final ReferenceCounted retain() { - refCnt.retain(); - return this; - } - - @Override - public final ReferenceCounted retain(int increment) { - refCnt.retain(increment); - return this; - } - - @Override - public final ReferenceCounted touch() { - refCnt.touch(); - return this; - } - - @Override - public final ReferenceCounted touch(Object hint) { - refCnt.touch(hint); - return this; - } - - @Override - public final boolean release() { - return refCnt.release(); - } - - @Override - public final boolean release(int decrement) { - return refCnt.release(decrement); - } - - @Override - public final synchronized SSLSession getHandshakeSession() { - // Javadocs state return value should be: - // null if this instance is not currently handshaking, or if the current handshake has not - // progressed far enough to create a basic SSLSession. Otherwise, this method returns the - // SSLSession currently being negotiated. - switch(handshakeState) { - case NOT_STARTED: - case FINISHED: - return null; - default: - return session; - } - } - - /** - * Returns the pointer to the {@code SSL} object for this {@link ReferenceCountedOpenSslEngine}. - * Be aware that it is freed as soon as the {@link #release()} or {@link #shutdown()} methods are called. - * At this point {@code 0} will be returned. - */ - public final synchronized long sslPointer() { - return ssl; - } - - /** - * Destroys this engine. - */ - public final synchronized void shutdown() { - if (!destroyed) { - destroyed = true; - engineMap.remove(ssl); - SSL.freeSSL(ssl); - ssl = networkBIO = 0; - - isInboundDone = outboundClosed = true; - } - - // On shutdown clear all errors - SSL.clearError(); - } - - /** - * Write plaintext data to the OpenSSL internal BIO - * - * Calling this function with src.remaining == 0 is undefined. - */ - private int writePlaintextData(final ByteBuffer src, int len) { - final int pos = src.position(); - final int limit = src.limit(); - final int sslWrote; - - if (src.isDirect()) { - sslWrote = SSL.writeToSSL(ssl, bufferAddress(src) + pos, len); - if (sslWrote > 0) { - src.position(pos + sslWrote); - } - } else { - ByteBuf buf = alloc.directBuffer(len); - try { - src.limit(pos + len); - - buf.setBytes(0, src); - src.limit(limit); - - sslWrote = SSL.writeToSSL(ssl, memoryAddress(buf), len); - if (sslWrote > 0) { - src.position(pos + sslWrote); - } else { - src.position(pos); - } - } finally { - buf.release(); - } - } - return sslWrote; - } - - /** - * Write encrypted data to the OpenSSL network BIO. - */ - private ByteBuf writeEncryptedData(final ByteBuffer src, int len) { - final int pos = src.position(); - if (src.isDirect()) { - SSL.bioSetByteBuffer(networkBIO, bufferAddress(src) + pos, len, false); - } else { - final ByteBuf buf = alloc.directBuffer(len); - try { - final int limit = src.limit(); - src.limit(pos + len); - buf.writeBytes(src); - // Restore the original position and limit because we don't want to consume from `src`. - src.position(pos); - src.limit(limit); - - SSL.bioSetByteBuffer(networkBIO, memoryAddress(buf), len, false); - return buf; - } catch (Throwable cause) { - buf.release(); - PlatformDependent.throwException(cause); - } - } - return null; - } - - /** - * Read plaintext data from the OpenSSL internal BIO - */ - private int readPlaintextData(final ByteBuffer dst) { - final int sslRead; - final int pos = dst.position(); - if (dst.isDirect()) { - sslRead = SSL.readFromSSL(ssl, bufferAddress(dst) + pos, dst.limit() - pos); - if (sslRead > 0) { - dst.position(pos + sslRead); - } - } else { - final int limit = dst.limit(); - final int len = min(maxEncryptedPacketLength0(), limit - pos); - final ByteBuf buf = alloc.directBuffer(len); - try { - sslRead = SSL.readFromSSL(ssl, memoryAddress(buf), len); - if (sslRead > 0) { - dst.limit(pos + sslRead); - buf.getBytes(buf.readerIndex(), dst); - dst.limit(limit); - } - } finally { - buf.release(); - } - } - - return sslRead; - } - - /** - * Visible only for testing! - */ - final synchronized int maxWrapOverhead() { - return maxWrapOverhead; - } - - /** - * Visible only for testing! - */ - final synchronized int maxEncryptedPacketLength() { - return maxEncryptedPacketLength0(); - } - - /** - * This method is intentionally not synchronized, only use if you know you are in the EventLoop - * thread and visibility on {@link #maxWrapOverhead} is achieved via other synchronized blocks. - */ - final int maxEncryptedPacketLength0() { - return maxWrapOverhead + MAX_PLAINTEXT_LENGTH; - } - - /** - * This method is intentionally not synchronized, only use if you know you are in the EventLoop - * thread and visibility on {@link #maxWrapBufferSize} and {@link #maxWrapOverhead} is achieved - * via other synchronized blocks. - */ - final int calculateMaxLengthForWrap(int plaintextLength, int numComponents) { - return (int) min(maxWrapBufferSize, plaintextLength + (long) maxWrapOverhead * numComponents); - } - - final synchronized int sslPending() { - return sslPending0(); - } - - /** - * It is assumed this method is called in a synchronized block (or the constructor)! - */ - private void calculateMaxWrapOverhead() { - maxWrapOverhead = SSL.getMaxWrapOverhead(ssl); - - // maxWrapBufferSize must be set after maxWrapOverhead because there is a dependency on this value. - // If jdkCompatibility mode is off we allow enough space to encrypt 16 buffers at a time. This could be - // configurable in the future if necessary. - maxWrapBufferSize = jdkCompatibilityMode ? maxEncryptedPacketLength0() : maxEncryptedPacketLength0() << 4; - } - - private int sslPending0() { - // OpenSSL has a limitation where if you call SSL_pending before the handshake is complete OpenSSL will throw a - // "called a function you should not call" error. Using the TLS_method instead of SSLv23_method may solve this - // issue but this API is only available in 1.1.0+ [1]. - // [1] https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_new.html - return handshakeState != HandshakeState.FINISHED ? 0 : SSL.sslPending(ssl); - } - - private boolean isBytesAvailableEnoughForWrap(int bytesAvailable, int plaintextLength, int numComponents) { - return bytesAvailable - (long) maxWrapOverhead * numComponents >= plaintextLength; - } - - @Override - public final SSLEngineResult wrap( - final ByteBuffer[] srcs, int offset, final int length, final ByteBuffer dst) throws SSLException { - // Throw required runtime exceptions - if (srcs == null) { - throw new IllegalArgumentException("srcs is null"); - } - if (dst == null) { - throw new IllegalArgumentException("dst is null"); - } - - if (offset >= srcs.length || offset + length > srcs.length) { - throw new IndexOutOfBoundsException( - "offset: " + offset + ", length: " + length + - " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))"); - } - - if (dst.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - - synchronized (this) { - if (isOutboundDone()) { - // All drained in the outbound buffer - return isInboundDone() || isDestroyed() ? CLOSED_NOT_HANDSHAKING : NEED_UNWRAP_CLOSED; - } - - int bytesProduced = 0; - ByteBuf bioReadCopyBuf = null; - try { - // Setup the BIO buffer so that we directly write the encryption results into dst. - if (dst.isDirect()) { - SSL.bioSetByteBuffer(networkBIO, bufferAddress(dst) + dst.position(), dst.remaining(), - true); - } else { - bioReadCopyBuf = alloc.directBuffer(dst.remaining()); - SSL.bioSetByteBuffer(networkBIO, memoryAddress(bioReadCopyBuf), bioReadCopyBuf.writableBytes(), - true); - } - - int bioLengthBefore = SSL.bioLengthByteBuffer(networkBIO); - - // Explicitly use outboundClosed as we want to drain any bytes that are still present. - if (outboundClosed) { - // If the outbound was closed we want to ensure we can produce the alert to the destination buffer. - // This is true even if we not using jdkCompatibilityMode. - // - // We use a plaintextLength of 2 as we at least want to have an alert fit into it. - // https://tools.ietf.org/html/rfc5246#section-7.2 - if (!isBytesAvailableEnoughForWrap(dst.remaining(), 2, 1)) { - return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); - } - - // There is something left to drain. - // See https://github.com/netty/netty/issues/6260 - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - if (bytesProduced <= 0) { - return newResultMayFinishHandshake(NOT_HANDSHAKING, 0, 0); - } - // It is possible when the outbound was closed there was not enough room in the non-application - // buffers to hold the close_notify. We should keep trying to close until we consume all the data - // OpenSSL can give us. - if (!doSSLShutdown()) { - return newResultMayFinishHandshake(NOT_HANDSHAKING, 0, bytesProduced); - } - bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO); - return newResultMayFinishHandshake(NEED_WRAP, 0, bytesProduced); - } - - // Flush any data that may be implicitly generated by OpenSSL (handshake, close, etc..). - SSLEngineResult.HandshakeStatus status = NOT_HANDSHAKING; - // Prepare OpenSSL to work in server mode and receive handshake - if (handshakeState != HandshakeState.FINISHED) { - if (handshakeState != HandshakeState.STARTED_EXPLICITLY) { - // Update accepted so we know we triggered the handshake via wrap - handshakeState = HandshakeState.STARTED_IMPLICITLY; - } - - // Flush any data that may have been written implicitly during the handshake by OpenSSL. - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - - if (pendingException != null) { - // TODO(scott): It is possible that when the handshake failed there was not enough room in the - // non-application buffers to hold the alert. We should get all the data before progressing on. - // However I'm not aware of a way to do this with the OpenSSL APIs. - // See https://github.com/netty/netty/issues/6385. - - // We produced / consumed some data during the handshake, signal back to the caller. - // If there is a handshake exception and we have produced data, we should send the data before - // we allow handshake() to throw the handshake exception. - // - // When the user calls wrap() again we will propagate the handshake error back to the user as - // soon as there is no more data to was produced (as part of an alert etc). - if (bytesProduced > 0) { - return newResult(NEED_WRAP, 0, bytesProduced); - } - // Nothing was produced see if there is a handshakeException that needs to be propagated - // to the caller by calling handshakeException() which will return the right HandshakeStatus - // if it can "recover" from the exception for now. - return newResult(handshakeException(), 0, 0); - } - - status = handshake(); - - // Handshake may have generated more data, for example if the internal SSL buffer is small - // we may have freed up space by flushing above. - bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO); - - if (status == NEED_TASK) { - return newResult(status, 0, bytesProduced); - } - - if (bytesProduced > 0) { - // If we have filled up the dst buffer and we have not finished the handshake we should try to - // wrap again. Otherwise we should only try to wrap again if there is still data pending in - // SSL buffers. - return newResult(mayFinishHandshake(status != FINISHED ? - bytesProduced == bioLengthBefore ? NEED_WRAP : - getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) : FINISHED), - 0, bytesProduced); - } - - if (status == NEED_UNWRAP) { - // Signal if the outbound is done or not. - return isOutboundDone() ? NEED_UNWRAP_CLOSED : NEED_UNWRAP_OK; - } - - // Explicit use outboundClosed and not outboundClosed() as we want to drain any bytes that are - // still present. - if (outboundClosed) { - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - return newResultMayFinishHandshake(status, 0, bytesProduced); - } - } - - final int endOffset = offset + length; - if (jdkCompatibilityMode) { - int srcsLen = 0; - for (int i = offset; i < endOffset; ++i) { - final ByteBuffer src = srcs[i]; - if (src == null) { - throw new IllegalArgumentException("srcs[" + i + "] is null"); - } - if (srcsLen == MAX_PLAINTEXT_LENGTH) { - continue; - } - - srcsLen += src.remaining(); - if (srcsLen > MAX_PLAINTEXT_LENGTH || srcsLen < 0) { - // If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to MAX_PLAINTEXT_LENGTH. - // This also help us to guard against overflow. - // We not break out here as we still need to check for null entries in srcs[]. - srcsLen = MAX_PLAINTEXT_LENGTH; - } - } - - // jdkCompatibilityMode will only produce a single TLS packet, and we don't aggregate src buffers, - // so we always fix the number of buffers to 1 when checking if the dst buffer is large enough. - if (!isBytesAvailableEnoughForWrap(dst.remaining(), srcsLen, 1)) { - return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); - } - } - - // There was no pending data in the network BIO -- encrypt any application data - int bytesConsumed = 0; - assert bytesProduced == 0; - - // Flush any data that may have been written implicitly by OpenSSL in case a shutdown/alert occurs. - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - - if (bytesProduced > 0) { - return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced); - } - // There was a pending exception that we just delayed because there was something to produce left. - // Throw it now and shutdown the engine. - if (pendingException != null) { - Throwable error = pendingException; - pendingException = null; - shutdown(); - // Throw a new exception wrapping the pending exception, so the stacktrace is meaningful and - // contains all the details. - throw new SSLException(error); - } - - for (; offset < endOffset; ++offset) { - final ByteBuffer src = srcs[offset]; - final int remaining = src.remaining(); - if (remaining == 0) { - continue; - } - - final int bytesWritten; - if (jdkCompatibilityMode) { - // Write plaintext application data to the SSL engine. We don't have to worry about checking - // if there is enough space if jdkCompatibilityMode because we only wrap at most - // MAX_PLAINTEXT_LENGTH and we loop over the input before hand and check if there is space. - bytesWritten = writePlaintextData(src, min(remaining, MAX_PLAINTEXT_LENGTH - bytesConsumed)); - } else { - // OpenSSL's SSL_write keeps state between calls. We should make sure the amount we attempt to - // write is guaranteed to succeed so we don't have to worry about keeping state consistent - // between calls. - final int availableCapacityForWrap = dst.remaining() - bytesProduced - maxWrapOverhead; - if (availableCapacityForWrap <= 0) { - return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, - bytesProduced); - } - bytesWritten = writePlaintextData(src, min(remaining, availableCapacityForWrap)); - } - - // Determine how much encrypted data was generated. - // - // Even if SSL_write doesn't consume any application data it is possible that OpenSSL will - // produce non-application data into the BIO. For example session tickets.... - // See https://github.com/netty/netty/issues/10041 - final int pendingNow = SSL.bioLengthByteBuffer(networkBIO); - bytesProduced += bioLengthBefore - pendingNow; - bioLengthBefore = pendingNow; - - if (bytesWritten > 0) { - bytesConsumed += bytesWritten; - - if (jdkCompatibilityMode || bytesProduced == dst.remaining()) { - return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced); - } - } else { - int sslError = SSL.getError(ssl, bytesWritten); - if (sslError == SSL.SSL_ERROR_ZERO_RETURN) { - // This means the connection was shutdown correctly, close inbound and outbound - if (!receivedShutdown) { - closeAll(); - - bytesProduced += bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO); - - // If we have filled up the dst buffer and we have not finished the handshake we should - // try to wrap again. Otherwise we should only try to wrap again if there is still data - // pending in SSL buffers. - SSLEngineResult.HandshakeStatus hs = mayFinishHandshake( - status != FINISHED ? bytesProduced == dst.remaining() ? NEED_WRAP - : getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) - : FINISHED); - return newResult(hs, bytesConsumed, bytesProduced); - } - - return newResult(NOT_HANDSHAKING, bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_READ) { - // If there is no pending data to read from BIO we should go back to event loop and try - // to read more data [1]. It is also possible that event loop will detect the socket has - // been closed. [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html - return newResult(NEED_UNWRAP, bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_WRITE) { - // SSL_ERROR_WANT_WRITE typically means that the underlying transport is not writable - // and we should set the "want write" flag on the selector and try again when the - // underlying transport is writable [1]. However we are not directly writing to the - // underlying transport and instead writing to a BIO buffer. The OpenSsl documentation - // says we should do the following [1]: - // - // "When using a buffering BIO, like a BIO pair, data must be written into or retrieved - // out of the BIO before being able to continue." - // - // In practice this means the destination buffer doesn't have enough space for OpenSSL - // to write encrypted data to. This is an OVERFLOW condition. - // [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html - if (bytesProduced > 0) { - // If we produced something we should report this back and let the user call - // wrap again. - return newResult(NEED_WRAP, bytesConsumed, bytesProduced); - } - return newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP || - sslError == SSL.SSL_ERROR_WANT_CERTIFICATE_VERIFY || - sslError == SSL.SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - - return newResult(NEED_TASK, bytesConsumed, bytesProduced); - } else { - // Everything else is considered as error - throw shutdownWithError("SSL_write", sslError); - } - } - } - return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced); - } finally { - SSL.bioClearByteBuffer(networkBIO); - if (bioReadCopyBuf == null) { - dst.position(dst.position() + bytesProduced); - } else { - assert bioReadCopyBuf.readableBytes() <= dst.remaining() : "The destination buffer " + dst + - " didn't have enough remaining space to hold the encrypted content in " + bioReadCopyBuf; - dst.put(bioReadCopyBuf.internalNioBuffer(bioReadCopyBuf.readerIndex(), bytesProduced)); - bioReadCopyBuf.release(); - } - } - } - } - - private SSLEngineResult newResult(SSLEngineResult.HandshakeStatus hs, int bytesConsumed, int bytesProduced) { - return newResult(OK, hs, bytesConsumed, bytesProduced); - } - - private SSLEngineResult newResult(SSLEngineResult.Status status, SSLEngineResult.HandshakeStatus hs, - int bytesConsumed, int bytesProduced) { - // If isOutboundDone, then the data from the network BIO - // was the close_notify message and all was consumed we are not required to wait - // for the receipt the peer's close_notify message -- shutdown. - if (isOutboundDone()) { - if (isInboundDone()) { - // If the inbound was done as well, we need to ensure we return NOT_HANDSHAKING to signal we are done. - hs = NOT_HANDSHAKING; - - // As the inbound and the outbound is done we can shutdown the engine now. - shutdown(); - } - return new SSLEngineResult(CLOSED, hs, bytesConsumed, bytesProduced); - } - if (hs == NEED_TASK) { - // Set needTask to true so getHandshakeStatus() will return the correct value. - needTask = true; - } - return new SSLEngineResult(status, hs, bytesConsumed, bytesProduced); - } - - private SSLEngineResult newResultMayFinishHandshake(SSLEngineResult.HandshakeStatus hs, - int bytesConsumed, int bytesProduced) throws SSLException { - return newResult(mayFinishHandshake(hs != FINISHED ? getHandshakeStatus() : FINISHED), - bytesConsumed, bytesProduced); - } - - private SSLEngineResult newResultMayFinishHandshake(SSLEngineResult.Status status, - SSLEngineResult.HandshakeStatus hs, - int bytesConsumed, int bytesProduced) throws SSLException { - return newResult(status, mayFinishHandshake(hs != FINISHED ? getHandshakeStatus() : FINISHED), - bytesConsumed, bytesProduced); - } - - /** - * Log the error, shutdown the engine and throw an exception. - */ - private SSLException shutdownWithError(String operations, int sslError) { - return shutdownWithError(operations, sslError, SSL.getLastErrorNumber()); - } - - private SSLException shutdownWithError(String operation, int sslError, int error) { - String errorString = SSL.getErrorString(error); - if (logger.isDebugEnabled()) { - logger.debug("{} failed with {}: OpenSSL error: {} {}", - operation, sslError, error, errorString); - } - - // There was an internal error -- shutdown - shutdown(); - if (handshakeState == HandshakeState.FINISHED) { - return new SSLException(errorString); - } - - SSLHandshakeException exception = new SSLHandshakeException(errorString); - // If we have a handshakeException stored already we should include it as well to help the user debug things. - if (pendingException != null) { - exception.initCause(pendingException); - pendingException = null; - } - return exception; - } - - public final SSLEngineResult unwrap( - final ByteBuffer[] srcs, int srcsOffset, final int srcsLength, - final ByteBuffer[] dsts, int dstsOffset, final int dstsLength) throws SSLException { - - // Throw required runtime exceptions - ObjectUtil.checkNotNull(srcs, "srcs"); - if (srcsOffset >= srcs.length - || srcsOffset + srcsLength > srcs.length) { - throw new IndexOutOfBoundsException( - "offset: " + srcsOffset + ", length: " + srcsLength + - " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))"); - } - if (dsts == null) { - throw new IllegalArgumentException("dsts is null"); - } - if (dstsOffset >= dsts.length || dstsOffset + dstsLength > dsts.length) { - throw new IndexOutOfBoundsException( - "offset: " + dstsOffset + ", length: " + dstsLength + - " (expected: offset <= offset + length <= dsts.length (" + dsts.length + "))"); - } - long capacity = 0; - final int dstsEndOffset = dstsOffset + dstsLength; - for (int i = dstsOffset; i < dstsEndOffset; i ++) { - ByteBuffer dst = dsts[i]; - if (dst == null) { - throw new IllegalArgumentException("dsts[" + i + "] is null"); - } - if (dst.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - capacity += dst.remaining(); - } - - final int srcsEndOffset = srcsOffset + srcsLength; - long len = 0; - for (int i = srcsOffset; i < srcsEndOffset; i++) { - ByteBuffer src = srcs[i]; - if (src == null) { - throw new IllegalArgumentException("srcs[" + i + "] is null"); - } - len += src.remaining(); - } - - synchronized (this) { - if (isInboundDone()) { - return isOutboundDone() || isDestroyed() ? CLOSED_NOT_HANDSHAKING : NEED_WRAP_CLOSED; - } - - SSLEngineResult.HandshakeStatus status = NOT_HANDSHAKING; - // Prepare OpenSSL to work in server mode and receive handshake - if (handshakeState != HandshakeState.FINISHED) { - if (handshakeState != HandshakeState.STARTED_EXPLICITLY) { - // Update accepted so we know we triggered the handshake via wrap - handshakeState = HandshakeState.STARTED_IMPLICITLY; - } - - status = handshake(); - - if (status == NEED_TASK) { - return newResult(status, 0, 0); - } - - if (status == NEED_WRAP) { - return NEED_WRAP_OK; - } - // Check if the inbound is considered to be closed if so let us try to wrap again. - if (isInboundDone) { - return NEED_WRAP_CLOSED; - } - } - - int sslPending = sslPending0(); - int packetLength; - // The JDK implies that only a single SSL packet should be processed per unwrap call [1]. If we are in - // JDK compatibility mode then we should honor this, but if not we just wrap as much as possible. If there - // are multiple records or partial records this may reduce thrashing events through the pipeline. - // [1] https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html - if (jdkCompatibilityMode) { - if (len < SSL_RECORD_HEADER_LENGTH) { - return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0); - } - - packetLength = SslUtils.getEncryptedPacketLength(srcs, srcsOffset); - if (packetLength == SslUtils.NOT_ENCRYPTED) { - throw new NotSslRecordException("not an SSL/TLS record"); - } - - final int packetLengthDataOnly = packetLength - SSL_RECORD_HEADER_LENGTH; - if (packetLengthDataOnly > capacity) { - // Not enough space in the destination buffer so signal the caller that the buffer needs to be - // increased. - if (packetLengthDataOnly > MAX_RECORD_SIZE) { - // The packet length MUST NOT exceed 2^14 [1]. However we do accommodate more data to support - // legacy use cases which may violate this condition (e.g. OpenJDK's SslEngineImpl). If the max - // length is exceeded we fail fast here to avoid an infinite loop due to the fact that we - // won't allocate a buffer large enough. - // [1] https://tools.ietf.org/html/rfc5246#section-6.2.1 - throw new SSLException("Illegal packet length: " + packetLengthDataOnly + " > " + - session.getApplicationBufferSize()); - } else { - session.tryExpandApplicationBufferSize(packetLengthDataOnly); - } - return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0); - } - - if (len < packetLength) { - // We either don't have enough data to read the packet length or not enough for reading the whole - // packet. - return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0); - } - } else if (len == 0 && sslPending <= 0) { - return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0); - } else if (capacity == 0) { - return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0); - } else { - packetLength = (int) min(MAX_VALUE, len); - } - - // This must always be the case when we reached here as if not we returned BUFFER_UNDERFLOW. - assert srcsOffset < srcsEndOffset; - - // This must always be the case if we reached here. - assert capacity > 0; - - // Number of produced bytes - int bytesProduced = 0; - int bytesConsumed = 0; - try { - srcLoop: - for (;;) { - ByteBuffer src = srcs[srcsOffset]; - int remaining = src.remaining(); - final ByteBuf bioWriteCopyBuf; - int pendingEncryptedBytes; - if (remaining == 0) { - if (sslPending <= 0) { - // We must skip empty buffers as BIO_write will return 0 if asked to write something - // with length 0. - if (++srcsOffset >= srcsEndOffset) { - break; - } - continue; - } else { - bioWriteCopyBuf = null; - pendingEncryptedBytes = SSL.bioLengthByteBuffer(networkBIO); - } - } else { - // Write more encrypted data into the BIO. Ensure we only read one packet at a time as - // stated in the SSLEngine javadocs. - pendingEncryptedBytes = min(packetLength, remaining); - bioWriteCopyBuf = writeEncryptedData(src, pendingEncryptedBytes); - } - try { - for (;;) { - ByteBuffer dst = dsts[dstsOffset]; - if (!dst.hasRemaining()) { - // No space left in the destination buffer, skip it. - if (++dstsOffset >= dstsEndOffset) { - break srcLoop; - } - continue; - } - - int bytesRead = readPlaintextData(dst); - // We are directly using the ByteBuffer memory for the write, and so we only know what has - // been consumed after we let SSL decrypt the data. At this point we should update the - // number of bytes consumed, update the ByteBuffer position, and release temp ByteBuf. - int localBytesConsumed = pendingEncryptedBytes - SSL.bioLengthByteBuffer(networkBIO); - bytesConsumed += localBytesConsumed; - packetLength -= localBytesConsumed; - pendingEncryptedBytes -= localBytesConsumed; - src.position(src.position() + localBytesConsumed); - - if (bytesRead > 0) { - bytesProduced += bytesRead; - - if (!dst.hasRemaining()) { - sslPending = sslPending0(); - // Move to the next dst buffer as this one is full. - if (++dstsOffset >= dstsEndOffset) { - return sslPending > 0 ? - newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced) : - newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, - bytesConsumed, bytesProduced); - } - } else if (packetLength == 0 || jdkCompatibilityMode) { - // We either consumed all data or we are in jdkCompatibilityMode and have consumed - // a single TLS packet and should stop consuming until this method is called again. - break srcLoop; - } - } else { - int sslError = SSL.getError(ssl, bytesRead); - if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) { - // break to the outer loop as we want to read more data which means we need to - // write more to the BIO. - break; - } else if (sslError == SSL.SSL_ERROR_ZERO_RETURN) { - // This means the connection was shutdown correctly, close inbound and outbound - if (!receivedShutdown) { - closeAll(); - } - return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, - bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP || - sslError == SSL.SSL_ERROR_WANT_CERTIFICATE_VERIFY || - sslError == SSL.SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - return newResult(isInboundDone() ? CLOSED : OK, - NEED_TASK, bytesConsumed, bytesProduced); - } else { - return sslReadErrorResult(sslError, SSL.getLastErrorNumber(), bytesConsumed, - bytesProduced); - } - } - } - - if (++srcsOffset >= srcsEndOffset) { - break; - } - } finally { - if (bioWriteCopyBuf != null) { - bioWriteCopyBuf.release(); - } - } - } - } finally { - SSL.bioClearByteBuffer(networkBIO); - rejectRemoteInitiatedRenegotiation(); - } - - // Check to see if we received a close_notify message from the peer. - if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) { - closeAll(); - } - - return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, bytesConsumed, bytesProduced); - } - } - - private SSLEngineResult sslReadErrorResult(int error, int stackError, int bytesConsumed, int bytesProduced) - throws SSLException { - // Check if we have a pending handshakeException and if so see if we need to consume all pending data from the - // BIO first or can just shutdown and throw it now. - // This is needed so we ensure close_notify etc is correctly send to the remote peer. - // See https://github.com/netty/netty/issues/3900 - if (SSL.bioLengthNonApplication(networkBIO) > 0) { - // we seems to have data left that needs to be transferred and so the user needs - // call wrap(...). Store the error so we can pick it up later. - String message = SSL.getErrorString(stackError); - SSLException exception = handshakeState == HandshakeState.FINISHED ? - new SSLException(message) : new SSLHandshakeException(message); - if (pendingException == null) { - pendingException = exception; - } else { - ThrowableUtil.addSuppressed(pendingException, exception); - } - // We need to clear all errors so we not pick up anything that was left on the stack on the next - // operation. Note that shutdownWithError(...) will cleanup the stack as well so its only needed here. - SSL.clearError(); - return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced); - } - throw shutdownWithError("SSL_read", error, stackError); - } - - private void closeAll() throws SSLException { - receivedShutdown = true; - closeOutbound(); - closeInbound(); - } - - private void rejectRemoteInitiatedRenegotiation() throws SSLHandshakeException { - // As rejectRemoteInitiatedRenegotiation() is called in a finally block we also need to check if we shutdown - // the engine before as otherwise SSL.getHandshakeCount(ssl) will throw an NPE if the passed in ssl is 0. - // See https://github.com/netty/netty/issues/7353 - if (!isDestroyed() && SSL.getHandshakeCount(ssl) > 1 && - // As we may count multiple handshakes when TLSv1.3 is used we should just ignore this here as - // renegotiation is not supported in TLSv1.3 as per spec. - !SslUtils.PROTOCOL_TLS_V1_3.equals(session.getProtocol()) && handshakeState == HandshakeState.FINISHED) { - // TODO: In future versions me may also want to send a fatal_alert to the client and so notify it - // that the renegotiation failed. - shutdown(); - throw new SSLHandshakeException("remote-initiated renegotiation not allowed"); - } - } - - public final SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException { - return unwrap(srcs, 0, srcs.length, dsts, 0, dsts.length); - } - - private ByteBuffer[] singleSrcBuffer(ByteBuffer src) { - singleSrcBuffer[0] = src; - return singleSrcBuffer; - } - - private void resetSingleSrcBuffer() { - singleSrcBuffer[0] = null; - } - - private ByteBuffer[] singleDstBuffer(ByteBuffer src) { - singleDstBuffer[0] = src; - return singleDstBuffer; - } - - private void resetSingleDstBuffer() { - singleDstBuffer[0] = null; - } - - @Override - public final synchronized SSLEngineResult unwrap( - final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException { - try { - return unwrap(singleSrcBuffer(src), 0, 1, dsts, offset, length); - } finally { - resetSingleSrcBuffer(); - } - } - - @Override - public final synchronized SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException { - try { - return wrap(singleSrcBuffer(src), dst); - } finally { - resetSingleSrcBuffer(); - } - } - - @Override - public final synchronized SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException { - try { - return unwrap(singleSrcBuffer(src), singleDstBuffer(dst)); - } finally { - resetSingleSrcBuffer(); - resetSingleDstBuffer(); - } - } - - @Override - public final synchronized SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException { - try { - return unwrap(singleSrcBuffer(src), dsts); - } finally { - resetSingleSrcBuffer(); - } - } - - @Override - public final synchronized Runnable getDelegatedTask() { - if (isDestroyed()) { - return null; - } - final Runnable task = SSL.getTask(ssl); - if (task == null) { - return null; - } - return new Runnable() { - @Override - public void run() { - if (isDestroyed()) { - // The engine was destroyed in the meantime, just return. - return; - } - try { - task.run(); - } finally { - // The task was run, reset needTask to false so getHandshakeStatus() returns the correct value. - needTask = false; - } - } - }; - } - - @Override - public final synchronized void closeInbound() throws SSLException { - if (isInboundDone) { - return; - } - - isInboundDone = true; - - if (isOutboundDone()) { - // Only call shutdown if there is no outbound data pending. - // See https://github.com/netty/netty/issues/6167 - shutdown(); - } - - if (handshakeState != HandshakeState.NOT_STARTED && !receivedShutdown) { - throw new SSLException( - "Inbound closed before receiving peer's close_notify: possible truncation attack?"); - } - } - - @Override - public final synchronized boolean isInboundDone() { - return isInboundDone; - } - - @Override - public final synchronized void closeOutbound() { - if (outboundClosed) { - return; - } - - outboundClosed = true; - - if (handshakeState != HandshakeState.NOT_STARTED && !isDestroyed()) { - int mode = SSL.getShutdown(ssl); - if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) { - doSSLShutdown(); - } - } else { - // engine closing before initial handshake - shutdown(); - } - } - - /** - * Attempt to call {@link SSL#shutdownSSL(long)}. - * @return {@code false} if the call to {@link SSL#shutdownSSL(long)} was not attempted or returned an error. - */ - private boolean doSSLShutdown() { - if (SSL.isInInit(ssl) != 0) { - // Only try to call SSL_shutdown if we are not in the init state anymore. - // Otherwise we will see 'error:140E0197:SSL routines:SSL_shutdown:shutdown while in init' in our logs. - // - // See also http://hg.nginx.org/nginx/rev/062c189fee20 - return false; - } - int err = SSL.shutdownSSL(ssl); - if (err < 0) { - int sslErr = SSL.getError(ssl, err); - if (sslErr == SSL.SSL_ERROR_SYSCALL || sslErr == SSL.SSL_ERROR_SSL) { - if (logger.isDebugEnabled()) { - int error = SSL.getLastErrorNumber(); - logger.debug("SSL_shutdown failed: OpenSSL error: {} {}", error, SSL.getErrorString(error)); - } - // There was an internal error -- shutdown - shutdown(); - return false; - } - SSL.clearError(); - } - return true; - } - - @Override - public final synchronized boolean isOutboundDone() { - // Check if there is anything left in the outbound buffer. - // We need to ensure we only call SSL.pendingWrittenBytesInBIO(...) if the engine was not destroyed yet. - return outboundClosed && (networkBIO == 0 || SSL.bioLengthNonApplication(networkBIO) == 0); - } - - @Override - public final String[] getSupportedCipherSuites() { - return OpenSsl.AVAILABLE_CIPHER_SUITES.toArray(new String[0]); - } - - @Override - public final String[] getEnabledCipherSuites() { - final String[] extraCiphers; - final String[] enabled; - synchronized (this) { - if (!isDestroyed()) { - enabled = SSL.getCiphers(ssl); - int opts = SSL.getOptions(ssl); - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, PROTOCOL_TLS_V1_3)) { - extraCiphers = OpenSsl.EXTRA_SUPPORTED_TLS_1_3_CIPHERS; - } else { - extraCiphers = EmptyArrays.EMPTY_STRINGS; - } - } else { - return EmptyArrays.EMPTY_STRINGS; - } - } - if (enabled == null) { - return EmptyArrays.EMPTY_STRINGS; - } else { - Set enabledSet = new LinkedHashSet(enabled.length + extraCiphers.length); - synchronized (this) { - for (int i = 0; i < enabled.length; i++) { - String mapped = toJavaCipherSuite(enabled[i]); - final String cipher = mapped == null ? enabled[i] : mapped; - if (!OpenSsl.isTlsv13Supported() && SslUtils.isTLSv13Cipher(cipher)) { - continue; - } - enabledSet.add(cipher); - } - Collections.addAll(enabledSet, extraCiphers); - } - return enabledSet.toArray(new String[0]); - } - } - - @Override - public final void setEnabledCipherSuites(String[] cipherSuites) { - checkNotNull(cipherSuites, "cipherSuites"); - - final StringBuilder buf = new StringBuilder(); - final StringBuilder bufTLSv13 = new StringBuilder(); - - CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13, OpenSsl.isBoringSSL()); - final String cipherSuiteSpec = buf.toString(); - final String cipherSuiteSpecTLSv13 = bufTLSv13.toString(); - - if (!OpenSsl.isTlsv13Supported() && !cipherSuiteSpecTLSv13.isEmpty()) { - throw new IllegalArgumentException("TLSv1.3 is not supported by this java version."); - } - synchronized (this) { - if (!isDestroyed()) { - // TODO: Should we also adjust the protocols based on if there are any ciphers left that can be used - // for TLSv1.3 or for previor SSL/TLS versions ? - try { - // Set non TLSv1.3 ciphers. - SSL.setCipherSuites(ssl, cipherSuiteSpec, false); - - if (OpenSsl.isTlsv13Supported()) { - // Set TLSv1.3 ciphers. - SSL.setCipherSuites(ssl, cipherSuiteSpecTLSv13, true); - } - - } catch (Exception e) { - throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e); - } - } else { - throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec); - } - } - } - - @Override - public final String[] getSupportedProtocols() { - return OpenSsl.SUPPORTED_PROTOCOLS_SET.toArray(new String[0]); - } - - @Override - public final String[] getEnabledProtocols() { - List enabled = new ArrayList(6); - // Seems like there is no way to explicit disable SSLv2Hello in openssl so it is always enabled - enabled.add(PROTOCOL_SSL_V2_HELLO); - - int opts; - synchronized (this) { - if (!isDestroyed()) { - opts = SSL.getOptions(ssl); - } else { - return enabled.toArray(new String[0]); - } - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1, PROTOCOL_TLS_V1)) { - enabled.add(PROTOCOL_TLS_V1); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_1, PROTOCOL_TLS_V1_1)) { - enabled.add(PROTOCOL_TLS_V1_1); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_2, PROTOCOL_TLS_V1_2)) { - enabled.add(PROTOCOL_TLS_V1_2); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, PROTOCOL_TLS_V1_3)) { - enabled.add(PROTOCOL_TLS_V1_3); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv2, PROTOCOL_SSL_V2)) { - enabled.add(PROTOCOL_SSL_V2); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv3, PROTOCOL_SSL_V3)) { - enabled.add(PROTOCOL_SSL_V3); - } - return enabled.toArray(new String[0]); - } - - private static boolean isProtocolEnabled(int opts, int disableMask, String protocolString) { - // We also need to check if the actual protocolString is supported as depending on the openssl API - // implementations it may use a disableMask of 0 (BoringSSL is doing this for example). - return (opts & disableMask) == 0 && OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocolString); - } - - /** - * {@inheritDoc} - * TLS doesn't support a way to advertise non-contiguous versions from the client's perspective, and the client - * just advertises the max supported version. The TLS protocol also doesn't support all different combinations of - * discrete protocols, and instead assumes contiguous ranges. OpenSSL has some unexpected behavior - * (e.g. handshake failures) if non-contiguous protocols are used even where there is a compatible set of protocols - * and ciphers. For these reasons this method will determine the minimum protocol and the maximum protocol and - * enabled a contiguous range from [min protocol, max protocol] in OpenSSL. - */ - @Override - public final void setEnabledProtocols(String[] protocols) { - if (protocols == null) { - // This is correct from the API docs - throw new IllegalArgumentException(); - } - int minProtocolIndex = OPENSSL_OP_NO_PROTOCOLS.length; - int maxProtocolIndex = 0; - for (String p: protocols) { - if (!OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(p)) { - throw new IllegalArgumentException("Protocol " + p + " is not supported."); - } - if (p.equals(PROTOCOL_SSL_V2)) { - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2; - } - } else if (p.equals(PROTOCOL_SSL_V3)) { - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3; - } - } else if (p.equals(PROTOCOL_TLS_V1)) { - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1; - } - } else if (p.equals(PROTOCOL_TLS_V1_1)) { - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1; - } - } else if (p.equals(PROTOCOL_TLS_V1_2)) { - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2; - } - } else if (p.equals(PROTOCOL_TLS_V1_3)) { - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_3; - } - } - } - synchronized (this) { - if (!isDestroyed()) { - // Clear out options which disable protocols - SSL.clearOptions(ssl, SSL.SSL_OP_NO_SSLv2 | SSL.SSL_OP_NO_SSLv3 | SSL.SSL_OP_NO_TLSv1 | - SSL.SSL_OP_NO_TLSv1_1 | SSL.SSL_OP_NO_TLSv1_2 | SSL.SSL_OP_NO_TLSv1_3); - - int opts = 0; - for (int i = 0; i < minProtocolIndex; ++i) { - opts |= OPENSSL_OP_NO_PROTOCOLS[i]; - } - assert maxProtocolIndex != MAX_VALUE; - for (int i = maxProtocolIndex + 1; i < OPENSSL_OP_NO_PROTOCOLS.length; ++i) { - opts |= OPENSSL_OP_NO_PROTOCOLS[i]; - } - - // Disable protocols we do not want - SSL.setOptions(ssl, opts); - } else { - throw new IllegalStateException("failed to enable protocols: " + Arrays.asList(protocols)); - } - } - } - - @Override - public final SSLSession getSession() { - return session; - } - - @Override - public final synchronized void beginHandshake() throws SSLException { - switch (handshakeState) { - case STARTED_IMPLICITLY: - checkEngineClosed(); - - // A user did not start handshake by calling this method by him/herself, - // but handshake has been started already by wrap() or unwrap() implicitly. - // Because it's the user's first time to call this method, it is unfair to - // raise an exception. From the user's standpoint, he or she never asked - // for renegotiation. - - handshakeState = HandshakeState.STARTED_EXPLICITLY; // Next time this method is invoked by the user, - calculateMaxWrapOverhead(); - // we should raise an exception. - break; - case STARTED_EXPLICITLY: - // Nothing to do as the handshake is not done yet. - break; - case FINISHED: - throw new SSLException("renegotiation unsupported"); - case NOT_STARTED: - handshakeState = HandshakeState.STARTED_EXPLICITLY; - if (handshake() == NEED_TASK) { - // Set needTask to true so getHandshakeStatus() will return the correct value. - needTask = true; - } - calculateMaxWrapOverhead(); - break; - default: - throw new Error(); - } - } - - private void checkEngineClosed() throws SSLException { - if (isDestroyed()) { - throw new SSLException("engine closed"); - } - } - - private static SSLEngineResult.HandshakeStatus pendingStatus(int pendingStatus) { - // Depending on if there is something left in the BIO we need to WRAP or UNWRAP - return pendingStatus > 0 ? NEED_WRAP : NEED_UNWRAP; - } - - private static boolean isEmpty(Object[] arr) { - return arr == null || arr.length == 0; - } - - private static boolean isEmpty(byte[] cert) { - return cert == null || cert.length == 0; - } - - private SSLEngineResult.HandshakeStatus handshakeException() throws SSLException { - if (SSL.bioLengthNonApplication(networkBIO) > 0) { - // There is something pending, we need to consume it first via a WRAP so we don't loose anything. - return NEED_WRAP; - } - - Throwable exception = pendingException; - assert exception != null; - pendingException = null; - shutdown(); - if (exception instanceof SSLHandshakeException) { - throw (SSLHandshakeException) exception; - } - SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); - e.initCause(exception); - throw e; - } - - /** - * Should be called if the handshake will be failed due a callback that throws an exception. - * This cause will then be used to give more details as part of the {@link SSLHandshakeException}. - */ - final void initHandshakeException(Throwable cause) { - if (pendingException == null) { - pendingException = cause; - } else { - ThrowableUtil.addSuppressed(pendingException, cause); - } - } - - private SSLEngineResult.HandshakeStatus handshake() throws SSLException { - if (needTask) { - return NEED_TASK; - } - if (handshakeState == HandshakeState.FINISHED) { - return FINISHED; - } - - checkEngineClosed(); - - if (pendingException != null) { - // Let's call SSL.doHandshake(...) again in case there is some async operation pending that would fill the - // outbound buffer. - if (SSL.doHandshake(ssl) <= 0) { - // Clear any error that was put on the stack by the handshake - SSL.clearError(); - } - return handshakeException(); - } - - // Adding the OpenSslEngine to the OpenSslEngineMap so it can be used in the AbstractCertificateVerifier. - engineMap.add(this); - if (lastAccessed == -1) { - lastAccessed = System.currentTimeMillis(); - } - - int code = SSL.doHandshake(ssl); - if (code <= 0) { - int sslError = SSL.getError(ssl, code); - if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) { - return pendingStatus(SSL.bioLengthNonApplication(networkBIO)); - } - - if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP || - sslError == SSL.SSL_ERROR_WANT_CERTIFICATE_VERIFY || - sslError == SSL.SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - return NEED_TASK; - } - - // Check if we have a pending exception that was created during the handshake and if so throw it after - // shutdown the connection. - if (pendingException != null) { - return handshakeException(); - } - - // Everything else is considered as error - throw shutdownWithError("SSL_do_handshake", sslError); - } - // We have produced more data as part of the handshake if this is the case the user should call wrap(...) - if (SSL.bioLengthNonApplication(networkBIO) > 0) { - return NEED_WRAP; - } - // if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished. - session.handshakeFinished(); - return FINISHED; - } - - private SSLEngineResult.HandshakeStatus mayFinishHandshake(SSLEngineResult.HandshakeStatus status) - throws SSLException { - if (status == NOT_HANDSHAKING && handshakeState != HandshakeState.FINISHED) { - // If the status was NOT_HANDSHAKING and we not finished the handshake we need to call - // SSL_do_handshake() again - return handshake(); - } - return status; - } - - @Override - public final synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() { - // Check if we are in the initial handshake phase or shutdown phase - if (needPendingStatus()) { - if (needTask) { - // There is a task outstanding - return NEED_TASK; - } - return pendingStatus(SSL.bioLengthNonApplication(networkBIO)); - } - return NOT_HANDSHAKING; - } - - private SSLEngineResult.HandshakeStatus getHandshakeStatus(int pending) { - // Check if we are in the initial handshake phase or shutdown phase - if (needPendingStatus()) { - if (needTask) { - // There is a task outstanding - return NEED_TASK; - } - return pendingStatus(pending); - } - return NOT_HANDSHAKING; - } - - private boolean needPendingStatus() { - return handshakeState != HandshakeState.NOT_STARTED && !isDestroyed() - && (handshakeState != HandshakeState.FINISHED || isInboundDone() || isOutboundDone()); - } - - /** - * Converts the specified OpenSSL cipher suite to the Java cipher suite. - */ - private String toJavaCipherSuite(String openSslCipherSuite) { - if (openSslCipherSuite == null) { - return null; - } - - String version = SSL.getVersion(ssl); - String prefix = toJavaCipherSuitePrefix(version); - return CipherSuiteConverter.toJava(openSslCipherSuite, prefix); - } - - /** - * Converts the protocol version string returned by {@link SSL#getVersion(long)} to protocol family string. - */ - private static String toJavaCipherSuitePrefix(String protocolVersion) { - final char c; - if (protocolVersion == null || protocolVersion.isEmpty()) { - c = 0; - } else { - c = protocolVersion.charAt(0); - } - - switch (c) { - case 'T': - return "TLS"; - case 'S': - return "SSL"; - default: - return "UNKNOWN"; - } - } - - @Override - public final void setUseClientMode(boolean clientMode) { - if (clientMode != this.clientMode) { - throw new UnsupportedOperationException(); - } - } - - @Override - public final boolean getUseClientMode() { - return clientMode; - } - - @Override - public final void setNeedClientAuth(boolean b) { - setClientAuth(b ? ClientAuth.REQUIRE : ClientAuth.NONE); - } - - @Override - public final boolean getNeedClientAuth() { - return clientAuth == ClientAuth.REQUIRE; - } - - @Override - public final void setWantClientAuth(boolean b) { - setClientAuth(b ? ClientAuth.OPTIONAL : ClientAuth.NONE); - } - - @Override - public final boolean getWantClientAuth() { - return clientAuth == ClientAuth.OPTIONAL; - } - - /** - * See SSL_set_verify and - * {@link SSL#setVerify(long, int, int)}. - */ - @UnstableApi - public final synchronized void setVerify(int verifyMode, int depth) { - if (!isDestroyed()) { - SSL.setVerify(ssl, verifyMode, depth); - } - } - - private void setClientAuth(ClientAuth mode) { - if (clientMode) { - return; - } - synchronized (this) { - if (clientAuth == mode) { - // No need to issue any JNI calls if the mode is the same - return; - } - if (!isDestroyed()) { - switch (mode) { - case NONE: - SSL.setVerify(ssl, SSL.SSL_CVERIFY_NONE, ReferenceCountedOpenSslContext.VERIFY_DEPTH); - break; - case REQUIRE: - SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRED, ReferenceCountedOpenSslContext.VERIFY_DEPTH); - break; - case OPTIONAL: - SSL.setVerify(ssl, SSL.SSL_CVERIFY_OPTIONAL, ReferenceCountedOpenSslContext.VERIFY_DEPTH); - break; - default: - throw new Error(mode.toString()); - } - } - clientAuth = mode; - } - } - - @Override - public final void setEnableSessionCreation(boolean b) { - if (b) { - throw new UnsupportedOperationException(); - } - } - - @Override - public final boolean getEnableSessionCreation() { - return false; - } - - @SuppressJava6Requirement(reason = "Usage guarded by java version check") - @Override - public final synchronized SSLParameters getSSLParameters() { - SSLParameters sslParameters = super.getSSLParameters(); - - int version = PlatformDependent.javaVersion(); - if (version >= 7) { - sslParameters.setEndpointIdentificationAlgorithm(endPointIdentificationAlgorithm); - Java7SslParametersUtils.setAlgorithmConstraints(sslParameters, algorithmConstraints); - if (version >= 8) { - if (sniHostNames != null) { - Java8SslUtils.setSniHostNames(sslParameters, sniHostNames); - } - if (!isDestroyed()) { - Java8SslUtils.setUseCipherSuitesOrder( - sslParameters, (SSL.getOptions(ssl) & SSL.SSL_OP_CIPHER_SERVER_PREFERENCE) != 0); - } - - Java8SslUtils.setSNIMatchers(sslParameters, matchers); - } - } - return sslParameters; - } - - @SuppressJava6Requirement(reason = "Usage guarded by java version check") - @Override - public final synchronized void setSSLParameters(SSLParameters sslParameters) { - int version = PlatformDependent.javaVersion(); - if (version >= 7) { - if (sslParameters.getAlgorithmConstraints() != null) { - throw new IllegalArgumentException("AlgorithmConstraints are not supported."); - } - - boolean isDestroyed = isDestroyed(); - if (version >= 8) { - if (!isDestroyed) { - if (clientMode) { - final List sniHostNames = Java8SslUtils.getSniHostNames(sslParameters); - for (String name: sniHostNames) { - SSL.setTlsExtHostName(ssl, name); - } - this.sniHostNames = sniHostNames; - } - if (Java8SslUtils.getUseCipherSuitesOrder(sslParameters)) { - SSL.setOptions(ssl, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - } else { - SSL.clearOptions(ssl, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - } - } - matchers = sslParameters.getSNIMatchers(); - } - - final String endPointIdentificationAlgorithm = sslParameters.getEndpointIdentificationAlgorithm(); - if (!isDestroyed) { - // If the user asks for hostname verification we must ensure we verify the peer. - // If the user disables hostname verification we leave it up to the user to change the mode manually. - if (clientMode && isEndPointVerificationEnabled(endPointIdentificationAlgorithm)) { - SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRED, -1); - } - } - this.endPointIdentificationAlgorithm = endPointIdentificationAlgorithm; - algorithmConstraints = sslParameters.getAlgorithmConstraints(); - } - super.setSSLParameters(sslParameters); - } - - private static boolean isEndPointVerificationEnabled(String endPointIdentificationAlgorithm) { - return endPointIdentificationAlgorithm != null && !endPointIdentificationAlgorithm.isEmpty(); - } - - private boolean isDestroyed() { - return destroyed; - } - - final boolean checkSniHostnameMatch(byte[] hostname) { - return Java8SslUtils.checkSniHostnameMatch(matchers, hostname); - } - - @Override - public String getNegotiatedApplicationProtocol() { - return applicationProtocol; - } - - private static long bufferAddress(ByteBuffer b) { - assert b.isDirect(); - if (PlatformDependent.hasUnsafe()) { - return PlatformDependent.directBufferAddress(b); - } - return Buffer.address(b); - } - - private final class DefaultOpenSslSession implements OpenSslSession { - private final OpenSslSessionContext sessionContext; - - // These are guarded by synchronized(OpenSslEngine.this) as handshakeFinished() may be triggered by any - // thread. - private X509Certificate[] x509PeerCerts; - private Certificate[] peerCerts; - - private String protocol; - private String cipher; - private byte[] id; - private long creationTime; - private volatile int applicationBufferSize = MAX_PLAINTEXT_LENGTH; - - // lazy init for memory reasons - private Map values; - - DefaultOpenSslSession(OpenSslSessionContext sessionContext) { - this.sessionContext = sessionContext; - } - - private SSLSessionBindingEvent newSSLSessionBindingEvent(String name) { - return new SSLSessionBindingEvent(session, name); - } - - @Override - public byte[] getId() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (id == null) { - return EmptyArrays.EMPTY_BYTES; - } - return id.clone(); - } - } - - @Override - public SSLSessionContext getSessionContext() { - return sessionContext; - } - - @Override - public long getCreationTime() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (creationTime == 0 && !isDestroyed()) { - creationTime = SSL.getTime(ssl) * 1000L; - } - } - return creationTime; - } - - @Override - public long getLastAccessedTime() { - long lastAccessed = ReferenceCountedOpenSslEngine.this.lastAccessed; - // if lastAccessed is -1 we will just return the creation time as the handshake was not started yet. - return lastAccessed == -1 ? getCreationTime() : lastAccessed; - } - - @Override - public void invalidate() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - SSL.setTimeout(ssl, 0); - } - } - } - - @Override - public boolean isValid() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - return System.currentTimeMillis() - (SSL.getTimeout(ssl) * 1000L) < (SSL.getTime(ssl) * 1000L); - } - } - return false; - } - - @Override - public void putValue(String name, Object value) { - ObjectUtil.checkNotNull(name, "name"); - ObjectUtil.checkNotNull(value, "value"); - - final Object old; - synchronized (this) { - Map values = this.values; - if (values == null) { - // Use size of 2 to keep the memory overhead small - values = this.values = new HashMap(2); - } - old = values.put(name, value); - } - - if (value instanceof SSLSessionBindingListener) { - // Use newSSLSessionBindingEvent so we alway use the wrapper if needed. - ((SSLSessionBindingListener) value).valueBound(newSSLSessionBindingEvent(name)); - } - notifyUnbound(old, name); - } - - @Override - public Object getValue(String name) { - ObjectUtil.checkNotNull(name, "name"); - synchronized (this) { - if (values == null) { - return null; - } - return values.get(name); - } - } - - @Override - public void removeValue(String name) { - ObjectUtil.checkNotNull(name, "name"); - - final Object old; - synchronized (this) { - Map values = this.values; - if (values == null) { - return; - } - old = values.remove(name); - } - - notifyUnbound(old, name); - } - - @Override - public String[] getValueNames() { - synchronized (this) { - Map values = this.values; - if (values == null || values.isEmpty()) { - return EmptyArrays.EMPTY_STRINGS; - } - return values.keySet().toArray(new String[0]); - } - } - - private void notifyUnbound(Object value, String name) { - if (value instanceof SSLSessionBindingListener) { - // Use newSSLSessionBindingEvent so we alway use the wrapper if needed. - ((SSLSessionBindingListener) value).valueUnbound(newSSLSessionBindingEvent(name)); - } - } - - /** - * Finish the handshake and so init everything in the {@link OpenSslSession} that should be accessible by - * the user. - */ - @Override - public void handshakeFinished() throws SSLException { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - id = SSL.getSessionId(ssl); - cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl)); - protocol = SSL.getVersion(ssl); - - initPeerCerts(); - selectApplicationProtocol(); - calculateMaxWrapOverhead(); - - handshakeState = HandshakeState.FINISHED; - } else { - throw new SSLException("Already closed"); - } - } - } - - /** - * Init peer certificates that can be obtained via {@link #getPeerCertificateChain()} - * and {@link #getPeerCertificates()}. - */ - private void initPeerCerts() { - // Return the full chain from the JNI layer. - byte[][] chain = SSL.getPeerCertChain(ssl); - if (clientMode) { - if (isEmpty(chain)) { - peerCerts = EmptyArrays.EMPTY_CERTIFICATES; - x509PeerCerts = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; - } else { - peerCerts = new Certificate[chain.length]; - x509PeerCerts = new X509Certificate[chain.length]; - initCerts(chain, 0); - } - } else { - // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer - // certificate. We use SSL_get_peer_certificate to get it in this case and add it to our - // array later. - // - // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html - byte[] clientCert = SSL.getPeerCertificate(ssl); - if (isEmpty(clientCert)) { - peerCerts = EmptyArrays.EMPTY_CERTIFICATES; - x509PeerCerts = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; - } else { - if (isEmpty(chain)) { - peerCerts = new Certificate[] {new OpenSslX509Certificate(clientCert)}; - x509PeerCerts = new X509Certificate[] {new OpenSslJavaxX509Certificate(clientCert)}; - } else { - peerCerts = new Certificate[chain.length + 1]; - x509PeerCerts = new X509Certificate[chain.length + 1]; - peerCerts[0] = new OpenSslX509Certificate(clientCert); - x509PeerCerts[0] = new OpenSslJavaxX509Certificate(clientCert); - initCerts(chain, 1); - } - } - } - } - - private void initCerts(byte[][] chain, int startPos) { - for (int i = 0; i < chain.length; i++) { - int certPos = startPos + i; - peerCerts[certPos] = new OpenSslX509Certificate(chain[i]); - x509PeerCerts[certPos] = new OpenSslJavaxX509Certificate(chain[i]); - } - } - - /** - * Select the application protocol used. - */ - private void selectApplicationProtocol() throws SSLException { - ApplicationProtocolConfig.SelectedListenerFailureBehavior behavior = apn.selectedListenerFailureBehavior(); - List protocols = apn.protocols(); - String applicationProtocol; - switch (apn.protocol()) { - case NONE: - break; - // We always need to check for applicationProtocol == null as the remote peer may not support - // the TLS extension or may have returned an empty selection. - case ALPN: - applicationProtocol = SSL.getAlpnSelected(ssl); - if (applicationProtocol != null) { - ReferenceCountedOpenSslEngine.this.applicationProtocol = selectApplicationProtocol( - protocols, behavior, applicationProtocol); - } - break; - case NPN: - applicationProtocol = SSL.getNextProtoNegotiated(ssl); - if (applicationProtocol != null) { - ReferenceCountedOpenSslEngine.this.applicationProtocol = selectApplicationProtocol( - protocols, behavior, applicationProtocol); - } - break; - case NPN_AND_ALPN: - applicationProtocol = SSL.getAlpnSelected(ssl); - if (applicationProtocol == null) { - applicationProtocol = SSL.getNextProtoNegotiated(ssl); - } - if (applicationProtocol != null) { - ReferenceCountedOpenSslEngine.this.applicationProtocol = selectApplicationProtocol( - protocols, behavior, applicationProtocol); - } - break; - default: - throw new Error(); - } - } - - private String selectApplicationProtocol(List protocols, - ApplicationProtocolConfig.SelectedListenerFailureBehavior behavior, - String applicationProtocol) throws SSLException { - if (behavior == ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT) { - return applicationProtocol; - } else { - int size = protocols.size(); - assert size > 0; - if (protocols.contains(applicationProtocol)) { - return applicationProtocol; - } else { - if (behavior == ApplicationProtocolConfig.SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL) { - return protocols.get(size - 1); - } else { - throw new SSLException("unknown protocol " + applicationProtocol); - } - } - } - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (isEmpty(peerCerts)) { - throw new SSLPeerUnverifiedException("peer not verified"); - } - return peerCerts.clone(); - } - } - - @Override - public Certificate[] getLocalCertificates() { - Certificate[] localCerts = ReferenceCountedOpenSslEngine.this.localCertificateChain; - if (localCerts == null) { - return null; - } - return localCerts.clone(); - } - - @Override - public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (isEmpty(x509PeerCerts)) { - throw new SSLPeerUnverifiedException("peer not verified"); - } - return x509PeerCerts.clone(); - } - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - Certificate[] peer = getPeerCertificates(); - // No need for null or length > 0 is needed as this is done in getPeerCertificates() - // already. - return ((java.security.cert.X509Certificate) peer[0]).getSubjectX500Principal(); - } - - @Override - public Principal getLocalPrincipal() { - Certificate[] local = ReferenceCountedOpenSslEngine.this.localCertificateChain; - if (local == null || local.length == 0) { - return null; - } - return ((java.security.cert.X509Certificate) local[0]).getIssuerX500Principal(); - } - - @Override - public String getCipherSuite() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (cipher == null) { - return SslUtils.INVALID_CIPHER; - } - return cipher; - } - } - - @Override - public String getProtocol() { - String protocol = this.protocol; - if (protocol == null) { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - protocol = SSL.getVersion(ssl); - } else { - protocol = StringUtil.EMPTY_STRING; - } - } - } - return protocol; - } - - @Override - public String getPeerHost() { - return ReferenceCountedOpenSslEngine.this.getPeerHost(); - } - - @Override - public int getPeerPort() { - return ReferenceCountedOpenSslEngine.this.getPeerPort(); - } - - @Override - public int getPacketBufferSize() { - return maxEncryptedPacketLength(); - } - - @Override - public int getApplicationBufferSize() { - return applicationBufferSize; - } - - @Override - public void tryExpandApplicationBufferSize(int packetLengthDataOnly) { - if (packetLengthDataOnly > MAX_PLAINTEXT_LENGTH && applicationBufferSize != MAX_RECORD_SIZE) { - applicationBufferSize = MAX_RECORD_SIZE; - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java deleted file mode 100644 index bac027a3b4..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2016 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.internal.tcnative.CertificateCallback; -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.internal.tcnative.SniHostNameMatcher; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SuppressJava6Requirement; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; - -import static io.netty.util.internal.ObjectUtil.checkNotNull; - -/** - * A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must not be released before any {@link ReferenceCountedOpenSslEngine} - * which depends upon the instance of this class is released. Otherwise if any method of - * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash. - */ -public final class ReferenceCountedOpenSslServerContext extends ReferenceCountedOpenSslContext { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ReferenceCountedOpenSslServerContext.class); - private static final byte[] ID = {'n', 'e', 't', 't', 'y'}; - private final OpenSslServerSessionContext sessionContext; - - private static final boolean ENABLE_SESSION_TICKET = - SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", false); - - ReferenceCountedOpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore) throws SSLException { - this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, - cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls, - enableOcsp, keyStore); - } - - ReferenceCountedOpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore) throws SSLException { - super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, true); - // Create a new SSL_CTX and configure it. - boolean success = false; - try { - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore); - if (ENABLE_SESSION_TICKET) { - sessionContext.setTicketKeys(); - } - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslServerSessionContext sessionContext() { - return sessionContext; - } - - static OpenSslServerSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, - OpenSslEngineMap engineMap, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, - String keyPassword, KeyManagerFactory keyManagerFactory, - String keyStore) - throws SSLException { - OpenSslKeyMaterialProvider keyMaterialProvider = null; - try { - try { - SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); - if (!OpenSsl.useKeyManagerFactory()) { - if (keyManagerFactory != null) { - throw new IllegalArgumentException( - "KeyManagerFactory not supported"); - } - checkNotNull(keyCertChain, "keyCertChain"); - - setKeyMaterial(ctx, keyCertChain, key, keyPassword); - } else { - // javadocs state that keyManagerFactory has precedent over keyCertChain, and we must have a - // keyManagerFactory for the server so build one if it is not specified. - if (keyManagerFactory == null) { - char[] keyPasswordChars = keyStorePassword(keyPassword); - KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore); - if (ks.aliases().hasMoreElements()) { - keyManagerFactory = new OpenSslX509KeyManagerFactory(); - } else { - keyManagerFactory = new OpenSslCachingX509KeyManagerFactory( - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())); - } - keyManagerFactory.init(ks, keyPasswordChars); - } - keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); - - SSLContext.setCertificateCallback(ctx, new OpenSslServerCertificateCallback( - engineMap, new OpenSslKeyMaterialManager(keyMaterialProvider))); - } - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore); - } else if (trustManagerFactory == null) { - // Mimic the way SSLContext.getInstance(KeyManager[], null, null) works - trustManagerFactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore) null); - } - - final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers()); - - // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as - // otherwise the context can never be collected. This is because the JNI code holds - // a global reference to the callbacks. - // - // See https://github.com/netty/netty/issues/5372 - - setVerifyCallback(ctx, engineMap, manager); - - X509Certificate[] issuers = manager.getAcceptedIssuers(); - if (issuers != null && issuers.length > 0) { - long bio = 0; - try { - bio = toBIO(ByteBufAllocator.DEFAULT, issuers); - if (!SSLContext.setCACertificateBio(ctx, bio)) { - throw new SSLException("unable to setup accepted issuers for trustmanager " + manager); - } - } finally { - freeBio(bio); - } - } - - if (PlatformDependent.javaVersion() >= 8) { - // Only do on Java8+ as SNIMatcher is not supported in earlier releases. - // IMPORTANT: The callbacks set for hostname matching must be static to prevent memory leak as - // otherwise the context can never be collected. This is because the JNI code holds - // a global reference to the matcher. - SSLContext.setSniHostnameMatcher(ctx, new OpenSslSniHostnameMatcher(engineMap)); - } - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException("unable to setup trustmanager", e); - } - - OpenSslServerSessionContext sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider); - sessionContext.setSessionIdContext(ID); - - keyMaterialProvider = null; - - return sessionContext; - } finally { - if (keyMaterialProvider != null) { - keyMaterialProvider.destroy(); - } - } - } - - @SuppressJava6Requirement(reason = "Guarded by java version check") - private static void setVerifyCallback(long ctx, OpenSslEngineMap engineMap, X509TrustManager manager) { - // Use this to prevent an error when running on java < 7 - if (useExtendedTrustManager(manager)) { - SSLContext.setCertVerifyCallback(ctx, new ExtendedTrustManagerVerifyCallback( - engineMap, (X509ExtendedTrustManager) manager)); - } else { - SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager)); - } - } - - private static final class OpenSslServerCertificateCallback implements CertificateCallback { - private final OpenSslEngineMap engineMap; - private final OpenSslKeyMaterialManager keyManagerHolder; - - OpenSslServerCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) { - this.engineMap = engineMap; - this.keyManagerHolder = keyManagerHolder; - } - - @Override - public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception { - final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - // Maybe null if destroyed in the meantime. - return; - } - try { - // For now we just ignore the asn1DerEncodedPrincipals as this is kind of inline with what the - // OpenJDK SSLEngineImpl does. - keyManagerHolder.setKeyMaterialServerSide(engine); - } catch (Throwable cause) { - logger.debug("Failed to set the server-side key material", cause); - engine.initHandshakeException(cause); - } - } - } - - private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509TrustManager manager; - - TrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509TrustManager manager) { - super(engineMap); - this.manager = manager; - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkClientTrusted(peerCerts, auth); - } - } - - @SuppressJava6Requirement(reason = "Usage guarded by java version check") - private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509ExtendedTrustManager manager; - - ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) { - super(engineMap); - this.manager = OpenSslTlsv13X509ExtendedTrustManager.wrap(manager); - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkClientTrusted(peerCerts, auth, engine); - } - } - - private static final class OpenSslSniHostnameMatcher implements SniHostNameMatcher { - private final OpenSslEngineMap engineMap; - - OpenSslSniHostnameMatcher(OpenSslEngineMap engineMap) { - this.engineMap = engineMap; - } - - @Override - public boolean match(long ssl, String hostname) { - ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine != null) { - // TODO: In the next release of tcnative we should pass the byte[] directly in and not use a String. - return engine.checkSniHostnameMatch(hostname.getBytes(CharsetUtil.UTF_8)); - } - logger.warn("No ReferenceCountedOpenSslEngine found for SSL pointer: {}", ssl); - return false; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index fef2702cfb..eb5110d145 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -120,11 +120,7 @@ public abstract class SslContext { } private static SslProvider defaultProvider() { - if (OpenSsl.isAvailable()) { - return SslProvider.OPENSSL; - } else { - return SslProvider.JDK; - } + return SslProvider.JDK; } /** @@ -466,18 +462,6 @@ public abstract class SslContext { trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls, keyStoreType); - case OPENSSL: - verifyNullSslContextProvider(provider, sslContextProvider); - return new OpenSslServerContext( - trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, protocols, startTls, enableOcsp, keyStoreType); - case OPENSSL_REFCNT: - verifyNullSslContextProvider(provider, sslContextProvider); - return new ReferenceCountedOpenSslServerContext( - trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, protocols, startTls, enableOcsp, keyStoreType); default: throw new Error(provider.toString()); } @@ -822,18 +806,6 @@ public abstract class SslContext { trustCert, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, keyStoreType); - case OPENSSL: - verifyNullSslContextProvider(provider, sslContextProvider); - return new OpenSslClientContext( - trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, - enableOcsp, keyStoreType); - case OPENSSL_REFCNT: - verifyNullSslContextProvider(provider, sslContextProvider); - return new ReferenceCountedOpenSslClientContext( - trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, - enableOcsp, keyStoreType); default: throw new Error(provider.toString()); } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 8e11bbf4cc..56be212b09 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -181,53 +181,6 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; private enum SslEngineType { - TCNATIVE(true, COMPOSITE_CUMULATOR) { - @Override - SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out) - throws SSLException { - int nioBufferCount = in.nioBufferCount(); - int writerIndex = out.writerIndex(); - final SSLEngineResult result; - if (nioBufferCount > 1) { - /* - * If {@link OpenSslEngine} is in use, - * we can use a special {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method - * that accepts multiple {@link ByteBuffer}s without additional memory copies. - */ - ReferenceCountedOpenSslEngine opensslEngine = (ReferenceCountedOpenSslEngine) handler.engine; - try { - handler.singleBuffer[0] = toByteBuffer(out, writerIndex, - out.writableBytes()); - result = opensslEngine.unwrap(in.nioBuffers(readerIndex, len), handler.singleBuffer); - } finally { - handler.singleBuffer[0] = null; - } - } else { - result = handler.engine.unwrap(toByteBuffer(in, readerIndex, len), - toByteBuffer(out, writerIndex, out.writableBytes())); - } - out.writerIndex(writerIndex + result.bytesProduced()); - return result; - } - - @Override - ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator, - int pendingBytes, int numComponents) { - return allocator.directBuffer(((ReferenceCountedOpenSslEngine) handler.engine) - .calculateMaxLengthForWrap(pendingBytes, numComponents)); - } - - @Override - int calculatePendingData(SslHandler handler, int guess) { - int sslPending = ((ReferenceCountedOpenSslEngine) handler.engine).sslPending(); - return sslPending > 0 ? sslPending : guess; - } - - @Override - boolean jdkCompatibilityMode(SSLEngine engine) { - return ((ReferenceCountedOpenSslEngine) engine).jdkCompatibilityMode; - } - }, JDK(false, MERGE_CUMULATOR) { @Override SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out) @@ -280,7 +233,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH }; static SslEngineType forEngine(SSLEngine engine) { - return engine instanceof ReferenceCountedOpenSslEngine ? TCNATIVE : JDK; + return JDK; } SslEngineType(boolean wantsDirectBuffer, Cumulator cumulator) { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java index b1c710c841..44ef0456ed 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java @@ -138,9 +138,6 @@ public abstract class SslMasterKeyHandler extends ChannelInboundHandlerAdapter { "via reflection.", e); } accept(secretKey, sslSession); - } else if (OpenSsl.isAvailable() && engine instanceof ReferenceCountedOpenSslEngine) { - SecretKeySpec secretKey = ((ReferenceCountedOpenSslEngine) engine).masterKey(); - accept(secretKey, sslSession); } } } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslProvider.java b/handler/src/main/java/io/netty/handler/ssl/SslProvider.java index e72cfed8d9..5263314e7c 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslProvider.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslProvider.java @@ -26,16 +26,7 @@ public enum SslProvider { /** * JDK's default implementation. */ - JDK, - /** - * OpenSSL-based implementation. - */ - OPENSSL, - /** - * OpenSSL-based implementation which does not have finalizers and instead implements {@link ReferenceCounted}. - */ - @UnstableApi - OPENSSL_REFCNT; + JDK; /** * Returns {@code true} if the specified {@link SslProvider} supports @@ -45,9 +36,6 @@ public enum SslProvider { switch (provider) { case JDK: return JdkAlpnApplicationProtocolNegotiator.isAlpnSupported(); - case OPENSSL: - case OPENSSL_REFCNT: - return OpenSsl.isAlpnSupported(); default: throw new Error("Unknown SslProvider: " + provider); } diff --git a/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java b/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java deleted file mode 100644 index c45c50e1e3..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl.ocsp; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.handler.ssl.ReferenceCountedOpenSslContext; -import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; -import io.netty.handler.ssl.SslHandshakeCompletionEvent; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.UnstableApi; - -import javax.net.ssl.SSLHandshakeException; - -/** - * A handler for SSL clients to handle and act upon stapled OCSP responses. - * - * @see ReferenceCountedOpenSslContext#enableOcsp() - * @see ReferenceCountedOpenSslEngine#getOcspResponse() - */ -@UnstableApi -public abstract class OcspClientHandler extends ChannelInboundHandlerAdapter { - - private final ReferenceCountedOpenSslEngine engine; - - protected OcspClientHandler(ReferenceCountedOpenSslEngine engine) { - this.engine = ObjectUtil.checkNotNull(engine, "engine"); - } - - /** - * @see ReferenceCountedOpenSslEngine#getOcspResponse() - */ - protected abstract boolean verify(ChannelHandlerContext ctx, ReferenceCountedOpenSslEngine engine) throws Exception; - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - ctx.pipeline().remove(this); - - SslHandshakeCompletionEvent event = (SslHandshakeCompletionEvent) evt; - if (event.isSuccess() && !verify(ctx, engine)) { - throw new SSLHandshakeException("Bad OCSP response"); - } - } - - ctx.fireUserEventTriggered(evt); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java b/handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java deleted file mode 100644 index 2883ff48cf..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 The Netty Project - * - * The Netty Project 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. - */ - -/** - * OCSP stapling, - * formally known as the TLS Certificate Status Request extension, is an - * alternative approach to the Online Certificate Status Protocol (OCSP) - * for checking the revocation status of X.509 digital certificates. - */ -package io.netty.handler.ssl.ocsp; diff --git a/pom.xml b/pom.xml index bedd6d6903..75a2da328a 100644 --- a/pom.xml +++ b/pom.xml @@ -465,16 +465,6 @@ 3.0.0-alpha-5 - - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.version} - ${tcnative.classifier} - compile - true - -