You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8986 lines
380 KiB
8986 lines
380 KiB
From 39b320920d3473d8cbc94d4a35dad37fa236e278 Mon Sep 17 00:00:00 2001
|
|
From: Severin Gehwolf <sgehwolf@redhat.com>
|
|
Date: Thu, 20 Oct 2016 15:54:52 +0200
|
|
Subject: [PATCH 1/3] Remove OpenSSL parts depending on tcnative.
|
|
|
|
---
|
|
handler/pom.xml | 6 -
|
|
.../main/java/io/netty/handler/ssl/OpenSsl.java | 503 -----
|
|
.../handler/ssl/OpenSslCertificateException.java | 79 -
|
|
.../io/netty/handler/ssl/OpenSslClientContext.java | 211 --
|
|
.../java/io/netty/handler/ssl/OpenSslContext.java | 58 -
|
|
.../java/io/netty/handler/ssl/OpenSslEngine.java | 40 -
|
|
.../io/netty/handler/ssl/OpenSslEngineMap.java | 35 -
|
|
.../ssl/OpenSslExtendedKeyMaterialManager.java | 40 -
|
|
.../handler/ssl/OpenSslKeyMaterialManager.java | 179 --
|
|
.../io/netty/handler/ssl/OpenSslServerContext.java | 373 ----
|
|
.../handler/ssl/OpenSslServerSessionContext.java | 124 --
|
|
.../netty/handler/ssl/OpenSslSessionContext.java | 137 --
|
|
.../io/netty/handler/ssl/OpenSslSessionStats.java | 253 ---
|
|
.../netty/handler/ssl/OpenSslSessionTicketKey.java | 78 -
|
|
.../ssl/ReferenceCountedOpenSslClientContext.java | 298 ---
|
|
.../ssl/ReferenceCountedOpenSslContext.java | 867 ---------
|
|
.../handler/ssl/ReferenceCountedOpenSslEngine.java | 2037 --------------------
|
|
.../ssl/ReferenceCountedOpenSslServerContext.java | 239 ---
|
|
.../main/java/io/netty/handler/ssl/SslContext.java | 30 +-
|
|
.../main/java/io/netty/handler/ssl/SslHandler.java | 47 +-
|
|
.../netty/handler/ssl/ocsp/OcspClientHandler.java | 65 -
|
|
.../io/netty/handler/ssl/ocsp/package-info.java | 23 -
|
|
.../handler/ssl/JdkOpenSslEngineInteroptTest.java | 108 --
|
|
.../ssl/OpenSslCertificateExceptionTest.java | 49 -
|
|
.../handler/ssl/OpenSslClientContextTest.java | 38 -
|
|
.../io/netty/handler/ssl/OpenSslEngineTest.java | 661 -------
|
|
.../ssl/OpenSslJdkSslEngineInteroptTest.java | 114 --
|
|
.../ssl/OpenSslRenegotiateSmallBIOTest.java | 23 -
|
|
.../netty/handler/ssl/OpenSslRenegotiateTest.java | 36 -
|
|
.../handler/ssl/OpenSslServerContextTest.java | 39 -
|
|
.../io/netty/handler/ssl/OpenSslTestUtils.java | 27 -
|
|
.../java/io/netty/handler/ssl/PemEncodedTest.java | 95 -
|
|
.../ssl/ReferenceCountedOpenSslEngineTest.java | 57 -
|
|
.../java/io/netty/handler/ssl/SniClientTest.java | 161 --
|
|
.../java/io/netty/handler/ssl/SniHandlerTest.java | 496 -----
|
|
.../netty/handler/ssl/SslContextBuilderTest.java | 132 --
|
|
.../java/io/netty/handler/ssl/SslErrorTest.java | 255 ---
|
|
.../java/io/netty/handler/ssl/SslHandlerTest.java | 58 +-
|
|
.../java/io/netty/handler/ssl/ocsp/OcspTest.java | 501 -----
|
|
39 files changed, 10 insertions(+), 8562 deletions(-)
|
|
delete mode 100644 handler/src/main/java/io/netty/handler/ssl/OpenSsl.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/OpenSslExtendedKeyMaterialManager.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/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/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
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateSmallBIOTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/SniClientTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java
|
|
delete mode 100644 handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java
|
|
|
|
diff --git a/handler/pom.xml b/handler/pom.xml
|
|
index 7535c45..d0ed1bc 100644
|
|
--- a/handler/pom.xml
|
|
+++ b/handler/pom.xml
|
|
@@ -50,12 +50,6 @@
|
|
<version>${project.version}</version>
|
|
</dependency>
|
|
<dependency>
|
|
- <groupId>${project.groupId}</groupId>
|
|
- <artifactId>${tcnative.artifactId}</artifactId>
|
|
- <classifier>${tcnative.classifier}</classifier>
|
|
- <optional>true</optional>
|
|
- </dependency>
|
|
- <dependency>
|
|
<groupId>org.bouncycastle</groupId>
|
|
<artifactId>bcpkix-jdk15on</artifactId>
|
|
<optional>true</optional>
|
|
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 d2f091a..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java
|
|
+++ /dev/null
|
|
@@ -1,503 +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.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.ReferenceCountUtil;
|
|
-import io.netty.util.ReferenceCounted;
|
|
-import io.netty.util.internal.NativeLibraryLoader;
|
|
-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.Buffer;
|
|
-import io.netty.internal.tcnative.Library;
|
|
-import io.netty.internal.tcnative.SSL;
|
|
-import io.netty.internal.tcnative.SSLContext;
|
|
-
|
|
-import java.security.AccessController;
|
|
-import java.security.PrivilegedAction;
|
|
-import java.util.Collections;
|
|
-import java.util.LinkedHashSet;
|
|
-import java.util.Locale;
|
|
-import java.util.Set;
|
|
-
|
|
-/**
|
|
- * Tells if <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
|
|
- * are available.
|
|
- */
|
|
-public final class OpenSsl {
|
|
-
|
|
- private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class);
|
|
- private static final String LINUX = "linux";
|
|
- private static final String UNKNOWN = "unknown";
|
|
- private static final Throwable UNAVAILABILITY_CAUSE;
|
|
-
|
|
- static final Set<String> AVAILABLE_CIPHER_SUITES;
|
|
- private static final Set<String> AVAILABLE_OPENSSL_CIPHER_SUITES;
|
|
- private static final Set<String> AVAILABLE_JAVA_CIPHER_SUITES;
|
|
- private static final boolean SUPPORTS_KEYMANAGER_FACTORY;
|
|
- private static final boolean SUPPORTS_HOSTNAME_VALIDATION;
|
|
- private static final boolean USE_KEYMANAGER_FACTORY;
|
|
- private static final boolean SUPPORTS_OCSP;
|
|
-
|
|
- // Protocols
|
|
- static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
|
|
- static final String PROTOCOL_SSL_V2 = "SSLv2";
|
|
- static final String PROTOCOL_SSL_V3 = "SSLv3";
|
|
- static final String PROTOCOL_TLS_V1 = "TLSv1";
|
|
- static final String PROTOCOL_TLS_V1_1 = "TLSv1.1";
|
|
- static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
|
|
-
|
|
- static final Set<String> SUPPORTED_PROTOCOLS_SET;
|
|
-
|
|
- static {
|
|
- Throwable cause = null;
|
|
-
|
|
- // Test if netty-tcnative is in the classpath first.
|
|
- try {
|
|
- Class.forName("io.netty.internal.tcnative.SSL", 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 http://netty.io/wiki/forked-tomcat-native.html for more information.", t);
|
|
- }
|
|
-
|
|
- try {
|
|
- initializeTcNative();
|
|
-
|
|
- // 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 http://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 Set<String> availableOpenSslCipherSuites = new LinkedHashSet<String>(128);
|
|
- boolean supportsKeyManagerFactory = false;
|
|
- boolean useKeyManagerFactory = false;
|
|
- boolean supportsHostNameValidation = false;
|
|
- try {
|
|
- final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
|
- long certBio = 0;
|
|
- SelfSignedCertificate cert = null;
|
|
- try {
|
|
- SSLContext.setCipherSuite(sslCtx, "ALL");
|
|
- 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)) {
|
|
- continue;
|
|
- }
|
|
- availableOpenSslCipherSuites.add(c);
|
|
- }
|
|
- try {
|
|
- SSL.setHostNameValidation(ssl, 0, "netty.io");
|
|
- supportsHostNameValidation = true;
|
|
- } catch (Throwable ignore) {
|
|
- logger.debug("Hostname Verification not supported.");
|
|
- }
|
|
- try {
|
|
- cert = new SelfSignedCertificate();
|
|
- certBio = ReferenceCountedOpenSslContext.toBIO(cert.cert());
|
|
- SSL.setCertificateChainBio(ssl, certBio, false);
|
|
- supportsKeyManagerFactory = true;
|
|
- try {
|
|
- useKeyManagerFactory = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
|
- @Override
|
|
- public Boolean run() {
|
|
- return SystemPropertyUtil.getBoolean(
|
|
- "io.netty.handler.ssl.openssl.useKeyManagerFactory", true);
|
|
- }
|
|
- });
|
|
- } catch (Throwable ignore) {
|
|
- logger.debug("Failed to get useKeyManagerFactory system property.");
|
|
- }
|
|
- } catch (Throwable ignore) {
|
|
- logger.debug("KeyManagerFactory not supported.");
|
|
- }
|
|
- } finally {
|
|
- SSL.freeSSL(ssl);
|
|
- if (certBio != 0) {
|
|
- SSL.freeBIO(certBio);
|
|
- }
|
|
- if (cert != null) {
|
|
- cert.delete();
|
|
- }
|
|
- }
|
|
- } 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<String> availableJavaCipherSuites = new LinkedHashSet<String>(
|
|
- AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2);
|
|
- for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) {
|
|
- // Included converted but also openssl cipher name
|
|
- availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "TLS"));
|
|
- availableJavaCipherSuites.add(CipherSuiteConverter.toJava(cipher, "SSL"));
|
|
- }
|
|
- AVAILABLE_JAVA_CIPHER_SUITES = Collections.unmodifiableSet(availableJavaCipherSuites);
|
|
-
|
|
- final Set<String> availableCipherSuites = new LinkedHashSet<String>(
|
|
- 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;
|
|
- SUPPORTS_HOSTNAME_VALIDATION = supportsHostNameValidation;
|
|
- USE_KEYMANAGER_FACTORY = useKeyManagerFactory;
|
|
-
|
|
- Set<String> protocols = new LinkedHashSet<String>(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)) {
|
|
- protocols.add(PROTOCOL_SSL_V2);
|
|
- }
|
|
- if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3)) {
|
|
- protocols.add(PROTOCOL_SSL_V3);
|
|
- }
|
|
- if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1)) {
|
|
- protocols.add(PROTOCOL_TLS_V1);
|
|
- }
|
|
- if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1)) {
|
|
- protocols.add(PROTOCOL_TLS_V1_1);
|
|
- }
|
|
- if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2)) {
|
|
- protocols.add(PROTOCOL_TLS_V1_2);
|
|
- }
|
|
-
|
|
- SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols);
|
|
- SUPPORTS_OCSP = doesSupportOcsp();
|
|
- } else {
|
|
- AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet();
|
|
- AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet();
|
|
- AVAILABLE_CIPHER_SUITES = Collections.emptySet();
|
|
- SUPPORTS_KEYMANAGER_FACTORY = false;
|
|
- SUPPORTS_HOSTNAME_VALIDATION = false;
|
|
- USE_KEYMANAGER_FACTORY = false;
|
|
- SUPPORTED_PROTOCOLS_SET = Collections.emptySet();
|
|
- SUPPORTS_OCSP = false;
|
|
- }
|
|
- }
|
|
-
|
|
- 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) {
|
|
- 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
|
|
- * <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> and its OpenSSL support
|
|
- * are available.
|
|
- */
|
|
- public static boolean isAvailable() {
|
|
- return UNAVAILABILITY_CAUSE == null;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns {@code true} if the used version of openssl supports
|
|
- * <a href="https://tools.ietf.org/html/rfc7301">ALPN</a>.
|
|
- */
|
|
- 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 <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> 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
|
|
- * <a href="http://netty.io/wiki/forked-tomcat-native.html">{@code netty-tcnative}</a> 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<String> 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<String> 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<String> 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);
|
|
- 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;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns {@code true} if <a href="https://wiki.openssl.org/index.php/Hostname_validation">Hostname Validation</a>
|
|
- * is supported when using OpenSSL.
|
|
- */
|
|
- public static boolean supportsHostnameValidation() {
|
|
- return SUPPORTS_HOSTNAME_VALIDATION;
|
|
- }
|
|
-
|
|
- 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 = normalizeOs(SystemPropertyUtil.get("os.name", ""));
|
|
- String arch = normalizeArch(SystemPropertyUtil.get("os.arch", ""));
|
|
-
|
|
- Set<String> libNames = new LinkedHashSet<String>(4);
|
|
- // First, try loading the platform-specific library. Platform-specific
|
|
- // libraries will be available if using a tcnative uber jar.
|
|
- libNames.add("netty-tcnative-" + os + '-' + arch);
|
|
- if (LINUX.equalsIgnoreCase(os)) {
|
|
- // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0)..
|
|
- libNames.add("netty-tcnative-" + os + '-' + arch + "-fedora");
|
|
- }
|
|
- // finally the default library.
|
|
- libNames.add("netty-tcnative");
|
|
- // in Java 8, statically compiled JNI code is namespaced
|
|
- libNames.add("netty_tcnative");
|
|
-
|
|
- NativeLibraryLoader.loadFirstAvailable(SSL.class.getClassLoader(),
|
|
- libNames.toArray(new String[libNames.size()]));
|
|
- }
|
|
-
|
|
- private static boolean initializeTcNative() throws Exception {
|
|
- return Library.initialize();
|
|
- }
|
|
-
|
|
- private static String normalizeOs(String value) {
|
|
- value = normalize(value);
|
|
- if (value.startsWith("aix")) {
|
|
- return "aix";
|
|
- }
|
|
- if (value.startsWith("hpux")) {
|
|
- return "hpux";
|
|
- }
|
|
- if (value.startsWith("os400")) {
|
|
- // Avoid the names such as os4000
|
|
- if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) {
|
|
- return "os400";
|
|
- }
|
|
- }
|
|
- if (value.startsWith(LINUX)) {
|
|
- return LINUX;
|
|
- }
|
|
- if (value.startsWith("macosx") || value.startsWith("osx")) {
|
|
- return "osx";
|
|
- }
|
|
- if (value.startsWith("freebsd")) {
|
|
- return "freebsd";
|
|
- }
|
|
- if (value.startsWith("openbsd")) {
|
|
- return "openbsd";
|
|
- }
|
|
- if (value.startsWith("netbsd")) {
|
|
- return "netbsd";
|
|
- }
|
|
- if (value.startsWith("solaris") || value.startsWith("sunos")) {
|
|
- return "sunos";
|
|
- }
|
|
- if (value.startsWith("windows")) {
|
|
- return "windows";
|
|
- }
|
|
-
|
|
- return UNKNOWN;
|
|
- }
|
|
-
|
|
- private static String normalizeArch(String value) {
|
|
- value = normalize(value);
|
|
- if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
|
|
- return "x86_64";
|
|
- }
|
|
- if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
|
|
- return "x86_32";
|
|
- }
|
|
- if (value.matches("^(ia64|itanium64)$")) {
|
|
- return "itanium_64";
|
|
- }
|
|
- if (value.matches("^(sparc|sparc32)$")) {
|
|
- return "sparc_32";
|
|
- }
|
|
- if (value.matches("^(sparcv9|sparc64)$")) {
|
|
- return "sparc_64";
|
|
- }
|
|
- if (value.matches("^(arm|arm32)$")) {
|
|
- return "arm_32";
|
|
- }
|
|
- if ("aarch64".equals(value)) {
|
|
- return "aarch_64";
|
|
- }
|
|
- if (value.matches("^(ppc|ppc32)$")) {
|
|
- return "ppc_32";
|
|
- }
|
|
- if ("ppc64".equals(value)) {
|
|
- return "ppc_64";
|
|
- }
|
|
- if ("ppc64le".equals(value)) {
|
|
- return "ppcle_64";
|
|
- }
|
|
- if ("s390".equals(value)) {
|
|
- return "s390_32";
|
|
- }
|
|
- if ("s390x".equals(value)) {
|
|
- return "s390_64";
|
|
- }
|
|
-
|
|
- return UNKNOWN;
|
|
- }
|
|
-
|
|
- private static String normalize(String value) {
|
|
- return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
|
|
- }
|
|
-
|
|
- static void releaseIfNeeded(ReferenceCounted counted) {
|
|
- if (counted.refCnt() > 0) {
|
|
- ReferenceCountUtil.safeRelease(counted);
|
|
- }
|
|
- }
|
|
-}
|
|
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 4672d00..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java
|
|
+++ /dev/null
|
|
@@ -1,79 +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
|
|
- * <a href="https://www.openssl.org/docs/manmaster/apps/verify.html">error code</a>.
|
|
- */
|
|
- public OpenSslCertificateException(int errorCode) {
|
|
- this((String) null, errorCode);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Construct a new exception with the msg and
|
|
- * <a href="https://www.openssl.org/docs/manmaster/apps/verify.html">error code</a> .
|
|
- */
|
|
- public OpenSslCertificateException(String msg, int errorCode) {
|
|
- super(msg);
|
|
- this.errorCode = checkErrorCode(errorCode);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Construct a new exception with the msg, cause and
|
|
- * <a href="https://www.openssl.org/docs/manmaster/apps/verify.html">error code</a> .
|
|
- */
|
|
- public OpenSslCertificateException(String message, Throwable cause, int errorCode) {
|
|
- super(message, cause);
|
|
- this.errorCode = checkErrorCode(errorCode);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Construct a new exception with the cause and
|
|
- * <a href="https://www.openssl.org/docs/manmaster/apps/verify.html">error code</a> .
|
|
- */
|
|
- public OpenSslCertificateException(Throwable cause, int errorCode) {
|
|
- this(null, cause, errorCode);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Return the <a href="https://www.openssl.org/docs/man1.0.2/apps/verify.html">error code</a> to use.
|
|
- */
|
|
- public int errorCode() {
|
|
- return errorCode;
|
|
- }
|
|
-
|
|
- private static int checkErrorCode(int errorCode) {
|
|
- if (!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 46412e9..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java
|
|
+++ /dev/null
|
|
@@ -1,211 +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.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.
|
|
- * <p>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((File) 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<String> 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<String> 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<String> 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);
|
|
- }
|
|
-
|
|
- OpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
|
|
- KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
|
|
- CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
|
|
- long sessionCacheSize, long sessionTimeout, boolean enableOcsp)
|
|
- throws SSLException {
|
|
- super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain,
|
|
- ClientAuth.NONE, protocols, false, enableOcsp);
|
|
- boolean success = false;
|
|
- try {
|
|
- sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
|
|
- keyCertChain, key, keyPassword, keyManagerFactory);
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- release();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public OpenSslSessionContext sessionContext() {
|
|
- return sessionContext;
|
|
- }
|
|
-
|
|
- @Override
|
|
- OpenSslKeyMaterialManager keyMaterialManager() {
|
|
- return null;
|
|
- }
|
|
-}
|
|
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 c4ca6b5..0000000
|
|
--- 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<String> 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<String> 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) {
|
|
- return new OpenSslEngine(this, alloc, peerHost, peerPort);
|
|
- }
|
|
-
|
|
- @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 cbc7ee4..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
|
|
+++ /dev/null
|
|
@@ -1,40 +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
|
|
- * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL BIO abstractions</a>.
|
|
- * <p>
|
|
- * 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) {
|
|
- super(context, alloc, peerHost, peerPort, 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 02131b4..0000000
|
|
--- 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/OpenSslExtendedKeyMaterialManager.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslExtendedKeyMaterialManager.java
|
|
deleted file mode 100644
|
|
index 38f6a7f..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslExtendedKeyMaterialManager.java
|
|
+++ /dev/null
|
|
@@ -1,40 +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.X509ExtendedKeyManager;
|
|
-import javax.security.auth.x500.X500Principal;
|
|
-
|
|
-final class OpenSslExtendedKeyMaterialManager extends OpenSslKeyMaterialManager {
|
|
-
|
|
- private final X509ExtendedKeyManager keyManager;
|
|
-
|
|
- OpenSslExtendedKeyMaterialManager(X509ExtendedKeyManager keyManager, String password) {
|
|
- super(keyManager, password);
|
|
- this.keyManager = keyManager;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected String chooseClientAlias(ReferenceCountedOpenSslEngine engine, String[] keyTypes,
|
|
- X500Principal[] issuer) {
|
|
- return keyManager.chooseEngineClientAlias(keyTypes, issuer, engine);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) {
|
|
- return keyManager.chooseEngineServerAlias(type, null, engine);
|
|
- }
|
|
-}
|
|
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 2e48e8b..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java
|
|
+++ /dev/null
|
|
@@ -1,179 +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.CertificateRequestedCallback;
|
|
-import io.netty.internal.tcnative.SSL;
|
|
-
|
|
-import javax.net.ssl.SSLException;
|
|
-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;
|
|
-
|
|
-import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.freeBio;
|
|
-import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO;
|
|
-
|
|
-/**
|
|
- * Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and
|
|
- * {@link X509Certificate}s.
|
|
- */
|
|
-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<String, String> KEY_TYPES = new HashMap<String, String>();
|
|
- 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 X509KeyManager keyManager;
|
|
- private final String password;
|
|
-
|
|
- OpenSslKeyMaterialManager(X509KeyManager keyManager, String password) {
|
|
- this.keyManager = keyManager;
|
|
- this.password = password;
|
|
- }
|
|
-
|
|
- void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException {
|
|
- long ssl = engine.sslPointer();
|
|
- String[] authMethods = SSL.authenticationMethods(ssl);
|
|
- Set<String> aliases = new HashSet<String>(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)) {
|
|
- setKeyMaterial(ssl, alias);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- CertificateRequestedCallback.KeyMaterial keyMaterial(ReferenceCountedOpenSslEngine engine, String[] keyTypes,
|
|
- X500Principal[] issuer) throws SSLException {
|
|
- String alias = chooseClientAlias(engine, keyTypes, issuer);
|
|
- long keyBio = 0;
|
|
- long keyCertChainBio = 0;
|
|
- long pkey = 0;
|
|
- long certChain = 0;
|
|
-
|
|
- try {
|
|
- // TODO: Should we cache these and so not need to do a memory copy all the time ?
|
|
- X509Certificate[] certificates = keyManager.getCertificateChain(alias);
|
|
- if (certificates == null || certificates.length == 0) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- PrivateKey key = keyManager.getPrivateKey(alias);
|
|
- keyCertChainBio = toBIO(certificates);
|
|
- certChain = SSL.parseX509Chain(keyCertChainBio);
|
|
- if (key != null) {
|
|
- keyBio = toBIO(key);
|
|
- pkey = SSL.parsePrivateKey(keyBio, password);
|
|
- }
|
|
- CertificateRequestedCallback.KeyMaterial material = new CertificateRequestedCallback.KeyMaterial(
|
|
- certChain, pkey);
|
|
-
|
|
- // Reset to 0 so we do not free these. This is needed as the client certificate callback takes ownership
|
|
- // of both the key and the certificate if they are returned from this method, and thus must not
|
|
- // be freed here.
|
|
- certChain = pkey = 0;
|
|
- return material;
|
|
- } catch (SSLException e) {
|
|
- throw e;
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- } finally {
|
|
- freeBio(keyBio);
|
|
- freeBio(keyCertChainBio);
|
|
- SSL.freePrivateKey(pkey);
|
|
- SSL.freeX509Chain(certChain);
|
|
- }
|
|
- }
|
|
-
|
|
- private void setKeyMaterial(long ssl, String alias) throws SSLException {
|
|
- long keyBio = 0;
|
|
- long keyCertChainBio = 0;
|
|
- long keyCertChainBio2 = 0;
|
|
-
|
|
- try {
|
|
- // TODO: Should we cache these and so not need to do a memory copy all the time ?
|
|
- X509Certificate[] certificates = keyManager.getCertificateChain(alias);
|
|
- if (certificates == null || certificates.length == 0) {
|
|
- return;
|
|
- }
|
|
-
|
|
- PrivateKey key = keyManager.getPrivateKey(alias);
|
|
-
|
|
- // Only encode one time
|
|
- PemEncoded encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, certificates);
|
|
- try {
|
|
- keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
|
|
- keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
|
|
-
|
|
- if (key != null) {
|
|
- keyBio = toBIO(key);
|
|
- }
|
|
- SSL.setCertificateBio(ssl, keyCertChainBio, keyBio, password);
|
|
-
|
|
- // We may have more then one cert in the chain so add all of them now.
|
|
- SSL.setCertificateChainBio(ssl, keyCertChainBio2, true);
|
|
- } finally {
|
|
- encoded.release();
|
|
- }
|
|
- } catch (SSLException e) {
|
|
- throw e;
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- } finally {
|
|
- freeBio(keyBio);
|
|
- freeBio(keyCertChainBio);
|
|
- freeBio(keyCertChainBio2);
|
|
- }
|
|
- }
|
|
-
|
|
- protected String chooseClientAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine,
|
|
- String[] keyTypes, X500Principal[] issuer) {
|
|
- return keyManager.chooseClientAlias(keyTypes, issuer, null);
|
|
- }
|
|
-
|
|
- protected String chooseServerAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine, String type) {
|
|
- return keyManager.chooseServerAlias(type, null, null);
|
|
- }
|
|
-}
|
|
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 f57434b..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java
|
|
+++ /dev/null
|
|
@@ -1,373 +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.handler.ssl.ReferenceCountedOpenSslServerContext.ServerContext;
|
|
-import io.netty.internal.tcnative.SSL;
|
|
-
|
|
-import java.io.File;
|
|
-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.
|
|
- * <p>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;
|
|
- private final OpenSslKeyMaterialManager keyMaterialManager;
|
|
-
|
|
- /**
|
|
- * 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<String> 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<String> ciphers, Iterable<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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);
|
|
- }
|
|
-
|
|
- OpenSslServerContext(
|
|
- X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
|
|
- Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
|
- long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
|
|
- boolean enableOcsp) throws SSLException {
|
|
- this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
|
|
- cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
|
|
- enableOcsp);
|
|
- }
|
|
-
|
|
- @SuppressWarnings("deprecation")
|
|
- private OpenSslServerContext(
|
|
- X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
|
|
- Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
|
|
- long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
|
|
- boolean enableOcsp) 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 {
|
|
- ServerContext context = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
|
|
- keyCertChain, key, keyPassword, keyManagerFactory);
|
|
- sessionContext = context.sessionContext;
|
|
- keyMaterialManager = context.keyMaterialManager;
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- release();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public OpenSslServerSessionContext sessionContext() {
|
|
- return sessionContext;
|
|
- }
|
|
-
|
|
- @Override
|
|
- OpenSslKeyMaterialManager keyMaterialManager() {
|
|
- return keyMaterialManager;
|
|
- }
|
|
-}
|
|
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 8c92deb..0000000
|
|
--- 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) {
|
|
- super(context);
|
|
- }
|
|
-
|
|
- @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 <a href="http://www.openssl.org/docs/ssl/SSL_CTX_set_session_id_context.html">
|
|
- * man SSL_CTX_set_session_id_context</a>
|
|
- *
|
|
- * @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 846a968..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java
|
|
+++ /dev/null
|
|
@@ -1,137 +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.ObjectUtil;
|
|
-import io.netty.internal.tcnative.SSL;
|
|
-import io.netty.internal.tcnative.SSLContext;
|
|
-import io.netty.internal.tcnative.SessionTicketKey;
|
|
-
|
|
-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<byte[]> EMPTY = new EmptyEnumeration();
|
|
-
|
|
- private final OpenSslSessionStats stats;
|
|
- 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) {
|
|
- this.context = context;
|
|
- stats = new OpenSslSessionStats(context);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLSession getSession(byte[] bytes) {
|
|
- if (bytes == null) {
|
|
- throw new NullPointerException("bytes");
|
|
- }
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Enumeration<byte[]> 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.
|
|
- */
|
|
- 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);
|
|
- 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;
|
|
- }
|
|
-
|
|
- private static final class EmptyEnumeration implements Enumeration<byte[]> {
|
|
- @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 f49b95f..0000000
|
|
--- 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 <a href="https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_number.html">SSL_CTX_sess_number</a>
|
|
- */
|
|
-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 79f71a6..0000000
|
|
--- 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/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java
|
|
deleted file mode 100644
|
|
index b213573..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java
|
|
+++ /dev/null
|
|
@@ -1,298 +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.util.internal.logging.InternalLogger;
|
|
-import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
-import io.netty.internal.tcnative.CertificateRequestedCallback;
|
|
-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.HashSet;
|
|
-import java.util.Set;
|
|
-
|
|
-import javax.net.ssl.KeyManagerFactory;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.SSLHandshakeException;
|
|
-import javax.net.ssl.TrustManagerFactory;
|
|
-import javax.net.ssl.X509ExtendedKeyManager;
|
|
-import javax.net.ssl.X509ExtendedTrustManager;
|
|
-import javax.net.ssl.X509KeyManager;
|
|
-import javax.net.ssl.X509TrustManager;
|
|
-import javax.security.auth.x500.X500Principal;
|
|
-
|
|
-/**
|
|
- * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
|
- * <p>Instances of this class must be {@link #release() released} or else native memory will leak!
|
|
- *
|
|
- * <p>Instances of this class <strong>must not</strong> 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 final OpenSslSessionContext sessionContext;
|
|
-
|
|
- ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
|
|
- KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
|
|
- CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
|
- String[] protocols, long sessionCacheSize, long sessionTimeout,
|
|
- boolean enableOcsp) 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);
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- release();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- OpenSslKeyMaterialManager keyMaterialManager() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @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) 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");
|
|
- }
|
|
- 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) {
|
|
- keyManagerFactory = buildKeyManagerFactory(
|
|
- keyCertChain, key, keyPassword, keyManagerFactory);
|
|
- }
|
|
-
|
|
- if (keyManagerFactory != null) {
|
|
- X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers());
|
|
- OpenSslKeyMaterialManager materialManager = useExtendedKeyManager(keyManager) ?
|
|
- new OpenSslExtendedKeyMaterialManager(
|
|
- (X509ExtendedKeyManager) keyManager, keyPassword) :
|
|
- new OpenSslKeyMaterialManager(keyManager, keyPassword);
|
|
- SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback(
|
|
- engineMap, materialManager));
|
|
- }
|
|
- }
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to set certificate and key", e);
|
|
- }
|
|
-
|
|
- SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
|
|
-
|
|
- try {
|
|
- if (trustCertCollection != null) {
|
|
- trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
|
|
- } 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
|
|
-
|
|
- // 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));
|
|
- }
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("unable to setup trustmanager", e);
|
|
- }
|
|
- return new OpenSslClientSessionContext(thiz);
|
|
- }
|
|
-
|
|
- // No cache is currently supported for client side mode.
|
|
- static final class OpenSslClientSessionContext extends OpenSslSessionContext {
|
|
- OpenSslClientSessionContext(ReferenceCountedOpenSslContext context) {
|
|
- super(context);
|
|
- }
|
|
-
|
|
- @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);
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier {
|
|
- private final X509ExtendedTrustManager manager;
|
|
-
|
|
- ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) {
|
|
- super(engineMap);
|
|
- this.manager = manager;
|
|
- }
|
|
-
|
|
- @Override
|
|
- void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth)
|
|
- throws Exception {
|
|
- manager.checkServerTrusted(peerCerts, auth, engine);
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class OpenSslCertificateRequestedCallback implements CertificateRequestedCallback {
|
|
- private final OpenSslEngineMap engineMap;
|
|
- private final OpenSslKeyMaterialManager keyManagerHolder;
|
|
-
|
|
- OpenSslCertificateRequestedCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) {
|
|
- this.engineMap = engineMap;
|
|
- this.keyManagerHolder = keyManagerHolder;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public KeyMaterial requested(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) {
|
|
- final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
|
|
- try {
|
|
- final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes);
|
|
- final String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]);
|
|
- 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]);
|
|
- }
|
|
- }
|
|
- return keyManagerHolder.keyMaterial(engine, keyTypes, issuers);
|
|
- } catch (Throwable cause) {
|
|
- logger.debug("request of key failed", cause);
|
|
- SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
|
|
- e.initCause(cause);
|
|
- engine.handshakeException = e;
|
|
- return null;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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<String> supportedClientKeyTypes(byte[] clientCertificateTypes) {
|
|
- Set<String> result = new HashSet<String>(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 CertificateRequestedCallback.TLS_CT_RSA_SIGN:
|
|
- return OpenSslKeyMaterialManager.KEY_TYPE_RSA; // RFC rsa_sign
|
|
- case CertificateRequestedCallback.TLS_CT_RSA_FIXED_DH:
|
|
- return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
|
|
- case CertificateRequestedCallback.TLS_CT_ECDSA_SIGN:
|
|
- return OpenSslKeyMaterialManager.KEY_TYPE_EC; // RFC ecdsa_sign
|
|
- case CertificateRequestedCallback.TLS_CT_RSA_FIXED_ECDH:
|
|
- return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
|
|
- case CertificateRequestedCallback.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 ee049ab..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java
|
|
+++ /dev/null
|
|
@@ -1,867 +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.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.PlatformDependent;
|
|
-import io.netty.util.internal.StringUtil;
|
|
-import io.netty.util.internal.SystemPropertyUtil;
|
|
-import io.netty.util.internal.logging.InternalLogger;
|
|
-import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
-
|
|
-import java.security.AccessController;
|
|
-import java.security.PrivateKey;
|
|
-import java.security.PrivilegedAction;
|
|
-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.ArrayList;
|
|
-import java.util.Arrays;
|
|
-import java.util.Collections;
|
|
-import java.util.List;
|
|
-import java.util.Map;
|
|
-
|
|
-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.SSLEngine;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.SSLHandshakeException;
|
|
-import javax.net.ssl.TrustManager;
|
|
-import javax.net.ssl.X509ExtendedKeyManager;
|
|
-import javax.net.ssl.X509ExtendedTrustManager;
|
|
-import javax.net.ssl.X509KeyManager;
|
|
-import javax.net.ssl.X509TrustManager;
|
|
-
|
|
-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
|
|
- * <a href="https://www.openssl.org/">OpenSsl</a> C library API.
|
|
- * <p>Instances of this class must be {@link #release() released} or else native memory will leak!
|
|
- *
|
|
- * <p>Instances of this class <strong>must not</strong> 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);
|
|
- /**
|
|
- * To make it easier for users to replace JDK implementation with OpenSsl version we also use
|
|
- * {@code jdk.tls.rejectClientInitiatedRenegotiation} to allow disabling client initiated renegotiation.
|
|
- * Java8+ uses this system property as well.
|
|
- * <p>
|
|
- * See also <a href="http://blog.ivanristic.com/2014/03/ssl-tls-improvements-in-java-8.html">
|
|
- * Significant SSL/TLS improvements in Java 8</a>
|
|
- */
|
|
- private static final boolean JDK_REJECT_CLIENT_INITIATED_RENEGOTIATION =
|
|
- AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
|
- @Override
|
|
- public Boolean run() {
|
|
- return SystemPropertyUtil.getBoolean("jdk.tls.rejectClientInitiatedRenegotiation", false);
|
|
- }
|
|
- });
|
|
-
|
|
- private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE =
|
|
- AccessController.doPrivileged(new PrivilegedAction<Integer>() {
|
|
- @Override
|
|
- public Integer run() {
|
|
- return Math.max(1,
|
|
- SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize",
|
|
- 2048));
|
|
- }
|
|
- });
|
|
-
|
|
- private static final List<String> DEFAULT_CIPHERS;
|
|
- private static final Integer DH_KEY_LENGTH;
|
|
- private static final ResourceLeakDetector<ReferenceCountedOpenSslContext> leakDetector =
|
|
- ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class);
|
|
-
|
|
- // TODO: Maybe make configurable ?
|
|
- protected static final int VERIFY_DEPTH = 10;
|
|
-
|
|
- /**
|
|
- * The OpenSSL SSL_CTX object.
|
|
- *
|
|
- * <strong>{@link #ctxLock} must be hold while using ctx!</strong>
|
|
- */
|
|
- protected long ctx;
|
|
- private final List<String> unmodifiableCiphers;
|
|
- private final long sessionCacheSize;
|
|
- private final long sessionTimeout;
|
|
- private final OpenSslApplicationProtocolNegotiator apn;
|
|
- private final int mode;
|
|
-
|
|
- // Reference Counting
|
|
- private final ResourceLeakTracker<ReferenceCountedOpenSslContext> 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 boolean rejectRemoteInitiatedRenegotiation;
|
|
- private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE;
|
|
-
|
|
- static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
|
|
- new OpenSslApplicationProtocolNegotiator() {
|
|
- @Override
|
|
- public ApplicationProtocolConfig.Protocol protocol() {
|
|
- return ApplicationProtocolConfig.Protocol.NONE;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public List<String> 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 {
|
|
- List<String> ciphers = new ArrayList<String>();
|
|
- // XXX: Make sure to sync this list with JdkSslEngineFactory.
|
|
- Collections.addAll(
|
|
- ciphers,
|
|
- "ECDHE-ECDSA-AES256-GCM-SHA384",
|
|
- "ECDHE-ECDSA-AES128-GCM-SHA256",
|
|
- "ECDHE-RSA-AES128-GCM-SHA256",
|
|
- "ECDHE-RSA-AES128-SHA",
|
|
- "ECDHE-RSA-AES256-SHA",
|
|
- "AES128-GCM-SHA256",
|
|
- "AES128-SHA",
|
|
- "AES256-SHA");
|
|
- DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
|
|
-
|
|
- if (logger.isDebugEnabled()) {
|
|
- logger.debug("Default cipher suite (OpenSSL): " + ciphers);
|
|
- }
|
|
-
|
|
- Integer dhLen = null;
|
|
-
|
|
- try {
|
|
- String dhKeySize = AccessController.doPrivileged(new PrivilegedAction<String>() {
|
|
- @Override
|
|
- public String run() {
|
|
- return 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<String> 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<String> 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;
|
|
-
|
|
- if (mode == SSL.SSL_MODE_SERVER) {
|
|
- rejectRemoteInitiatedRenegotiation =
|
|
- JDK_REJECT_CLIENT_INITIATED_RENEGOTIATION;
|
|
- }
|
|
- this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone();
|
|
- final List<String> convertedCiphers;
|
|
- if (ciphers == null) {
|
|
- convertedCiphers = null;
|
|
- } else {
|
|
- convertedCiphers = new ArrayList<String>();
|
|
- for (String c : ciphers) {
|
|
- if (c == null) {
|
|
- break;
|
|
- }
|
|
-
|
|
- String converted = CipherSuiteConverter.toOpenSsl(c);
|
|
- if (converted != null) {
|
|
- c = converted;
|
|
- }
|
|
- convertedCiphers.add(c);
|
|
- }
|
|
- }
|
|
-
|
|
- unmodifiableCiphers = Arrays.asList(checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
|
|
- convertedCiphers, DEFAULT_CIPHERS, OpenSsl.availableOpenSslCipherSuites()));
|
|
-
|
|
- this.apn = checkNotNull(apn, "apn");
|
|
-
|
|
- // Create a new SSL_CTX and configure it.
|
|
- boolean success = false;
|
|
- try {
|
|
- try {
|
|
- ctx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, mode);
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to create an SSL_CTX", e);
|
|
- }
|
|
-
|
|
- SSLContext.setOptions(ctx, SSLContext.getOptions(ctx) |
|
|
- SSL.SSL_OP_NO_SSLv2 |
|
|
- SSL.SSL_OP_NO_SSLv3 |
|
|
- 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);
|
|
-
|
|
- // 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 the ciphers that are permitted to negotiate. */
|
|
- try {
|
|
- SSLContext.setCipherSuite(ctx, CipherSuiteConverter.toOpenSsl(unmodifiableCiphers));
|
|
- } catch (SSLException e) {
|
|
- throw e;
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to set cipher suite: " + unmodifiableCiphers, e);
|
|
- }
|
|
-
|
|
- List<String> nextProtoList = apn.protocols();
|
|
- /* Set next protocols for next protocol negotiation extension, if specified */
|
|
- if (!nextProtoList.isEmpty()) {
|
|
- String[] appProtocols = nextProtoList.toArray(new String[nextProtoList.size()]);
|
|
- 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) {
|
|
- this.sessionCacheSize = sessionCacheSize;
|
|
- SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
|
|
- } else {
|
|
- // Get the default session cache size using SSLContext.setSessionCacheSize()
|
|
- this.sessionCacheSize = sessionCacheSize = SSLContext.setSessionCacheSize(ctx, 20480);
|
|
- // Revert the session cache size to the default value.
|
|
- SSLContext.setSessionCacheSize(ctx, sessionCacheSize);
|
|
- }
|
|
-
|
|
- /* Set session timeout, if specified */
|
|
- if (sessionTimeout > 0) {
|
|
- this.sessionTimeout = sessionTimeout;
|
|
- SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
|
|
- } else {
|
|
- // Get the default session timeout using SSLContext.setSessionCacheTimeout()
|
|
- this.sessionTimeout = sessionTimeout = SSLContext.setSessionCacheTimeout(ctx, 300);
|
|
- // Revert the session timeout to the default value.
|
|
- SSLContext.setSessionCacheTimeout(ctx, sessionTimeout);
|
|
- }
|
|
-
|
|
- if (enableOcsp) {
|
|
- SSLContext.enableOcsp(ctx, isClient());
|
|
- }
|
|
- 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<String> 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);
|
|
- }
|
|
-
|
|
- SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
|
- return new ReferenceCountedOpenSslEngine(this, alloc, peerHost, peerPort, true);
|
|
- }
|
|
-
|
|
- abstract OpenSslKeyMaterialManager keyMaterialManager();
|
|
-
|
|
- /**
|
|
- * 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() {
|
|
- Lock readerLock = ctxLock.readLock();
|
|
- readerLock.lock();
|
|
- try {
|
|
- return ctx;
|
|
- } finally {
|
|
- readerLock.unlock();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the stats of this context.
|
|
- *
|
|
- * @deprecated use {@link #sessionContext#stats()}
|
|
- */
|
|
- @Deprecated
|
|
- public final OpenSslSessionStats stats() {
|
|
- return sessionContext().stats();
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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.
|
|
- */
|
|
- public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) {
|
|
- this.rejectRemoteInitiatedRenegotiation = rejectRemoteInitiatedRenegotiation;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns if remote initiated renegotiation is supported or not.
|
|
- */
|
|
- public boolean getRejectRemoteInitiatedRenegotiation() {
|
|
- return rejectRemoteInitiatedRenegotiation;
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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 ctx;
|
|
- } finally {
|
|
- readerLock.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;
|
|
- }
|
|
- } 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) {
|
|
- 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
|
|
- */
|
|
- 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();
|
|
- }
|
|
- }
|
|
-
|
|
- static boolean useExtendedTrustManager(X509TrustManager trustManager) {
|
|
- return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
|
|
- }
|
|
-
|
|
- static boolean useExtendedKeyManager(X509KeyManager keyManager) {
|
|
- return PlatformDependent.javaVersion() >= 7 && keyManager instanceof X509ExtendedKeyManager;
|
|
- }
|
|
-
|
|
- @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) {
|
|
- X509Certificate[] peerCerts = certificates(chain);
|
|
- final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
|
|
- try {
|
|
- verify(engine, peerCerts, auth);
|
|
- return CertificateVerifier.X509_V_OK;
|
|
- } catch (Throwable cause) {
|
|
- logger.debug("verification of certificate failed", cause);
|
|
- SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
|
|
- e.initCause(cause);
|
|
- engine.handshakeException = e;
|
|
-
|
|
- // 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) {
|
|
- 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();
|
|
- }
|
|
- }
|
|
-
|
|
- // Could not detect a specific error code to use, so fallback to a default code.
|
|
- 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<Long, ReferenceCountedOpenSslEngine> 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(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 <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
|
|
- * or {@code 0} if the {@code key} is {@code null}. The BIO contains the content of the {@code key}.
|
|
- */
|
|
- static long toBIO(PrivateKey key) throws Exception {
|
|
- if (key == null) {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
|
|
- PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key);
|
|
- try {
|
|
- return toBIO(allocator, pem.retain());
|
|
- } finally {
|
|
- pem.release();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Return the pointer to a <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
|
|
- * or {@code 0} if the {@code certChain} is {@code null}. The BIO contains the content of the {@code certChain}.
|
|
- */
|
|
- static long toBIO(X509Certificate... certChain) throws Exception {
|
|
- if (certChain == null) {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (certChain.length == 0) {
|
|
- throw new IllegalArgumentException("certChain can't be empty");
|
|
- }
|
|
-
|
|
- ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
|
|
- 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();
|
|
- }
|
|
- }
|
|
-}
|
|
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 27460c7..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java
|
|
+++ /dev/null
|
|
@@ -1,2037 +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.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.PlatformDependent;
|
|
-import io.netty.util.internal.StringUtil;
|
|
-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.HashMap;
|
|
-import java.util.List;
|
|
-import java.util.Map;
|
|
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
|
-
|
|
-import java.util.concurrent.locks.Lock;
|
|
-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.SSL_RECORD_HEADER_LENGTH;
|
|
-import static io.netty.util.internal.EmptyArrays.EMPTY_CERTIFICATES;
|
|
-import static io.netty.util.internal.EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
|
-import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
-import static java.lang.Math.min;
|
|
-import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
|
|
-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
|
|
- * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL BIO abstractions</a>.
|
|
- * <p>Instances of this class must be {@link #release() released} or else native memory will leak!
|
|
- *
|
|
- * <p>Instances of this class <strong>must</strong> 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 {
|
|
-
|
|
- private static final InternalLogger logger = InternalLoggerFactory.getInstance(ReferenceCountedOpenSslEngine.class);
|
|
-
|
|
- private static final SSLException BEGIN_HANDSHAKE_ENGINE_CLOSED = ThrowableUtil.unknownStackTrace(
|
|
- new SSLException("engine closed"), ReferenceCountedOpenSslEngine.class, "beginHandshake()");
|
|
- private static final SSLException HANDSHAKE_ENGINE_CLOSED = ThrowableUtil.unknownStackTrace(
|
|
- new SSLException("engine closed"), ReferenceCountedOpenSslEngine.class, "handshake()");
|
|
- private static final SSLException RENEGOTIATION_UNSUPPORTED = ThrowableUtil.unknownStackTrace(
|
|
- new SSLException("renegotiation unsupported"), ReferenceCountedOpenSslEngine.class, "beginHandshake()");
|
|
- private static final ResourceLeakDetector<ReferenceCountedOpenSslEngine> leakDetector =
|
|
- ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslEngine.class);
|
|
- /**
|
|
- * <a href="https://www.openssl.org/docs/man1.0.2/crypto/X509_check_host.html">The flags argument is usually 0</a>.
|
|
- */
|
|
- private static final int DEFAULT_HOSTNAME_VALIDATION_FLAGS = 0;
|
|
-
|
|
- static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
|
|
-
|
|
- /**
|
|
- * This is the maximum overhead when encrypting plaintext as defined by
|
|
- * <a href="https://www.ietf.org/rfc/rfc5246.txt">rfc5264</a>,
|
|
- * <a href="https://www.ietf.org/rfc/rfc5289.txt">rfc5289</a> and openssl implementation itself.
|
|
- *
|
|
- * Please note that we use a padding of 16 here as openssl uses PKC#5 which uses 16 bytes while the spec itself
|
|
- * allow up to 255 bytes. 16 bytes is the max for PKC#5 (which handles it the same way as PKC#7) as we use a block
|
|
- * size of 16. See <a href="https://tools.ietf.org/html/rfc5652#section-6.3">rfc5652#section-6.3</a>.
|
|
- *
|
|
- * TLS Header (5) + 16 (IV) + 48 (MAC) + 1 (Padding_length field) + 15 (Padding) + 1 (ContentType) +
|
|
- * 2 (ProtocolVersion) + 2 (Length)
|
|
- *
|
|
- * TODO: We may need to review this calculation once TLS 1.3 becomes available.
|
|
- */
|
|
- static final int MAX_TLS_RECORD_OVERHEAD_LENGTH = SSL_RECORD_HEADER_LENGTH + 16 + 48 + 1 + 15 + 1 + 2 + 2;
|
|
-
|
|
- static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_PLAINTEXT_LENGTH + MAX_TLS_RECORD_OVERHEAD_LENGTH;
|
|
-
|
|
- private static final AtomicIntegerFieldUpdater<ReferenceCountedOpenSslEngine> DESTROYED_UPDATER =
|
|
- AtomicIntegerFieldUpdater.newUpdater(ReferenceCountedOpenSslEngine.class, "destroyed");
|
|
-
|
|
- private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
|
|
- 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 boolean certificateSet;
|
|
-
|
|
- 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 renegotiationPending;
|
|
- private boolean receivedShutdown;
|
|
- private volatile int destroyed;
|
|
-
|
|
- // Reference Counting
|
|
- private final ResourceLeakTracker<ReferenceCountedOpenSslEngine> 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;
|
|
- }
|
|
- }
|
|
- };
|
|
-
|
|
- private volatile ClientAuth clientAuth = ClientAuth.NONE;
|
|
-
|
|
- // 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<String> 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;
|
|
-
|
|
- private final boolean clientMode;
|
|
- private final ByteBufAllocator alloc;
|
|
- private final OpenSslEngineMap engineMap;
|
|
- private final OpenSslApplicationProtocolNegotiator apn;
|
|
- private final boolean rejectRemoteInitiatedRenegotiation;
|
|
- private final OpenSslSession session;
|
|
- private final Certificate[] localCerts;
|
|
- private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1];
|
|
- private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
|
|
- private final OpenSslKeyMaterialManager keyMaterialManager;
|
|
- private final boolean enableOcsp;
|
|
-
|
|
- // This is package-private as we set it from OpenSslContext if an exception is thrown during
|
|
- // the verification step.
|
|
- SSLHandshakeException handshakeException;
|
|
-
|
|
- /**
|
|
- * 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 leakDetection {@code true} to enable leak detection of this object.
|
|
- */
|
|
- ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, ByteBufAllocator alloc, String peerHost,
|
|
- int peerPort, boolean leakDetection) {
|
|
- super(peerHost, peerPort);
|
|
- OpenSsl.ensureAvailability();
|
|
- leak = leakDetection ? leakDetector.track(this) : null;
|
|
- this.alloc = checkNotNull(alloc, "alloc");
|
|
- apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator();
|
|
- session = new OpenSslSession(context.sessionContext());
|
|
- clientMode = context.isClient();
|
|
- engineMap = context.engineMap;
|
|
- rejectRemoteInitiatedRenegotiation = context.getRejectRemoteInitiatedRenegotiation();
|
|
- localCerts = context.keyCertChain;
|
|
- keyMaterialManager = context.keyMaterialManager();
|
|
- enableOcsp = context.enableOcsp;
|
|
-
|
|
- Lock readerLock = context.ctxLock.readLock();
|
|
- readerLock.lock();
|
|
- try {
|
|
- ssl = SSL.newSSL(context.ctx, !context.isClient());
|
|
- } finally {
|
|
- readerLock.unlock();
|
|
- }
|
|
- 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
|
|
- // See https://github.com/netty/netty/issues/4746
|
|
- if (clientMode && peerHost != null) {
|
|
- SSL.setTlsExtHostName(ssl, peerHost);
|
|
- }
|
|
-
|
|
- if (enableOcsp) {
|
|
- SSL.enableOcsp(ssl);
|
|
- }
|
|
- } catch (Throwable cause) {
|
|
- SSL.freeSSL(ssl);
|
|
- PlatformDependent.throwException(cause);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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) {
|
|
- 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) {
|
|
- 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_UPDATER.compareAndSet(this, 0, 1)) {
|
|
- 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, Buffer.address(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, Buffer.address(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, Buffer.address(dst) + pos, dst.limit() - pos);
|
|
- if (sslRead > 0) {
|
|
- dst.position(pos + sslRead);
|
|
- }
|
|
- } else {
|
|
- final int limit = dst.limit();
|
|
- final int len = min(MAX_ENCRYPTED_PACKET_LENGTH, 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;
|
|
- }
|
|
-
|
|
- @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, Buffer.address(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);
|
|
-
|
|
- // Explicit use outboundClosed as we want to drain any bytes that are still present.
|
|
- if (outboundClosed) {
|
|
- // 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 (bytesProduced > 0 && handshakeException != 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.
|
|
- return newResult(NEED_WRAP, 0, bytesProduced);
|
|
- }
|
|
-
|
|
- status = handshake();
|
|
-
|
|
- if (renegotiationPending && status == FINISHED) {
|
|
- // If renegotiationPending is true that means when we attempted to start renegotiation
|
|
- // the BIO buffer didn't have enough space to hold the HelloRequest which prompts the
|
|
- // client to initiate a renegotiation. At this point the HelloRequest has been written
|
|
- // so we can actually start the handshake process.
|
|
- renegotiationPending = false;
|
|
- SSL.setState(ssl, SSL.SSL_ST_ACCEPT);
|
|
- handshakeState = HandshakeState.STARTED_EXPLICITLY;
|
|
- 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 (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);
|
|
- }
|
|
- }
|
|
-
|
|
- int srcsLen = 0;
|
|
- final int endOffset = offset + length;
|
|
- 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;
|
|
- }
|
|
- }
|
|
-
|
|
- // we 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 (dst.remaining() < calculateOutNetBufSize(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;
|
|
- // Flush any data that may have been written implicitly by OpenSSL in case a shutdown/alert occurs.
|
|
- bytesProduced = SSL.bioFlushByteBuffer(networkBIO);
|
|
- for (; offset < endOffset; ++offset) {
|
|
- final ByteBuffer src = srcs[offset];
|
|
- final int remaining = src.remaining();
|
|
- if (remaining == 0) {
|
|
- continue;
|
|
- }
|
|
-
|
|
- // Write plaintext application data to the SSL engine
|
|
- int bytesWritten = writePlaintextData(src, min(remaining, MAX_PLAINTEXT_LENGTH - bytesConsumed));
|
|
-
|
|
- if (bytesWritten > 0) {
|
|
- bytesConsumed += bytesWritten;
|
|
-
|
|
- // Determine how much encrypted data was generated:
|
|
- final int pendingNow = SSL.bioLengthByteBuffer(networkBIO);
|
|
- bytesProduced += bioLengthBefore - pendingNow;
|
|
- bioLengthBefore = pendingNow;
|
|
-
|
|
- 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."
|
|
- //
|
|
- // So we attempt to drain the BIO buffer below, but if there is no data this condition
|
|
- // is undefined and we assume their is a fatal error with the openssl engine and close.
|
|
- // [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html
|
|
- return newResult(NEED_WRAP, bytesConsumed, bytesProduced);
|
|
- } else {
|
|
- // Everything else is considered as error
|
|
- throw shutdownWithError("SSL_write");
|
|
- }
|
|
- }
|
|
- }
|
|
- 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);
|
|
- }
|
|
- 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) {
|
|
- String err = SSL.getLastError();
|
|
- return shutdownWithError(operations, err);
|
|
- }
|
|
-
|
|
- private SSLException shutdownWithError(String operation, String err) {
|
|
- if (logger.isDebugEnabled()) {
|
|
- logger.debug("{} failed: OpenSSL error: {}", operation, err);
|
|
- }
|
|
-
|
|
- // There was an internal error -- shutdown
|
|
- shutdown();
|
|
- if (handshakeState == HandshakeState.FINISHED) {
|
|
- return new SSLException(err);
|
|
- }
|
|
- return new SSLHandshakeException(err);
|
|
- }
|
|
-
|
|
- 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
|
|
- if (srcs == null) {
|
|
- throw new NullPointerException("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_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;
|
|
- }
|
|
- }
|
|
-
|
|
- if (len < SSL_RECORD_HEADER_LENGTH) {
|
|
- return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
|
- }
|
|
-
|
|
- int packetLength = SslUtils.getEncryptedPacketLength(srcs, srcsOffset);
|
|
-
|
|
- if (packetLength == SslUtils.NOT_ENCRYPTED) {
|
|
- throw new NotSslRecordException("not an SSL/TLS record");
|
|
- }
|
|
-
|
|
- if (packetLength - SSL_RECORD_HEADER_LENGTH > capacity) {
|
|
- // No enough space in the destination buffer so signal the caller
|
|
- // that the buffer needs to be increased.
|
|
- return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0);
|
|
- }
|
|
-
|
|
- if (len < packetLength) {
|
|
- // We either have no enough data to read the packet length at all or not enough for reading
|
|
- // the whole packet.
|
|
- return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0);
|
|
- }
|
|
-
|
|
- // 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 {
|
|
- for (; srcsOffset < srcsEndOffset; ++srcsOffset) {
|
|
- ByteBuffer src = srcs[srcsOffset];
|
|
- int remaining = src.remaining();
|
|
- if (remaining == 0) {
|
|
- // We must skip empty buffers as BIO_write will return 0 if asked to write something
|
|
- // with length 0.
|
|
- continue;
|
|
- }
|
|
- // Write more encrypted data into the BIO. Ensure we only read one packet at a time as
|
|
- // stated in the SSLEngine javadocs.
|
|
- int pendingEncryptedBytes = min(packetLength, remaining);
|
|
- ByteBuf bioWriteCopyBuf = writeEncryptedData(src, pendingEncryptedBytes);
|
|
- try {
|
|
- readLoop:
|
|
- for (; dstsOffset < dstsEndOffset; ++dstsOffset) {
|
|
- ByteBuffer dst = dsts[dstsOffset];
|
|
- if (!dst.hasRemaining()) {
|
|
- // No space left in the destination buffer, skip it.
|
|
- 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()) {
|
|
- // Move to the next dst buffer as this one is full.
|
|
- continue;
|
|
- }
|
|
- if (packetLength == 0) {
|
|
- // We read everything return now.
|
|
- return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status,
|
|
- bytesConsumed, bytesProduced);
|
|
- }
|
|
- // try to write again to the BIO. stop reading from it by break out of the readLoop.
|
|
- break;
|
|
- } 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 readLoop;
|
|
- } 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 {
|
|
- return sslReadErrorResult(SSL.getLastErrorNumber(), bytesConsumed,
|
|
- bytesProduced);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // Either we have no more dst buffers to put the data, or no more data to generate; we are done.
|
|
- if (dstsOffset >= dstsEndOffset || packetLength == 0) {
|
|
- 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 err, int bytesConsumed, int bytesProduced) throws SSLException {
|
|
- String errStr = SSL.getErrorString(err);
|
|
-
|
|
- // 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) {
|
|
- if (handshakeException == null && handshakeState != HandshakeState.FINISHED) {
|
|
- // we seems to have data left that needs to be transfered and so the user needs
|
|
- // call wrap(...). Store the error so we can pick it up later.
|
|
- handshakeException = new SSLHandshakeException(errStr);
|
|
- }
|
|
- return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced);
|
|
- }
|
|
- throw shutdownWithError("SSL_read", errStr);
|
|
- }
|
|
-
|
|
- private void closeAll() throws SSLException {
|
|
- receivedShutdown = true;
|
|
- closeOutbound();
|
|
- closeInbound();
|
|
- }
|
|
-
|
|
- private void rejectRemoteInitiatedRenegotiation() throws SSLHandshakeException {
|
|
- if (rejectRemoteInitiatedRenegotiation && SSL.getHandshakeCount(ssl) > 1) {
|
|
- // 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 Runnable getDelegatedTask() {
|
|
- // Currently, we do not delegate SSL computation tasks
|
|
- // TODO: in the future, possibly create tasks to do encrypt / decrypt async
|
|
-
|
|
- return null;
|
|
- }
|
|
-
|
|
- @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()) {
|
|
- logger.debug("SSL_shutdown failed: OpenSSL error: {}", SSL.getLastError());
|
|
- }
|
|
- // 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[OpenSsl.AVAILABLE_CIPHER_SUITES.size()]);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public final String[] getEnabledCipherSuites() {
|
|
- final String[] enabled;
|
|
- synchronized (this) {
|
|
- if (!isDestroyed()) {
|
|
- enabled = SSL.getCiphers(ssl);
|
|
- } else {
|
|
- return EmptyArrays.EMPTY_STRINGS;
|
|
- }
|
|
- }
|
|
- if (enabled == null) {
|
|
- return EmptyArrays.EMPTY_STRINGS;
|
|
- } else {
|
|
- synchronized (this) {
|
|
- for (int i = 0; i < enabled.length; i++) {
|
|
- String mapped = toJavaCipherSuite(enabled[i]);
|
|
- if (mapped != null) {
|
|
- enabled[i] = mapped;
|
|
- }
|
|
- }
|
|
- }
|
|
- return enabled;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public final void setEnabledCipherSuites(String[] cipherSuites) {
|
|
- checkNotNull(cipherSuites, "cipherSuites");
|
|
-
|
|
- final StringBuilder buf = new StringBuilder();
|
|
- for (String c: cipherSuites) {
|
|
- if (c == null) {
|
|
- break;
|
|
- }
|
|
-
|
|
- String converted = CipherSuiteConverter.toOpenSsl(c);
|
|
- if (converted == null) {
|
|
- converted = c;
|
|
- }
|
|
-
|
|
- if (!OpenSsl.isCipherSuiteAvailable(converted)) {
|
|
- throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')');
|
|
- }
|
|
-
|
|
- buf.append(converted);
|
|
- buf.append(':');
|
|
- }
|
|
-
|
|
- if (buf.length() == 0) {
|
|
- throw new IllegalArgumentException("empty cipher suites");
|
|
- }
|
|
- buf.setLength(buf.length() - 1);
|
|
-
|
|
- final String cipherSuiteSpec = buf.toString();
|
|
-
|
|
- synchronized (this) {
|
|
- if (!isDestroyed()) {
|
|
- try {
|
|
- SSL.setCipherSuites(ssl, cipherSuiteSpec);
|
|
- } 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[OpenSsl.SUPPORTED_PROTOCOLS_SET.size()]);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public final String[] getEnabledProtocols() {
|
|
- List<String> enabled = new ArrayList<String>(6);
|
|
- // Seems like there is no way to explicit disable SSLv2Hello in openssl so it is always enabled
|
|
- enabled.add(OpenSsl.PROTOCOL_SSL_V2_HELLO);
|
|
-
|
|
- int opts;
|
|
- synchronized (this) {
|
|
- if (!isDestroyed()) {
|
|
- opts = SSL.getOptions(ssl);
|
|
- } else {
|
|
- return enabled.toArray(new String[1]);
|
|
- }
|
|
- }
|
|
- if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1, OpenSsl.PROTOCOL_TLS_V1)) {
|
|
- enabled.add(OpenSsl.PROTOCOL_TLS_V1);
|
|
- }
|
|
- if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_1, OpenSsl.PROTOCOL_TLS_V1_1)) {
|
|
- enabled.add(OpenSsl.PROTOCOL_TLS_V1_1);
|
|
- }
|
|
- if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_2, OpenSsl.PROTOCOL_TLS_V1_2)) {
|
|
- enabled.add(OpenSsl.PROTOCOL_TLS_V1_2);
|
|
- }
|
|
- if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv2, OpenSsl.PROTOCOL_SSL_V2)) {
|
|
- enabled.add(OpenSsl.PROTOCOL_SSL_V2);
|
|
- }
|
|
- if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv3, OpenSsl.PROTOCOL_SSL_V3)) {
|
|
- enabled.add(OpenSsl.PROTOCOL_SSL_V3);
|
|
- }
|
|
- return enabled.toArray(new String[enabled.size()]);
|
|
- }
|
|
-
|
|
- 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);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public final void setEnabledProtocols(String[] protocols) {
|
|
- if (protocols == null) {
|
|
- // This is correct from the API docs
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- boolean sslv2 = false;
|
|
- boolean sslv3 = false;
|
|
- boolean tlsv1 = false;
|
|
- boolean tlsv1_1 = false;
|
|
- boolean tlsv1_2 = false;
|
|
- for (String p: protocols) {
|
|
- if (!OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(p)) {
|
|
- throw new IllegalArgumentException("Protocol " + p + " is not supported.");
|
|
- }
|
|
- if (p.equals(OpenSsl.PROTOCOL_SSL_V2)) {
|
|
- sslv2 = true;
|
|
- } else if (p.equals(OpenSsl.PROTOCOL_SSL_V3)) {
|
|
- sslv3 = true;
|
|
- } else if (p.equals(OpenSsl.PROTOCOL_TLS_V1)) {
|
|
- tlsv1 = true;
|
|
- } else if (p.equals(OpenSsl.PROTOCOL_TLS_V1_1)) {
|
|
- tlsv1_1 = true;
|
|
- } else if (p.equals(OpenSsl.PROTOCOL_TLS_V1_2)) {
|
|
- tlsv1_2 = true;
|
|
- }
|
|
- }
|
|
- 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);
|
|
-
|
|
- int opts = 0;
|
|
- if (!sslv2) {
|
|
- opts |= SSL.SSL_OP_NO_SSLv2;
|
|
- }
|
|
- if (!sslv3) {
|
|
- opts |= SSL.SSL_OP_NO_SSLv3;
|
|
- }
|
|
- if (!tlsv1) {
|
|
- opts |= SSL.SSL_OP_NO_TLSv1;
|
|
- }
|
|
- if (!tlsv1_1) {
|
|
- opts |= SSL.SSL_OP_NO_TLSv1_1;
|
|
- }
|
|
- if (!tlsv1_2) {
|
|
- opts |= SSL.SSL_OP_NO_TLSv1_2;
|
|
- }
|
|
-
|
|
- // 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(BEGIN_HANDSHAKE_ENGINE_CLOSED);
|
|
-
|
|
- // 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,
|
|
- // we should raise an exception.
|
|
- break;
|
|
- case STARTED_EXPLICITLY:
|
|
- // Nothing to do as the handshake is not done yet.
|
|
- break;
|
|
- case FINISHED:
|
|
- if (clientMode) {
|
|
- // Only supported for server mode at the moment.
|
|
- throw RENEGOTIATION_UNSUPPORTED;
|
|
- }
|
|
- // For renegotiate on the server side we need to issue the following command sequence with openssl:
|
|
- //
|
|
- // SSL_renegotiate(ssl)
|
|
- // SSL_do_handshake(ssl)
|
|
- // ssl->state = SSL_ST_ACCEPT
|
|
- // SSL_do_handshake(ssl)
|
|
- //
|
|
- // Because of this we fall-through to call handshake() after setting the state, as this will also take
|
|
- // care of updating the internal OpenSslSession object.
|
|
- //
|
|
- // See also:
|
|
- // https://github.com/apache/httpd/blob/2.4.16/modules/ssl/ssl_engine_kernel.c#L812
|
|
- // http://h71000.www7.hp.com/doc/83final/ba554_90007/ch04s03.html
|
|
- int status;
|
|
- if ((status = SSL.renegotiate(ssl)) != 1 || (status = SSL.doHandshake(ssl)) != 1) {
|
|
- int err = SSL.getError(ssl, status);
|
|
- if (err == SSL.SSL_ERROR_WANT_READ || err == SSL.SSL_ERROR_WANT_WRITE) {
|
|
- // If the internal SSL buffer is small it is possible that doHandshake may "fail" because
|
|
- // there is not enough room to write, so we should wait until the renegotiation has been.
|
|
- renegotiationPending = true;
|
|
- handshakeState = HandshakeState.STARTED_EXPLICITLY;
|
|
- lastAccessed = System.currentTimeMillis();
|
|
- return;
|
|
- } else {
|
|
- throw shutdownWithError("renegotiation failed");
|
|
- }
|
|
- }
|
|
-
|
|
- SSL.setState(ssl, SSL.SSL_ST_ACCEPT);
|
|
-
|
|
- lastAccessed = System.currentTimeMillis();
|
|
-
|
|
- // fall-through
|
|
- case NOT_STARTED:
|
|
- handshakeState = HandshakeState.STARTED_EXPLICITLY;
|
|
- handshake();
|
|
- break;
|
|
- default:
|
|
- throw new Error();
|
|
- }
|
|
- }
|
|
-
|
|
- private void checkEngineClosed(SSLException cause) throws SSLException {
|
|
- if (isDestroyed()) {
|
|
- throw cause;
|
|
- }
|
|
- }
|
|
-
|
|
- 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 handshake() throws SSLException {
|
|
- if (handshakeState == HandshakeState.FINISHED) {
|
|
- return FINISHED;
|
|
- }
|
|
- checkEngineClosed(HANDSHAKE_ENGINE_CLOSED);
|
|
-
|
|
- // 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
|
|
- SSLHandshakeException exception = handshakeException;
|
|
- if (exception != null) {
|
|
- 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;
|
|
- }
|
|
- // No more data left to send to the remote peer, so null out the exception field, shutdown and throw
|
|
- // the exception.
|
|
- handshakeException = null;
|
|
- shutdown();
|
|
- throw exception;
|
|
- }
|
|
-
|
|
- // Adding the OpenSslEngine to the OpenSslEngineMap so it can be used in the AbstractCertificateVerifier.
|
|
- engineMap.add(this);
|
|
- if (lastAccessed == -1) {
|
|
- lastAccessed = System.currentTimeMillis();
|
|
- }
|
|
-
|
|
- if (!certificateSet && keyMaterialManager != null) {
|
|
- certificateSet = true;
|
|
- keyMaterialManager.setKeyMaterial(this);
|
|
- }
|
|
-
|
|
- int code = SSL.doHandshake(ssl);
|
|
- if (code <= 0) {
|
|
- // Check if we have a pending exception that was created during the handshake and if so throw it after
|
|
- // shutdown the connection.
|
|
- if (handshakeException != null) {
|
|
- exception = handshakeException;
|
|
- handshakeException = null;
|
|
- shutdown();
|
|
- throw exception;
|
|
- }
|
|
-
|
|
- int sslError = SSL.getError(ssl, code);
|
|
- if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) {
|
|
- return pendingStatus(SSL.bioLengthNonApplication(networkBIO));
|
|
- } else {
|
|
- // Everything else is considered as error
|
|
- throw shutdownWithError("SSL_do_handshake");
|
|
- }
|
|
- }
|
|
- // if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished.
|
|
- session.handshakeFinished();
|
|
- engineMap.remove(ssl);
|
|
- 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
|
|
- return needPendingStatus() ? pendingStatus(SSL.bioLengthNonApplication(networkBIO)) : NOT_HANDSHAKING;
|
|
- }
|
|
-
|
|
- private SSLEngineResult.HandshakeStatus getHandshakeStatus(int pending) {
|
|
- // Check if we are in the initial handshake phase or shutdown phase
|
|
- return needPendingStatus() ? pendingStatus(pending) : 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 prefix = toJavaCipherSuitePrefix(SSL.getVersion(ssl));
|
|
- 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 <a href="https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html">SSL_set_verify</a> and
|
|
- * {@link SSL#setVerify(long, int, int)}.
|
|
- */
|
|
- @UnstableApi
|
|
- public final synchronized void setVerify(int verifyMode, int depth) {
|
|
- 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;
|
|
- }
|
|
- 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;
|
|
- }
|
|
-
|
|
- @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;
|
|
- }
|
|
-
|
|
- @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.");
|
|
- }
|
|
-
|
|
- if (version >= 8) {
|
|
- if (!isDestroyed()) {
|
|
- if (clientMode) {
|
|
- final List<String> 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();
|
|
- final boolean endPointVerificationEnabled = endPointIdentificationAlgorithm != null &&
|
|
- !endPointIdentificationAlgorithm.isEmpty();
|
|
- SSL.setHostNameValidation(ssl, DEFAULT_HOSTNAME_VALIDATION_FLAGS,
|
|
- endPointVerificationEnabled ? getPeerHost() : null);
|
|
- // 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 && endPointVerificationEnabled) {
|
|
- SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRED, -1);
|
|
- }
|
|
-
|
|
- this.endPointIdentificationAlgorithm = endPointIdentificationAlgorithm;
|
|
- algorithmConstraints = sslParameters.getAlgorithmConstraints();
|
|
- }
|
|
- super.setSSLParameters(sslParameters);
|
|
- }
|
|
-
|
|
- private boolean isDestroyed() {
|
|
- return destroyed != 0;
|
|
- }
|
|
-
|
|
- static int calculateOutNetBufSize(int pendingBytes, int numComponents) {
|
|
- return (int) min(MAX_ENCRYPTED_PACKET_LENGTH,
|
|
- pendingBytes + (long) MAX_TLS_RECORD_OVERHEAD_LENGTH * numComponents);
|
|
- }
|
|
-
|
|
- final boolean checkSniHostnameMatch(String hostname) {
|
|
- return Java8SslUtils.checkSniHostnameMatch(matchers, hostname);
|
|
- }
|
|
-
|
|
- private final class OpenSslSession implements SSLSession, ApplicationProtocolAccessor {
|
|
- 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 applicationProtocol;
|
|
- private String cipher;
|
|
- private byte[] id;
|
|
- private long creationTime;
|
|
-
|
|
- // lazy init for memory reasons
|
|
- private Map<String, Object> values;
|
|
-
|
|
- OpenSslSession(OpenSslSessionContext sessionContext) {
|
|
- this.sessionContext = sessionContext;
|
|
- }
|
|
-
|
|
- @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) {
|
|
- if (name == null) {
|
|
- throw new NullPointerException("name");
|
|
- }
|
|
- if (value == null) {
|
|
- throw new NullPointerException("value");
|
|
- }
|
|
- Map<String, Object> values = this.values;
|
|
- if (values == null) {
|
|
- // Use size of 2 to keep the memory overhead small
|
|
- values = this.values = new HashMap<String, Object>(2);
|
|
- }
|
|
- Object old = values.put(name, value);
|
|
- if (value instanceof SSLSessionBindingListener) {
|
|
- ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
|
|
- }
|
|
- notifyUnbound(old, name);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Object getValue(String name) {
|
|
- if (name == null) {
|
|
- throw new NullPointerException("name");
|
|
- }
|
|
- if (values == null) {
|
|
- return null;
|
|
- }
|
|
- return values.get(name);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void removeValue(String name) {
|
|
- if (name == null) {
|
|
- throw new NullPointerException("name");
|
|
- }
|
|
- Map<String, Object> values = this.values;
|
|
- if (values == null) {
|
|
- return;
|
|
- }
|
|
- Object old = values.remove(name);
|
|
- notifyUnbound(old, name);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String[] getValueNames() {
|
|
- Map<String, Object> values = this.values;
|
|
- if (values == null || values.isEmpty()) {
|
|
- return EmptyArrays.EMPTY_STRINGS;
|
|
- }
|
|
- return values.keySet().toArray(new String[values.size()]);
|
|
- }
|
|
-
|
|
- private void notifyUnbound(Object value, String name) {
|
|
- if (value instanceof SSLSessionBindingListener) {
|
|
- ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Finish the handshake and so init everything in the {@link OpenSslSession} that should be accessible by
|
|
- * the user.
|
|
- */
|
|
- void handshakeFinished() throws SSLException {
|
|
- synchronized (ReferenceCountedOpenSslEngine.this) {
|
|
- if (!isDestroyed()) {
|
|
- id = SSL.getSessionId(ssl);
|
|
- cipher = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
|
|
- protocol = SSL.getVersion(ssl);
|
|
-
|
|
- initPeerCerts();
|
|
- selectApplicationProtocol();
|
|
-
|
|
- 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 = EMPTY_CERTIFICATES;
|
|
- x509PeerCerts = 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 = EMPTY_CERTIFICATES;
|
|
- x509PeerCerts = 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<String> 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) {
|
|
- this.applicationProtocol = selectApplicationProtocol(
|
|
- protocols, behavior, applicationProtocol);
|
|
- }
|
|
- break;
|
|
- case NPN:
|
|
- applicationProtocol = SSL.getNextProtoNegotiated(ssl);
|
|
- if (applicationProtocol != null) {
|
|
- 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) {
|
|
- this.applicationProtocol = selectApplicationProtocol(
|
|
- protocols, behavior, applicationProtocol);
|
|
- }
|
|
- break;
|
|
- default:
|
|
- throw new Error();
|
|
- }
|
|
- }
|
|
-
|
|
- private String selectApplicationProtocol(List<String> 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() {
|
|
- 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 = localCerts;
|
|
- 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 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 getApplicationProtocol() {
|
|
- synchronized (ReferenceCountedOpenSslEngine.this) {
|
|
- return applicationProtocol;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String getPeerHost() {
|
|
- return ReferenceCountedOpenSslEngine.this.getPeerHost();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getPeerPort() {
|
|
- return ReferenceCountedOpenSslEngine.this.getPeerPort();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getPacketBufferSize() {
|
|
- return MAX_ENCRYPTED_PACKET_LENGTH;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getApplicationBufferSize() {
|
|
- return MAX_PLAINTEXT_LENGTH;
|
|
- }
|
|
- }
|
|
-}
|
|
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 4c9df31..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java
|
|
+++ /dev/null
|
|
@@ -1,239 +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.SSL;
|
|
-import io.netty.internal.tcnative.SSLContext;
|
|
-import io.netty.internal.tcnative.SniHostNameMatcher;
|
|
-import io.netty.util.internal.PlatformDependent;
|
|
-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.X509ExtendedKeyManager;
|
|
-import javax.net.ssl.X509ExtendedTrustManager;
|
|
-import javax.net.ssl.X509KeyManager;
|
|
-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.
|
|
- * <p>Instances of this class must be {@link #release() released} or else native memory will leak!
|
|
- *
|
|
- * <p>Instances of this class <strong>must not</strong> 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 final OpenSslKeyMaterialManager keyMaterialManager;
|
|
-
|
|
- ReferenceCountedOpenSslServerContext(
|
|
- X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
|
|
- Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
|
- long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
|
|
- boolean enableOcsp) throws SSLException {
|
|
- this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
|
|
- cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
|
|
- enableOcsp);
|
|
- }
|
|
-
|
|
- private ReferenceCountedOpenSslServerContext(
|
|
- X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
|
|
- Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
|
|
- long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
|
|
- boolean enableOcsp) 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 {
|
|
- ServerContext context = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
|
|
- keyCertChain, key, keyPassword, keyManagerFactory);
|
|
- sessionContext = context.sessionContext;
|
|
- keyMaterialManager = context.keyMaterialManager;
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- release();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public OpenSslServerSessionContext sessionContext() {
|
|
- return sessionContext;
|
|
- }
|
|
-
|
|
- @Override
|
|
- OpenSslKeyMaterialManager keyMaterialManager() {
|
|
- return keyMaterialManager;
|
|
- }
|
|
-
|
|
- static final class ServerContext {
|
|
- OpenSslServerSessionContext sessionContext;
|
|
- OpenSslKeyMaterialManager keyMaterialManager;
|
|
- }
|
|
-
|
|
- static ServerContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, OpenSslEngineMap engineMap,
|
|
- X509Certificate[] trustCertCollection,
|
|
- TrustManagerFactory trustManagerFactory,
|
|
- X509Certificate[] keyCertChain, PrivateKey key,
|
|
- String keyPassword, KeyManagerFactory keyManagerFactory)
|
|
- throws SSLException {
|
|
- ServerContext result = new ServerContext();
|
|
- 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) {
|
|
- keyManagerFactory = buildKeyManagerFactory(
|
|
- keyCertChain, key, keyPassword, keyManagerFactory);
|
|
- }
|
|
- X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers());
|
|
- result.keyMaterialManager = useExtendedKeyManager(keyManager) ?
|
|
- new OpenSslExtendedKeyMaterialManager(
|
|
- (X509ExtendedKeyManager) keyManager, keyPassword) :
|
|
- new OpenSslKeyMaterialManager(keyManager, keyPassword);
|
|
- }
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to set certificate and key", e);
|
|
- }
|
|
- try {
|
|
- if (trustCertCollection != null) {
|
|
- trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
|
|
- } 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
|
|
-
|
|
- // 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));
|
|
- }
|
|
-
|
|
- X509Certificate[] issuers = manager.getAcceptedIssuers();
|
|
- if (issuers != null && issuers.length > 0) {
|
|
- long bio = 0;
|
|
- try {
|
|
- bio = toBIO(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);
|
|
- }
|
|
-
|
|
- result.sessionContext = new OpenSslServerSessionContext(thiz);
|
|
- result.sessionContext.setSessionIdContext(ID);
|
|
- return result;
|
|
- }
|
|
-
|
|
- 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);
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier {
|
|
- private final X509ExtendedTrustManager manager;
|
|
-
|
|
- ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) {
|
|
- super(engineMap);
|
|
- this.manager = 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) {
|
|
- return engine.checkSniHostnameMatch(hostname);
|
|
- }
|
|
- 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 4998d0d..8dbc3cf 100644
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java
|
|
+++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java
|
|
@@ -115,11 +115,7 @@ public abstract class SslContext {
|
|
}
|
|
|
|
private static SslProvider defaultProvider() {
|
|
- if (OpenSsl.isAvailable()) {
|
|
- return SslProvider.OPENSSL;
|
|
- } else {
|
|
- return SslProvider.JDK;
|
|
- }
|
|
+ return SslProvider.JDK;
|
|
}
|
|
|
|
/**
|
|
@@ -416,18 +412,6 @@ public abstract class SslContext {
|
|
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
|
|
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
|
|
clientAuth, protocols, startTls);
|
|
- case OPENSSL:
|
|
- verifyNullSslContextProvider(provider, sslContextProvider);
|
|
- return new OpenSslServerContext(
|
|
- trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
|
|
- keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
|
|
- clientAuth, protocols, startTls, enableOcsp);
|
|
- case OPENSSL_REFCNT:
|
|
- verifyNullSslContextProvider(provider, sslContextProvider);
|
|
- return new ReferenceCountedOpenSslServerContext(
|
|
- trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
|
|
- keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
|
|
- clientAuth, protocols, startTls, enableOcsp);
|
|
default:
|
|
throw new Error(provider.toString());
|
|
}
|
|
@@ -770,18 +754,6 @@ public abstract class SslContext {
|
|
return new JdkSslClientContext(sslContextProvider,
|
|
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
|
|
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout);
|
|
- case OPENSSL:
|
|
- verifyNullSslContextProvider(provider, sslContextProvider);
|
|
- return new OpenSslClientContext(
|
|
- trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
|
|
- keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
|
|
- enableOcsp);
|
|
- case OPENSSL_REFCNT:
|
|
- verifyNullSslContextProvider(provider, sslContextProvider);
|
|
- return new ReferenceCountedOpenSslClientContext(
|
|
- trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
|
|
- keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
|
|
- enableOcsp);
|
|
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 c054964..05c451a 100644
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
|
|
+++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
|
|
@@ -159,6 +159,12 @@ import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength;
|
|
* <a href="https://github.com/netty/netty/issues/832">#832</a> in our issue tracker.
|
|
*/
|
|
public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundHandler {
|
|
+ private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
|
|
+ private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024;
|
|
+ private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
|
|
+ // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
|
|
+ static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
|
|
+ static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
|
|
|
|
private static final InternalLogger logger =
|
|
InternalLoggerFactory.getInstance(SslHandler.class);
|
|
@@ -181,40 +187,6 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
new ClosedChannelException(), SslHandler.class, "channelInactive(...)");
|
|
|
|
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
|
|
- int calculateWrapBufferCapacity(SslHandler handler, int pendingBytes, int numComponents) {
|
|
- return ReferenceCountedOpenSslEngine.calculateOutNetBufSize(pendingBytes, numComponents);
|
|
- }
|
|
- },
|
|
CONSCRYPT(true, COMPOSITE_CUMULATOR) {
|
|
@Override
|
|
SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int readerIndex, int len, ByteBuf out)
|
|
@@ -265,9 +237,6 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
};
|
|
|
|
static SslEngineType forEngine(SSLEngine engine) {
|
|
- if (engine instanceof ReferenceCountedOpenSslEngine) {
|
|
- return TCNATIVE;
|
|
- }
|
|
if (engine instanceof ConscryptAlpnSslEngine) {
|
|
return CONSCRYPT;
|
|
}
|
|
@@ -1034,7 +1003,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
|
|
boolean nonSslRecord = false;
|
|
|
|
- while (totalLength < ReferenceCountedOpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
+ while (totalLength < MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
final int readableBytes = endOffset - offset;
|
|
if (readableBytes < SslUtils.SSL_RECORD_HEADER_LENGTH) {
|
|
break;
|
|
@@ -1055,7 +1024,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
}
|
|
|
|
int newTotalLength = totalLength + packetLength;
|
|
- if (newTotalLength > ReferenceCountedOpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
+ if (newTotalLength > MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
// Don't read too much.
|
|
break;
|
|
}
|
|
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 aff0949..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java
|
|
+++ /dev/null
|
|
@@ -1,65 +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.ThrowableUtil;
|
|
-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 static final SSLHandshakeException OCSP_VERIFICATION_EXCEPTION = ThrowableUtil.unknownStackTrace(
|
|
- new SSLHandshakeException("Bad OCSP response"), OcspClientHandler.class, "verify(...)");
|
|
-
|
|
- 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 OCSP_VERIFICATION_EXCEPTION;
|
|
- }
|
|
- }
|
|
-
|
|
- 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 2883ff4..0000000
|
|
--- 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.
|
|
- */
|
|
-
|
|
-/**
|
|
- * <a href="https://en.wikipedia.org/wiki/OCSP_stapling">OCSP stapling</a>,
|
|
- * 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/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java
|
|
deleted file mode 100644
|
|
index d696d6b..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java
|
|
+++ /dev/null
|
|
@@ -1,108 +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 org.junit.BeforeClass;
|
|
-import org.junit.Test;
|
|
-import org.junit.runner.RunWith;
|
|
-import org.junit.runners.Parameterized;
|
|
-
|
|
-import java.util.ArrayList;
|
|
-import java.util.Collection;
|
|
-import java.util.List;
|
|
-
|
|
-import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory;
|
|
-import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-@RunWith(Parameterized.class)
|
|
-public class JdkOpenSslEngineInteroptTest extends SSLEngineTest {
|
|
-
|
|
- @Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
|
- public static Collection<Object> data() {
|
|
- List<Object> params = new ArrayList<Object>();
|
|
- for (BufferType type: BufferType.values()) {
|
|
- params.add(type);
|
|
- }
|
|
- return params;
|
|
- }
|
|
-
|
|
- public JdkOpenSslEngineInteroptTest(BufferType type) {
|
|
- super(type);
|
|
- }
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOpenSsl() {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslClientProvider() {
|
|
- return SslProvider.JDK;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslServerProvider() {
|
|
- return SslProvider.OPENSSL;
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) {
|
|
- ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) handler.engine();
|
|
- engine.setVerify(SSL_CVERIFY_IGNORED, 1);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) {
|
|
- // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException.
|
|
- return super.mySetupMutualAuthServerIsValidClientException(cause) || causedBySSLException(cause);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java
|
|
deleted file mode 100644
|
|
index 229e853..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java
|
|
+++ /dev/null
|
|
@@ -1,49 +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;
|
|
-
|
|
-import io.netty.internal.tcnative.CertificateVerifier;
|
|
-import org.junit.Assert;
|
|
-import org.junit.Assume;
|
|
-import org.junit.BeforeClass;
|
|
-import org.junit.Test;
|
|
-
|
|
-import java.lang.reflect.Field;
|
|
-
|
|
-public class OpenSslCertificateExceptionTest {
|
|
-
|
|
- @BeforeClass
|
|
- public static void assumeOpenSsl() {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testValidErrorCode() throws Exception {
|
|
- Field[] fields = CertificateVerifier.class.getFields();
|
|
- for (Field field : fields) {
|
|
- if (field.isAccessible()) {
|
|
- int errorCode = field.getInt(null);
|
|
- OpenSslCertificateException exception = new OpenSslCertificateException(errorCode);
|
|
- Assert.assertEquals(errorCode, exception.errorCode());
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalArgumentException.class)
|
|
- public void testNonValidErrorCode() {
|
|
- new OpenSslCertificateException(Integer.MIN_VALUE);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java
|
|
deleted file mode 100644
|
|
index 6011cf7..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java
|
|
+++ /dev/null
|
|
@@ -1,38 +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.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import org.junit.BeforeClass;
|
|
-
|
|
-import javax.net.ssl.SSLException;
|
|
-import java.io.File;
|
|
-
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-public class OpenSslClientContextTest extends SslContextTest {
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOpenSsl() {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException {
|
|
- return new OpenSslClientContext(crtFile, InsecureTrustManagerFactory.INSTANCE, crtFile, keyFile, pass,
|
|
- null, null, IdentityCipherSuiteFilter.INSTANCE, ApplicationProtocolConfig.DISABLED, 0, 0);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java
|
|
deleted file mode 100644
|
|
index 5939b66..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java
|
|
+++ /dev/null
|
|
@@ -1,661 +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.buffer.UnpooledByteBufAllocator;
|
|
-import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
|
-import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
|
-import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
|
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.internal.PlatformDependent;
|
|
-import org.junit.Assume;
|
|
-import org.junit.BeforeClass;
|
|
-import org.junit.Test;
|
|
-import org.junit.runner.RunWith;
|
|
-import org.junit.runners.Parameterized;
|
|
-
|
|
-import java.nio.ByteBuffer;
|
|
-import java.security.AlgorithmConstraints;
|
|
-import java.security.AlgorithmParameters;
|
|
-import java.security.CryptoPrimitive;
|
|
-import java.security.Key;
|
|
-import java.util.ArrayList;
|
|
-import java.util.Collection;
|
|
-import java.util.List;
|
|
-import java.util.Set;
|
|
-import javax.net.ssl.SSLEngine;
|
|
-import javax.net.ssl.SSLEngineResult;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.SSLParameters;
|
|
-
|
|
-import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory;
|
|
-import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH;
|
|
-import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH;
|
|
-import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_PLAINTEXT_LENGTH;
|
|
-import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED;
|
|
-import static java.lang.Integer.MAX_VALUE;
|
|
-import static org.junit.Assert.assertEquals;
|
|
-import static org.junit.Assert.assertFalse;
|
|
-import static org.junit.Assert.assertNull;
|
|
-import static org.junit.Assert.assertSame;
|
|
-import static org.junit.Assert.assertTrue;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-@RunWith(Parameterized.class)
|
|
-public class OpenSslEngineTest extends SSLEngineTest {
|
|
- private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2";
|
|
- private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1";
|
|
-
|
|
- @Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
|
- public static Collection<Object> data() {
|
|
- List<Object> params = new ArrayList<Object>();
|
|
- for (BufferType type: BufferType.values()) {
|
|
- params.add(type);
|
|
- }
|
|
- return params;
|
|
- }
|
|
-
|
|
- public OpenSslEngineTest(BufferType type) {
|
|
- super(type);
|
|
- }
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOpenSsl() {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testClientHostnameValidationSuccess() throws InterruptedException, SSLException {
|
|
- assumeTrue(OpenSsl.supportsHostnameValidation());
|
|
- super.testClientHostnameValidationSuccess();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testClientHostnameValidationFail() throws InterruptedException, SSLException {
|
|
- assumeTrue(OpenSsl.supportsHostnameValidation());
|
|
- super.testClientHostnameValidationFail();
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testNpn() throws Exception {
|
|
- ApplicationProtocolConfig apn = acceptingNegotiator(Protocol.NPN,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- setupHandlers(apn);
|
|
- runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpn() throws Exception {
|
|
- assumeTrue(OpenSsl.isAlpnSupported());
|
|
- ApplicationProtocolConfig apn = acceptingNegotiator(Protocol.ALPN,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- setupHandlers(apn);
|
|
- runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpnCompatibleProtocolsDifferentClientOrder() throws Exception {
|
|
- assumeTrue(OpenSsl.isAlpnSupported());
|
|
- ApplicationProtocolConfig clientApn = acceptingNegotiator(Protocol.ALPN,
|
|
- FALLBACK_APPLICATION_LEVEL_PROTOCOL, PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- ApplicationProtocolConfig serverApn = acceptingNegotiator(Protocol.ALPN,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL, FALLBACK_APPLICATION_LEVEL_PROTOCOL);
|
|
- setupHandlers(serverApn, clientApn);
|
|
- assertNull(serverException);
|
|
- runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testEnablingAnAlreadyDisabledSslProtocol() throws Exception {
|
|
- testEnablingAnAlreadyDisabledSslProtocol(new String[]{PROTOCOL_SSL_V2_HELLO},
|
|
- new String[]{PROTOCOL_SSL_V2_HELLO, PROTOCOL_TLS_V1_2});
|
|
- }
|
|
- @Test
|
|
- public void testWrapBuffersNoWritePendingError() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
- SSLEngine clientEngine = null;
|
|
- SSLEngine serverEngine = null;
|
|
- try {
|
|
- clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- handshake(clientEngine, serverEngine);
|
|
-
|
|
- ByteBuffer src = allocateBuffer(1024 * 10);
|
|
- byte[] data = new byte[src.capacity()];
|
|
- PlatformDependent.threadLocalRandom().nextBytes(data);
|
|
- src.put(data).flip();
|
|
- ByteBuffer dst = allocateBuffer(1);
|
|
- // Try to wrap multiple times so we are more likely to hit the issue.
|
|
- for (int i = 0; i < 100; i++) {
|
|
- src.position(0);
|
|
- dst.position(0);
|
|
- assertSame(SSLEngineResult.Status.BUFFER_OVERFLOW, clientEngine.wrap(src, dst).getStatus());
|
|
- }
|
|
- } finally {
|
|
- cleanupClientSslEngine(clientEngine);
|
|
- cleanupServerSslEngine(serverEngine);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testOnlySmallBufferNeededForWrap() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
- SSLEngine clientEngine = null;
|
|
- SSLEngine serverEngine = null;
|
|
- try {
|
|
- clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- handshake(clientEngine, serverEngine);
|
|
-
|
|
- // Allocate a buffer which is small enough and set the limit to the capacity to mark its whole content
|
|
- // as readable.
|
|
- int srcLen = 1024;
|
|
- ByteBuffer src = allocateBuffer(srcLen);
|
|
-
|
|
- ByteBuffer dstTooSmall = allocateBuffer(
|
|
- src.capacity() + MAX_TLS_RECORD_OVERHEAD_LENGTH - 1);
|
|
- ByteBuffer dst = allocateBuffer(
|
|
- src.capacity() + MAX_TLS_RECORD_OVERHEAD_LENGTH);
|
|
-
|
|
- // Check that we fail to wrap if the dst buffers capacity is not at least
|
|
- // src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH
|
|
- SSLEngineResult result = clientEngine.wrap(src, dstTooSmall);
|
|
- assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
|
- assertEquals(0, result.bytesConsumed());
|
|
- assertEquals(0, result.bytesProduced());
|
|
- assertEquals(src.remaining(), src.capacity());
|
|
- assertEquals(dst.remaining(), dst.capacity());
|
|
-
|
|
- // Check that we can wrap with a dst buffer that has the capacity of
|
|
- // src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH
|
|
- result = clientEngine.wrap(src, dst);
|
|
- assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
|
- assertEquals(srcLen, result.bytesConsumed());
|
|
- assertEquals(0, src.remaining());
|
|
- assertTrue(result.bytesProduced() > srcLen);
|
|
- assertEquals(src.capacity() - result.bytesConsumed(), src.remaining());
|
|
- assertEquals(dst.capacity() - result.bytesProduced(), dst.remaining());
|
|
- } finally {
|
|
- cleanupClientSslEngine(clientEngine);
|
|
- cleanupServerSslEngine(serverEngine);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testNeededDstCapacityIsCorrectlyCalculated() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
- SSLEngine clientEngine = null;
|
|
- SSLEngine serverEngine = null;
|
|
- try {
|
|
- clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- handshake(clientEngine, serverEngine);
|
|
-
|
|
- ByteBuffer src = allocateBuffer(1024);
|
|
- ByteBuffer src2 = src.duplicate();
|
|
-
|
|
- ByteBuffer dst = allocateBuffer(src.capacity()
|
|
- + MAX_TLS_RECORD_OVERHEAD_LENGTH);
|
|
-
|
|
- SSLEngineResult result = clientEngine.wrap(new ByteBuffer[] { src, src2 }, dst);
|
|
- assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
|
- assertEquals(0, src.position());
|
|
- assertEquals(0, src2.position());
|
|
- assertEquals(0, dst.position());
|
|
- assertEquals(0, result.bytesConsumed());
|
|
- assertEquals(0, result.bytesProduced());
|
|
- } finally {
|
|
- cleanupClientSslEngine(clientEngine);
|
|
- cleanupServerSslEngine(serverEngine);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testSrcsLenOverFlowCorrectlyHandled() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
- SSLEngine clientEngine = null;
|
|
- SSLEngine serverEngine = null;
|
|
- try {
|
|
- clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- handshake(clientEngine, serverEngine);
|
|
-
|
|
- ByteBuffer src = allocateBuffer(1024);
|
|
- List<ByteBuffer> srcList = new ArrayList<ByteBuffer>();
|
|
- long srcsLen = 0;
|
|
- long maxLen = ((long) MAX_VALUE) * 2;
|
|
-
|
|
- while (srcsLen < maxLen) {
|
|
- ByteBuffer dup = src.duplicate();
|
|
- srcList.add(dup);
|
|
- srcsLen += dup.capacity();
|
|
- }
|
|
-
|
|
- ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[srcList.size()]);
|
|
-
|
|
- ByteBuffer dst = allocateBuffer(MAX_ENCRYPTED_PACKET_LENGTH - 1);
|
|
-
|
|
- SSLEngineResult result = clientEngine.wrap(srcs, dst);
|
|
- assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus());
|
|
-
|
|
- for (ByteBuffer buffer : srcs) {
|
|
- assertEquals(0, buffer.position());
|
|
- }
|
|
- assertEquals(0, dst.position());
|
|
- assertEquals(0, result.bytesConsumed());
|
|
- assertEquals(0, result.bytesProduced());
|
|
- } finally {
|
|
- cleanupClientSslEngine(clientEngine);
|
|
- cleanupServerSslEngine(serverEngine);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testCalculateOutNetBufSizeOverflow() {
|
|
- assertEquals(MAX_ENCRYPTED_PACKET_LENGTH,
|
|
- ReferenceCountedOpenSslEngine.calculateOutNetBufSize(MAX_VALUE, 1));
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testCalculateOutNetBufSize0() {
|
|
- assertEquals(MAX_TLS_RECORD_OVERHEAD_LENGTH,
|
|
- ReferenceCountedOpenSslEngine.calculateOutNetBufSize(0, 1));
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testCalculateOutNetBufSizeMaxEncryptedPacketLength() {
|
|
- assertEquals(MAX_ENCRYPTED_PACKET_LENGTH,
|
|
- ReferenceCountedOpenSslEngine.calculateOutNetBufSize(MAX_ENCRYPTED_PACKET_LENGTH + 1, 2));
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) {
|
|
- ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) handler.engine();
|
|
- engine.setVerify(SSL_CVERIFY_IGNORED, 1);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testWrapWithDifferentSizesTLSv1() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
-
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ECDHE-RSA-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "AECDH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "AECDH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "DHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "EDH-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "IDEA-CBC-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "DHE-RSA-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "AECDH-RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "DHE-RSA-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "AECDH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ECDHE-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ADH-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "DHE-RSA-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ECDHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "DHE-RSA-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1, "ECDHE-RSA-RC4-SHA");
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testWrapWithDifferentSizesTLSv1_1() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
-
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "DHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "DHE-RSA-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "AECDH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "DHE-RSA-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ECDHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "IDEA-CBC-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "AECDH-RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ECDHE-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "EDH-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "AECDH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "ADH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_1, "DES-CBC3-SHA");
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testWrapWithDifferentSizesTLSv1_2() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
-
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-AES256-GCM-SHA384");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AECDH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AES128-GCM-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-AES128-GCM-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES256-SHA384");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AECDH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AES256-GCM-SHA384");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AES256-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES128-GCM-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES128-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-AES128-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "EDH-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "IDEA-CBC-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-AES128-GCM-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AES128-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AECDH-RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-AES256-GCM-SHA384");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-AES256-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "AECDH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES256-GCM-SHA384");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-AES256-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ADH-AES128-SHA256");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "DHE-RSA-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_TLS_V1_2, "ECDHE-RSA-RC4-SHA");
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testWrapWithDifferentSizesSSLv3() throws Exception {
|
|
- clientSslCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider())
|
|
- .build();
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
-
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "AECDH-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "AECDH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "DHE-RSA-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "EDH-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-RC4-MD5");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "IDEA-CBC-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "DHE-RSA-AES128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "AECDH-RC4-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "DHE-RSA-SEED-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "AECDH-AES256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ECDHE-RSA-DES-CBC3-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ADH-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "DHE-RSA-CAMELLIA256-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "DHE-RSA-CAMELLIA128-SHA");
|
|
- testWrapWithDifferentSizes(OpenSsl.PROTOCOL_SSL_V3, "ECDHE-RSA-RC4-SHA");
|
|
- }
|
|
-
|
|
- private void testWrapWithDifferentSizes(String protocol, String cipher) throws Exception {
|
|
- assumeTrue(OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocol));
|
|
- if (!OpenSsl.isCipherSuiteAvailable(cipher)) {
|
|
- return;
|
|
- }
|
|
-
|
|
- SSLEngine clientEngine = null;
|
|
- SSLEngine serverEngine = null;
|
|
- try {
|
|
- clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- serverEngine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- clientEngine.setEnabledCipherSuites(new String[] { cipher });
|
|
- clientEngine.setEnabledProtocols(new String[] { protocol });
|
|
- serverEngine.setEnabledCipherSuites(new String[] { cipher });
|
|
- serverEngine.setEnabledProtocols(new String[] { protocol });
|
|
-
|
|
- try {
|
|
- handshake(clientEngine, serverEngine);
|
|
- } catch (SSLException e) {
|
|
- if (e.getMessage().contains("unsupported protocol")) {
|
|
- Assume.assumeNoException(protocol + " not supported with cipher " + cipher, e);
|
|
- }
|
|
- throw e;
|
|
- }
|
|
-
|
|
- int srcLen = 64;
|
|
- do {
|
|
- testWrapDstBigEnough(clientEngine, srcLen);
|
|
- srcLen += 64;
|
|
- } while (srcLen < MAX_PLAINTEXT_LENGTH);
|
|
-
|
|
- testWrapDstBigEnough(clientEngine, MAX_PLAINTEXT_LENGTH);
|
|
- } finally {
|
|
- cleanupClientSslEngine(clientEngine);
|
|
- cleanupServerSslEngine(serverEngine);
|
|
- }
|
|
- }
|
|
-
|
|
- private void testWrapDstBigEnough(SSLEngine engine, int srcLen) throws SSLException {
|
|
- ByteBuffer src = allocateBuffer(srcLen);
|
|
- ByteBuffer dst = allocateBuffer(srcLen + MAX_TLS_RECORD_OVERHEAD_LENGTH);
|
|
-
|
|
- SSLEngineResult result = engine.wrap(src, dst);
|
|
- assertEquals(SSLEngineResult.Status.OK, result.getStatus());
|
|
- int consumed = result.bytesConsumed();
|
|
- int produced = result.bytesProduced();
|
|
- assertEquals(srcLen, consumed);
|
|
- assertTrue(produced > consumed);
|
|
-
|
|
- dst.flip();
|
|
- assertEquals(produced, dst.remaining());
|
|
- assertFalse(src.hasRemaining());
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testSNIMatchersDoesNotThrow() throws Exception {
|
|
- assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
-
|
|
- SSLEngine engine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- try {
|
|
- SSLParameters parameters = new SSLParameters();
|
|
- Java8SslTestUtils.setSNIMatcher(parameters);
|
|
- engine.setSSLParameters(parameters);
|
|
- } finally {
|
|
- cleanupServerSslEngine(engine);
|
|
- ssc.delete();
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalArgumentException.class)
|
|
- public void testAlgorithmConstraintsThrows() throws Exception {
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslServerProvider())
|
|
- .build();
|
|
-
|
|
- SSLEngine engine = serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- try {
|
|
- SSLParameters parameters = new SSLParameters();
|
|
- parameters.setAlgorithmConstraints(new AlgorithmConstraints() {
|
|
- @Override
|
|
- public boolean permits(
|
|
- Set<CryptoPrimitive> primitives, String algorithm, AlgorithmParameters parameters) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean permits(
|
|
- Set<CryptoPrimitive> primitives, String algorithm, Key key, AlgorithmParameters parameters) {
|
|
- return false;
|
|
- }
|
|
- });
|
|
- engine.setSSLParameters(parameters);
|
|
- } finally {
|
|
- cleanupServerSslEngine(engine);
|
|
- ssc.delete();
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslClientProvider() {
|
|
- return SslProvider.OPENSSL;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslServerProvider() {
|
|
- return SslProvider.OPENSSL;
|
|
- }
|
|
-
|
|
- private static ApplicationProtocolConfig acceptingNegotiator(Protocol protocol,
|
|
- String... supportedProtocols) {
|
|
- return new ApplicationProtocolConfig(protocol,
|
|
- SelectorFailureBehavior.NO_ADVERTISE,
|
|
- SelectedListenerFailureBehavior.ACCEPT,
|
|
- supportedProtocols);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java
|
|
deleted file mode 100644
|
|
index f63a16f..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java
|
|
+++ /dev/null
|
|
@@ -1,114 +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 org.junit.BeforeClass;
|
|
-import org.junit.Ignore;
|
|
-import org.junit.Test;
|
|
-
|
|
-import javax.net.ssl.SSLException;
|
|
-import org.junit.runner.RunWith;
|
|
-import org.junit.runners.Parameterized;
|
|
-
|
|
-import java.util.ArrayList;
|
|
-import java.util.Collection;
|
|
-import java.util.List;
|
|
-
|
|
-import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-@RunWith(Parameterized.class)
|
|
-public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest {
|
|
-
|
|
- @Parameterized.Parameters(name = "{index}: bufferType = {0}")
|
|
- public static Collection<Object> data() {
|
|
- List<Object> params = new ArrayList<Object>();
|
|
- for (BufferType type: BufferType.values()) {
|
|
- params.add(type);
|
|
- }
|
|
- return params;
|
|
- }
|
|
-
|
|
- public OpenSslJdkSslEngineInteroptTest(BufferType type) {
|
|
- super(type);
|
|
- }
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOpenSsl() {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslClientProvider() {
|
|
- return SslProvider.OPENSSL;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslServerProvider() {
|
|
- return SslProvider.JDK;
|
|
- }
|
|
-
|
|
- @Ignore /* Does the JDK support a "max certificate chain length"? */
|
|
- @Override
|
|
- public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth() throws Exception {
|
|
- }
|
|
-
|
|
- @Ignore /* Does the JDK support a "max certificate chain length"? */
|
|
- @Override
|
|
- public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth() throws Exception {
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth() throws Exception {
|
|
- checkShouldUseKeyManagerFactory();
|
|
- super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testClientHostnameValidationSuccess() throws InterruptedException, SSLException {
|
|
- assumeTrue(OpenSsl.supportsHostnameValidation());
|
|
- super.testClientHostnameValidationSuccess();
|
|
- }
|
|
-
|
|
- @Override
|
|
- @Test
|
|
- public void testClientHostnameValidationFail() throws InterruptedException, SSLException {
|
|
- assumeTrue(OpenSsl.supportsHostnameValidation());
|
|
- super.testClientHostnameValidationFail();
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) {
|
|
- // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException.
|
|
- return super.mySetupMutualAuthServerIsValidServerException(cause) || causedBySSLException(cause);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateSmallBIOTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateSmallBIOTest.java
|
|
deleted file mode 100644
|
|
index 3959e64..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateSmallBIOTest.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.
|
|
- */
|
|
-package io.netty.handler.ssl;
|
|
-
|
|
-public class OpenSslRenegotiateSmallBIOTest extends OpenSslRenegotiateTest {
|
|
- @Override
|
|
- protected void initSslServerContext(SslContext context) {
|
|
- ((ReferenceCountedOpenSslContext) context).setBioNonApplicationBufferSize(1);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java
|
|
deleted file mode 100644
|
|
index 8f3dfee..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java
|
|
+++ /dev/null
|
|
@@ -1,36 +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 org.junit.BeforeClass;
|
|
-
|
|
-import static org.junit.Assume.assumeFalse;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-public class OpenSslRenegotiateTest extends RenegotiateTest {
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOpenSsl() {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- // BoringSSL does not support renegotiation intentionally.
|
|
- assumeFalse("BoringSSL".equals(OpenSsl.versionString()));
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider serverSslProvider() {
|
|
- return SslProvider.OPENSSL;
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java
|
|
deleted file mode 100644
|
|
index f22d045..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java
|
|
+++ /dev/null
|
|
@@ -1,39 +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 org.junit.Assume;
|
|
-import org.junit.BeforeClass;
|
|
-
|
|
-import javax.net.ssl.SSLException;
|
|
-import java.io.File;
|
|
-
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-public class OpenSslServerContextTest extends SslContextTest {
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOpenSsl() {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- return new OpenSslServerContext(crtFile, keyFile, pass);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java
|
|
deleted file mode 100644
|
|
index 7882a61..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java
|
|
+++ /dev/null
|
|
@@ -1,27 +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;
|
|
-
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-final class OpenSslTestUtils {
|
|
- private OpenSslTestUtils() {
|
|
- }
|
|
-
|
|
- static void checkShouldUseKeyManagerFactory() {
|
|
- assumeTrue(OpenSsl.supportsKeyManagerFactory() && OpenSsl.useKeyManagerFactory());
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java b/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java
|
|
deleted file mode 100644
|
|
index 793f772..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java
|
|
+++ /dev/null
|
|
@@ -1,95 +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 static org.junit.Assert.assertEquals;
|
|
-import static org.junit.Assert.assertTrue;
|
|
-import static org.junit.Assume.assumeFalse;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-import java.io.ByteArrayOutputStream;
|
|
-import java.io.File;
|
|
-import java.io.FileInputStream;
|
|
-
|
|
-import org.junit.Test;
|
|
-
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.ReferenceCountUtil;
|
|
-
|
|
-public class PemEncodedTest {
|
|
-
|
|
- @Test
|
|
- public void testPemEncodedOpenSsl() throws Exception {
|
|
- testPemEncoded(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testPemEncodedOpenSslRef() throws Exception {
|
|
- testPemEncoded(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- private static void testPemEncoded(SslProvider provider) throws Exception {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- assumeFalse(OpenSsl.useKeyManagerFactory());
|
|
- PemPrivateKey pemKey;
|
|
- PemX509Certificate pemCert;
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- try {
|
|
- pemKey = PemPrivateKey.valueOf(toByteArray(ssc.privateKey()));
|
|
- pemCert = PemX509Certificate.valueOf(toByteArray(ssc.certificate()));
|
|
- } finally {
|
|
- ssc.delete();
|
|
- }
|
|
-
|
|
- SslContext context = SslContextBuilder.forServer(pemKey, pemCert)
|
|
- .sslProvider(provider)
|
|
- .build();
|
|
- assertEquals(1, pemKey.refCnt());
|
|
- assertEquals(1, pemCert.refCnt());
|
|
- try {
|
|
- assertTrue(context instanceof ReferenceCountedOpenSslContext);
|
|
- } finally {
|
|
- ReferenceCountUtil.release(context);
|
|
- assertRelease(pemKey);
|
|
- assertRelease(pemCert);
|
|
- }
|
|
- }
|
|
-
|
|
- private static void assertRelease(PemEncoded encoded) {
|
|
- assertTrue(encoded.release());
|
|
- }
|
|
-
|
|
- private static byte[] toByteArray(File file) throws Exception {
|
|
- FileInputStream in = new FileInputStream(file);
|
|
- try {
|
|
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
- try {
|
|
- byte[] buf = new byte[1024];
|
|
- int len;
|
|
- while ((len = in.read(buf)) != -1) {
|
|
- baos.write(buf, 0, len);
|
|
- }
|
|
- } finally {
|
|
- baos.close();
|
|
- }
|
|
-
|
|
- return baos.toByteArray();
|
|
- } finally {
|
|
- in.close();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java
|
|
deleted file mode 100644
|
|
index 6d38940..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java
|
|
+++ /dev/null
|
|
@@ -1,57 +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.util.ReferenceCountUtil;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-
|
|
-public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest {
|
|
-
|
|
- public ReferenceCountedOpenSslEngineTest(BufferType type) {
|
|
- super(type);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslClientProvider() {
|
|
- return SslProvider.OPENSSL_REFCNT;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslServerProvider() {
|
|
- return SslProvider.OPENSSL_REFCNT;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected void cleanupClientSslContext(SslContext ctx) {
|
|
- ReferenceCountUtil.release(ctx);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected void cleanupClientSslEngine(SSLEngine engine) {
|
|
- ReferenceCountUtil.release(engine);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected void cleanupServerSslContext(SslContext ctx) {
|
|
- ReferenceCountUtil.release(ctx);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected void cleanupServerSslEngine(SSLEngine engine) {
|
|
- ReferenceCountUtil.release(engine);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java
|
|
deleted file mode 100644
|
|
index 3193d20..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java
|
|
+++ /dev/null
|
|
@@ -1,161 +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.bootstrap.Bootstrap;
|
|
-import io.netty.bootstrap.ServerBootstrap;
|
|
-import io.netty.buffer.ByteBufAllocator;
|
|
-import io.netty.channel.Channel;
|
|
-import io.netty.channel.ChannelInitializer;
|
|
-import io.netty.channel.DefaultEventLoopGroup;
|
|
-import io.netty.channel.EventLoopGroup;
|
|
-import io.netty.channel.local.LocalAddress;
|
|
-import io.netty.channel.local.LocalChannel;
|
|
-import io.netty.channel.local.LocalServerChannel;
|
|
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.Mapping;
|
|
-import io.netty.util.concurrent.Promise;
|
|
-import io.netty.util.internal.PlatformDependent;
|
|
-import org.junit.Assert;
|
|
-import org.junit.Assume;
|
|
-import org.junit.Test;
|
|
-
|
|
-import java.nio.channels.ClosedChannelException;
|
|
-
|
|
-public class SniClientTest {
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniClientJdkSslServerJdkSsl() throws Exception {
|
|
- testSniClient(SslProvider.JDK, SslProvider.JDK);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniClientOpenSslServerOpenSsl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniClientJdkSslServerOpenSsl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testSniClient(SslProvider.JDK, SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniClientOpenSslServerJdkSsl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testSniClient(SslProvider.OPENSSL, SslProvider.JDK);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniSNIMatcherMatchesClientJdkSslServerJdkSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.JDK, true);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000, expected = ClosedChannelException.class)
|
|
- public void testSniSNIMatcherDoesNotMatchClientJdkSslServerJdkSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.JDK, false);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniSNIMatcherMatchesClientOpenSslServerOpenSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL, true);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000, expected = ClosedChannelException.class)
|
|
- public void testSniSNIMatcherDoesNotMatchClientOpenSslServerOpenSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.OPENSSL, false);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniSNIMatcherMatchesClientJdkSslServerOpenSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.OPENSSL, true);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000, expected = ClosedChannelException.class)
|
|
- public void testSniSNIMatcherDoesNotMatchClientJdkSslServerOpenSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.JDK, SslProvider.OPENSSL, false);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testSniSNIMatcherMatchesClientOpenSslServerJdkSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.JDK, true);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000, expected = ClosedChannelException.class)
|
|
- public void testSniSNIMatcherDoesNotMatchClientOpenSslServerJdkSsl() throws Exception {
|
|
- Assume.assumeTrue(PlatformDependent.javaVersion() >= 8);
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- SniClientJava8TestUtil.testSniClient(SslProvider.OPENSSL, SslProvider.JDK, false);
|
|
- }
|
|
-
|
|
- private static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider) throws Exception {
|
|
- final String sniHost = "sni.netty.io";
|
|
- LocalAddress address = new LocalAddress("test");
|
|
- EventLoopGroup group = new DefaultEventLoopGroup(1);
|
|
- Channel sc = null;
|
|
- Channel cc = null;
|
|
- try {
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
- final SslContext sslServerContext = SslContextBuilder.forServer(cert.key(), cert.cert())
|
|
- .sslProvider(sslServerProvider).build();
|
|
-
|
|
- final Promise<String> promise = group.next().newPromise();
|
|
- ServerBootstrap sb = new ServerBootstrap();
|
|
- sc = sb.group(group).channel(LocalServerChannel.class).childHandler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ch.pipeline().addFirst(new SniHandler(new Mapping<String, SslContext>() {
|
|
- @Override
|
|
- public SslContext map(String input) {
|
|
- promise.setSuccess(input);
|
|
- return sslServerContext;
|
|
- }
|
|
- }));
|
|
- }
|
|
- }).bind(address).syncUninterruptibly().channel();
|
|
-
|
|
- SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .sslProvider(sslClientProvider).build();
|
|
- Bootstrap cb = new Bootstrap();
|
|
- cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler(
|
|
- sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1)))
|
|
- .connect(address).syncUninterruptibly().channel();
|
|
- Assert.assertEquals(sniHost, promise.syncUninterruptibly().getNow());
|
|
- } finally {
|
|
- if (cc != null) {
|
|
- cc.close().syncUninterruptibly();
|
|
- }
|
|
- if (sc != null) {
|
|
- sc.close().syncUninterruptibly();
|
|
- }
|
|
- group.shutdownGracefully();
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java
|
|
deleted file mode 100644
|
|
index 07c87c6..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java
|
|
+++ /dev/null
|
|
@@ -1,496 +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 static org.hamcrest.CoreMatchers.is;
|
|
-import static org.hamcrest.CoreMatchers.nullValue;
|
|
-import static org.junit.Assert.assertEquals;
|
|
-import static org.junit.Assert.assertThat;
|
|
-import static org.junit.Assert.assertTrue;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-import java.io.File;
|
|
-import java.net.InetSocketAddress;
|
|
-import java.util.ArrayList;
|
|
-import java.util.List;
|
|
-import java.util.concurrent.CountDownLatch;
|
|
-import java.util.concurrent.TimeUnit;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-
|
|
-import org.junit.Test;
|
|
-
|
|
-import io.netty.bootstrap.Bootstrap;
|
|
-import io.netty.bootstrap.ServerBootstrap;
|
|
-import io.netty.buffer.ByteBufAllocator;
|
|
-import io.netty.buffer.Unpooled;
|
|
-import io.netty.channel.Channel;
|
|
-import io.netty.channel.ChannelFuture;
|
|
-import io.netty.channel.ChannelHandlerContext;
|
|
-import io.netty.channel.ChannelInitializer;
|
|
-import io.netty.channel.ChannelPipeline;
|
|
-import io.netty.channel.DefaultEventLoopGroup;
|
|
-import io.netty.channel.EventLoopGroup;
|
|
-import io.netty.channel.embedded.EmbeddedChannel;
|
|
-import io.netty.channel.local.LocalAddress;
|
|
-import io.netty.channel.local.LocalChannel;
|
|
-import io.netty.channel.local.LocalServerChannel;
|
|
-import io.netty.channel.nio.NioEventLoopGroup;
|
|
-import io.netty.channel.socket.nio.NioServerSocketChannel;
|
|
-import io.netty.channel.socket.nio.NioSocketChannel;
|
|
-import io.netty.handler.codec.DecoderException;
|
|
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.DomainNameMapping;
|
|
-import io.netty.util.DomainNameMappingBuilder;
|
|
-import io.netty.util.Mapping;
|
|
-import io.netty.util.ReferenceCountUtil;
|
|
-import io.netty.util.ReferenceCounted;
|
|
-import io.netty.util.concurrent.Promise;
|
|
-import io.netty.util.internal.ObjectUtil;
|
|
-import io.netty.util.internal.StringUtil;
|
|
-import org.junit.runner.RunWith;
|
|
-import org.junit.runners.Parameterized;
|
|
-
|
|
-@RunWith(Parameterized.class)
|
|
-public class SniHandlerTest {
|
|
-
|
|
- private static ApplicationProtocolConfig newApnConfig() {
|
|
- return new ApplicationProtocolConfig(
|
|
- ApplicationProtocolConfig.Protocol.ALPN,
|
|
- // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers.
|
|
- ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
|
- // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers.
|
|
- ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
|
- "myprotocol");
|
|
- }
|
|
-
|
|
- private static void assumeApnSupported(SslProvider provider) {
|
|
- switch (provider) {
|
|
- case OPENSSL:
|
|
- case OPENSSL_REFCNT:
|
|
- assumeTrue(OpenSsl.isAlpnSupported());
|
|
- break;
|
|
- case JDK:
|
|
- assumeTrue(JettyAlpnSslEngine.isAvailable());
|
|
- break;
|
|
- default:
|
|
- throw new Error();
|
|
- }
|
|
- }
|
|
-
|
|
- private static SslContext makeSslContext(SslProvider provider, boolean apn) throws Exception {
|
|
- if (apn) {
|
|
- assumeApnSupported(provider);
|
|
- }
|
|
-
|
|
- File keyFile = new File(SniHandlerTest.class.getResource("test_encrypted.pem").getFile());
|
|
- File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile());
|
|
-
|
|
- SslContextBuilder sslCtxBuilder = SslContextBuilder.forServer(crtFile, keyFile, "12345")
|
|
- .sslProvider(provider);
|
|
- if (apn) {
|
|
- sslCtxBuilder.applicationProtocolConfig(newApnConfig());
|
|
- }
|
|
- return sslCtxBuilder.build();
|
|
- }
|
|
-
|
|
- private static SslContext makeSslClientContext(SslProvider provider, boolean apn) throws Exception {
|
|
- if (apn) {
|
|
- assumeApnSupported(provider);
|
|
- }
|
|
-
|
|
- File crtFile = new File(SniHandlerTest.class.getResource("test.crt").getFile());
|
|
-
|
|
- SslContextBuilder sslCtxBuilder = SslContextBuilder.forClient().trustManager(crtFile).sslProvider(provider);
|
|
- if (apn) {
|
|
- sslCtxBuilder.applicationProtocolConfig(newApnConfig());
|
|
- }
|
|
- return sslCtxBuilder.build();
|
|
- }
|
|
-
|
|
- @Parameterized.Parameters(name = "{index}: sslProvider={0}")
|
|
- public static Iterable<?> data() {
|
|
- List<SslProvider> params = new ArrayList<SslProvider>(3);
|
|
- if (OpenSsl.isAvailable()) {
|
|
- params.add(SslProvider.OPENSSL);
|
|
- params.add(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
- params.add(SslProvider.JDK);
|
|
- return params;
|
|
- }
|
|
-
|
|
- private final SslProvider provider;
|
|
-
|
|
- public SniHandlerTest(SslProvider provider) {
|
|
- this.provider = provider;
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testServerNameParsing() throws Exception {
|
|
- SslContext nettyContext = makeSslContext(provider, false);
|
|
- SslContext leanContext = makeSslContext(provider, false);
|
|
- SslContext leanContext2 = makeSslContext(provider, false);
|
|
-
|
|
- try {
|
|
- DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
|
|
- .add("*.netty.io", nettyContext)
|
|
- // input with custom cases
|
|
- .add("*.LEANCLOUD.CN", leanContext)
|
|
- // a hostname conflict with previous one, since we are using order-sensitive config,
|
|
- // the engine won't be used with the handler.
|
|
- .add("chat4.leancloud.cn", leanContext2)
|
|
- .build();
|
|
-
|
|
- SniHandler handler = new SniHandler(mapping);
|
|
- EmbeddedChannel ch = new EmbeddedChannel(handler);
|
|
-
|
|
- try {
|
|
- // hex dump of a client hello packet, which contains hostname "CHAT4.LEANCLOUD.CN"
|
|
- String tlsHandshakeMessageHex1 = "16030100";
|
|
- // part 2
|
|
- String tlsHandshakeMessageHex = "c6010000c20303bb0855d66532c05a0ef784f7c384feeafa68b3" +
|
|
- "b655ac7288650d5eed4aa3fb52000038c02cc030009fcca9cca8ccaac02b" +
|
|
- "c02f009ec024c028006bc023c0270067c00ac0140039c009c0130033009d" +
|
|
- "009c003d003c0035002f00ff010000610000001700150000124348415434" +
|
|
- "2e4c45414e434c4f55442e434e000b000403000102000a000a0008001d00" +
|
|
- "170019001800230000000d0020001e060106020603050105020503040104" +
|
|
- "0204030301030203030201020202030016000000170000";
|
|
-
|
|
- ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex1)));
|
|
- ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex)));
|
|
-
|
|
- // This should produce an alert
|
|
- assertTrue(ch.finish());
|
|
-
|
|
- assertThat(handler.hostname(), is("chat4.leancloud.cn"));
|
|
- assertThat(handler.sslContext(), is(leanContext));
|
|
- } finally {
|
|
- ch.finishAndReleaseAll();
|
|
- }
|
|
- } finally {
|
|
- releaseAll(leanContext, leanContext2, nettyContext);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(expected = DecoderException.class)
|
|
- public void testNonAsciiServerNameParsing() throws Exception {
|
|
- SslContext nettyContext = makeSslContext(provider, false);
|
|
- SslContext leanContext = makeSslContext(provider, false);
|
|
- SslContext leanContext2 = makeSslContext(provider, false);
|
|
-
|
|
- try {
|
|
- DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
|
|
- .add("*.netty.io", nettyContext)
|
|
- // input with custom cases
|
|
- .add("*.LEANCLOUD.CN", leanContext)
|
|
- // a hostname conflict with previous one, since we are using order-sensitive config,
|
|
- // the engine won't be used with the handler.
|
|
- .add("chat4.leancloud.cn", leanContext2)
|
|
- .build();
|
|
-
|
|
- SniHandler handler = new SniHandler(mapping);
|
|
- EmbeddedChannel ch = new EmbeddedChannel(handler);
|
|
-
|
|
- try {
|
|
- // hex dump of a client hello packet, which contains an invalid hostname "CHAT4。LEANCLOUD。CN"
|
|
- String tlsHandshakeMessageHex1 = "16030100";
|
|
- // part 2
|
|
- String tlsHandshakeMessageHex = "bd010000b90303a74225676d1814ba57faff3b366" +
|
|
- "3656ed05ee9dbb2a4dbb1bb1c32d2ea5fc39e0000000100008c0000001700150000164348" +
|
|
- "415434E380824C45414E434C4F5544E38082434E000b000403000102000a00340032000e0" +
|
|
- "00d0019000b000c00180009000a0016001700080006000700140015000400050012001300" +
|
|
- "0100020003000f0010001100230000000d0020001e0601060206030501050205030401040" +
|
|
- "20403030103020303020102020203000f00010133740000";
|
|
-
|
|
- // Push the handshake message.
|
|
- // Decode should fail because of the badly encoded "HostName" string in the SNI extension
|
|
- // that isn't ASCII as per RFC 6066 - https://tools.ietf.org/html/rfc6066#page-6
|
|
- ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex1)));
|
|
- ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex)));
|
|
- } finally {
|
|
- ch.finishAndReleaseAll();
|
|
- }
|
|
- } finally {
|
|
- releaseAll(leanContext, leanContext2, nettyContext);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testFallbackToDefaultContext() throws Exception {
|
|
- SslContext nettyContext = makeSslContext(provider, false);
|
|
- SslContext leanContext = makeSslContext(provider, false);
|
|
- SslContext leanContext2 = makeSslContext(provider, false);
|
|
-
|
|
- try {
|
|
- DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
|
|
- .add("*.netty.io", nettyContext)
|
|
- // input with custom cases
|
|
- .add("*.LEANCLOUD.CN", leanContext)
|
|
- // a hostname conflict with previous one, since we are using order-sensitive config,
|
|
- // the engine won't be used with the handler.
|
|
- .add("chat4.leancloud.cn", leanContext2)
|
|
- .build();
|
|
-
|
|
- SniHandler handler = new SniHandler(mapping);
|
|
- EmbeddedChannel ch = new EmbeddedChannel(handler);
|
|
-
|
|
- // invalid
|
|
- byte[] message = {22, 3, 1, 0, 0};
|
|
-
|
|
- try {
|
|
- // Push the handshake message.
|
|
- ch.writeInbound(Unpooled.wrappedBuffer(message));
|
|
- } catch (Exception e) {
|
|
- // expected
|
|
- }
|
|
-
|
|
- assertThat(ch.finish(), is(false));
|
|
- assertThat(handler.hostname(), nullValue());
|
|
- assertThat(handler.sslContext(), is(nettyContext));
|
|
- } finally {
|
|
- releaseAll(leanContext, leanContext2, nettyContext);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testSniWithApnHandler() throws Exception {
|
|
- SslContext nettyContext = makeSslContext(provider, true);
|
|
- SslContext sniContext = makeSslContext(provider, true);
|
|
- final SslContext clientContext = makeSslClientContext(provider, true);
|
|
- try {
|
|
- final CountDownLatch serverApnDoneLatch = new CountDownLatch(1);
|
|
- final CountDownLatch clientApnDoneLatch = new CountDownLatch(1);
|
|
-
|
|
- final DomainNameMapping<SslContext> mapping = new DomainNameMappingBuilder<SslContext>(nettyContext)
|
|
- .add("*.netty.io", nettyContext)
|
|
- .add("sni.fake.site", sniContext).build();
|
|
- final SniHandler handler = new SniHandler(mapping);
|
|
- EventLoopGroup group = new NioEventLoopGroup(2);
|
|
- Channel serverChannel = null;
|
|
- Channel clientChannel = null;
|
|
- try {
|
|
- ServerBootstrap sb = new ServerBootstrap();
|
|
- sb.group(group);
|
|
- sb.channel(NioServerSocketChannel.class);
|
|
- sb.childHandler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ChannelPipeline p = ch.pipeline();
|
|
- // Server side SNI.
|
|
- p.addLast(handler);
|
|
- // Catch the notification event that APN has completed successfully.
|
|
- p.addLast(new ApplicationProtocolNegotiationHandler("foo") {
|
|
- @Override
|
|
- protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
|
|
- serverApnDoneLatch.countDown();
|
|
- }
|
|
- });
|
|
- }
|
|
- });
|
|
-
|
|
- Bootstrap cb = new Bootstrap();
|
|
- cb.group(group);
|
|
- cb.channel(NioSocketChannel.class);
|
|
- cb.handler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ch.pipeline().addLast(new SslHandler(clientContext.newEngine(
|
|
- ch.alloc(), "sni.fake.site", -1)));
|
|
- // Catch the notification event that APN has completed successfully.
|
|
- ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("foo") {
|
|
- @Override
|
|
- protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
|
|
- clientApnDoneLatch.countDown();
|
|
- }
|
|
- });
|
|
- }
|
|
- });
|
|
-
|
|
- serverChannel = sb.bind(new InetSocketAddress(0)).sync().channel();
|
|
-
|
|
- ChannelFuture ccf = cb.connect(serverChannel.localAddress());
|
|
- assertTrue(ccf.awaitUninterruptibly().isSuccess());
|
|
- clientChannel = ccf.channel();
|
|
-
|
|
- assertTrue(serverApnDoneLatch.await(5, TimeUnit.SECONDS));
|
|
- assertTrue(clientApnDoneLatch.await(5, TimeUnit.SECONDS));
|
|
- assertThat(handler.hostname(), is("sni.fake.site"));
|
|
- assertThat(handler.sslContext(), is(sniContext));
|
|
- } finally {
|
|
- if (serverChannel != null) {
|
|
- serverChannel.close().sync();
|
|
- }
|
|
- if (clientChannel != null) {
|
|
- clientChannel.close().sync();
|
|
- }
|
|
- group.shutdownGracefully(0, 0, TimeUnit.MICROSECONDS);
|
|
- }
|
|
- } finally {
|
|
- releaseAll(clientContext, nettyContext, sniContext);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testReplaceHandler() throws Exception {
|
|
- switch (provider) {
|
|
- case OPENSSL:
|
|
- case OPENSSL_REFCNT:
|
|
- final String sniHost = "sni.netty.io";
|
|
- LocalAddress address = new LocalAddress("testReplaceHandler-" + Math.random());
|
|
- EventLoopGroup group = new DefaultEventLoopGroup(1);
|
|
- Channel sc = null;
|
|
- Channel cc = null;
|
|
- SslContext sslContext = null;
|
|
-
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
-
|
|
- try {
|
|
- final SslContext sslServerContext = SslContextBuilder
|
|
- .forServer(cert.key(), cert.cert())
|
|
- .sslProvider(provider)
|
|
- .build();
|
|
-
|
|
- final Mapping<String, SslContext> mapping = new Mapping<String, SslContext>() {
|
|
- @Override
|
|
- public SslContext map(String input) {
|
|
- return sslServerContext;
|
|
- }
|
|
- };
|
|
-
|
|
- final Promise<Void> releasePromise = group.next().newPromise();
|
|
-
|
|
- final SniHandler handler = new SniHandler(mapping) {
|
|
- @Override
|
|
- protected void replaceHandler(ChannelHandlerContext ctx,
|
|
- String hostname, final SslContext sslContext)
|
|
- throws Exception {
|
|
-
|
|
- boolean success = false;
|
|
- try {
|
|
- // The SniHandler's replaceHandler() method allows us to implement custom behavior.
|
|
- // As an example, we want to release() the SslContext upon channelInactive() or rather
|
|
- // when the SslHandler closes it's SslEngine. If you take a close look at SslHandler
|
|
- // you'll see that it's doing it in the #handlerRemoved0() method.
|
|
-
|
|
- SSLEngine sslEngine = sslContext.newEngine(ctx.alloc());
|
|
- try {
|
|
- SslHandler customSslHandler = new CustomSslHandler(sslContext, sslEngine) {
|
|
- @Override
|
|
- public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
|
|
- try {
|
|
- super.handlerRemoved0(ctx);
|
|
- } finally {
|
|
- releasePromise.trySuccess(null);
|
|
- }
|
|
- }
|
|
- };
|
|
- ctx.pipeline().replace(this, CustomSslHandler.class.getName(), customSslHandler);
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- ReferenceCountUtil.safeRelease(sslEngine);
|
|
- }
|
|
- }
|
|
- } finally {
|
|
- if (!success) {
|
|
- ReferenceCountUtil.safeRelease(sslContext);
|
|
- releasePromise.cancel(true);
|
|
- }
|
|
- }
|
|
- }
|
|
- };
|
|
-
|
|
- ServerBootstrap sb = new ServerBootstrap();
|
|
- sc = sb.group(group).channel(LocalServerChannel.class)
|
|
- .childHandler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ch.pipeline().addFirst(handler);
|
|
- }
|
|
- }).bind(address).syncUninterruptibly().channel();
|
|
-
|
|
- sslContext = SslContextBuilder.forClient().sslProvider(provider)
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
|
|
-
|
|
- Bootstrap cb = new Bootstrap();
|
|
- cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler(
|
|
- sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1)))
|
|
- .connect(address).syncUninterruptibly().channel();
|
|
-
|
|
- cc.writeAndFlush(Unpooled.wrappedBuffer("Hello, World!".getBytes()))
|
|
- .syncUninterruptibly();
|
|
-
|
|
- // Notice how the server's SslContext refCnt is 1
|
|
- assertEquals(1, ((ReferenceCounted) sslServerContext).refCnt());
|
|
-
|
|
- // The client disconnects
|
|
- cc.close().syncUninterruptibly();
|
|
- if (!releasePromise.awaitUninterruptibly(10L, TimeUnit.SECONDS)) {
|
|
- throw new IllegalStateException("It doesn't seem #replaceHandler() got called.");
|
|
- }
|
|
-
|
|
- // We should have successfully release() the SslContext
|
|
- assertEquals(0, ((ReferenceCounted) sslServerContext).refCnt());
|
|
- } finally {
|
|
- if (cc != null) {
|
|
- cc.close().syncUninterruptibly();
|
|
- }
|
|
- if (sc != null) {
|
|
- sc.close().syncUninterruptibly();
|
|
- }
|
|
- if (sslContext != null) {
|
|
- ReferenceCountUtil.release(sslContext);
|
|
- }
|
|
- group.shutdownGracefully();
|
|
-
|
|
- cert.delete();
|
|
- }
|
|
- case JDK:
|
|
- return;
|
|
- default:
|
|
- throw new Error();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * This is a {@link SslHandler} that will call {@code release()} on the {@link SslContext} when
|
|
- * the client disconnects.
|
|
- *
|
|
- * @see SniHandlerTest#testReplaceHandler()
|
|
- */
|
|
- private static class CustomSslHandler extends SslHandler {
|
|
- private final SslContext sslContext;
|
|
-
|
|
- public CustomSslHandler(SslContext sslContext, SSLEngine sslEngine) {
|
|
- super(sslEngine);
|
|
- this.sslContext = ObjectUtil.checkNotNull(sslContext, "sslContext");
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
|
|
- super.handlerRemoved0(ctx);
|
|
- ReferenceCountUtil.release(sslContext);
|
|
- }
|
|
- }
|
|
-
|
|
- private static void releaseAll(SslContext... contexts) {
|
|
- for (SslContext ctx: contexts) {
|
|
- ReferenceCountUtil.release(ctx);
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java
|
|
deleted file mode 100644
|
|
index 752424c..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java
|
|
+++ /dev/null
|
|
@@ -1,132 +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 static org.junit.Assert.assertFalse;
|
|
-import static org.junit.Assert.assertTrue;
|
|
-
|
|
-import io.netty.buffer.UnpooledByteBufAllocator;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import org.junit.Assume;
|
|
-import org.junit.Test;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-
|
|
-public class SslContextBuilderTest {
|
|
-
|
|
- @Test
|
|
- public void testClientContextFromFileJdk() throws Exception {
|
|
- testClientContextFromFile(SslProvider.JDK);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testClientContextFromFileOpenssl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testClientContextFromFile(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testClientContextJdk() throws Exception {
|
|
- testClientContext(SslProvider.JDK);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testClientContextOpenssl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testClientContext(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testServerContextFromFileJdk() throws Exception {
|
|
- testServerContextFromFile(SslProvider.JDK);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testServerContextFromFileOpenssl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testServerContextFromFile(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testServerContextJdk() throws Exception {
|
|
- testServerContext(SslProvider.JDK);
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testServerContextOpenssl() throws Exception {
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
- testServerContext(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- private static void testClientContextFromFile(SslProvider provider) throws Exception {
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
- SslContextBuilder builder = SslContextBuilder.forClient()
|
|
- .sslProvider(provider)
|
|
- .keyManager(cert.certificate(),
|
|
- cert.privateKey())
|
|
- .trustManager(cert.certificate())
|
|
- .clientAuth(ClientAuth.OPTIONAL);
|
|
- SslContext context = builder.build();
|
|
- SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- assertFalse(engine.getWantClientAuth());
|
|
- assertFalse(engine.getNeedClientAuth());
|
|
- engine.closeInbound();
|
|
- engine.closeOutbound();
|
|
- }
|
|
-
|
|
- private static void testClientContext(SslProvider provider) throws Exception {
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
- SslContextBuilder builder = SslContextBuilder.forClient()
|
|
- .sslProvider(provider)
|
|
- .keyManager(cert.key(), cert.cert())
|
|
- .trustManager(cert.cert())
|
|
- .clientAuth(ClientAuth.OPTIONAL);
|
|
- SslContext context = builder.build();
|
|
- SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- assertFalse(engine.getWantClientAuth());
|
|
- assertFalse(engine.getNeedClientAuth());
|
|
- engine.closeInbound();
|
|
- engine.closeOutbound();
|
|
- }
|
|
-
|
|
- private static void testServerContextFromFile(SslProvider provider) throws Exception {
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
- SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey())
|
|
- .sslProvider(provider)
|
|
- .trustManager(cert.certificate())
|
|
- .clientAuth(ClientAuth.OPTIONAL);
|
|
- SslContext context = builder.build();
|
|
- SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- assertTrue(engine.getWantClientAuth());
|
|
- assertFalse(engine.getNeedClientAuth());
|
|
- engine.closeInbound();
|
|
- engine.closeOutbound();
|
|
- }
|
|
-
|
|
- private static void testServerContext(SslProvider provider) throws Exception {
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
- SslContextBuilder builder = SslContextBuilder.forServer(cert.key(), cert.cert())
|
|
- .sslProvider(provider)
|
|
- .trustManager(cert.cert())
|
|
- .clientAuth(ClientAuth.REQUIRE);
|
|
- SslContext context = builder.build();
|
|
- SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
|
|
- assertFalse(engine.getWantClientAuth());
|
|
- assertTrue(engine.getNeedClientAuth());
|
|
- engine.closeInbound();
|
|
- engine.closeOutbound();
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java
|
|
deleted file mode 100644
|
|
index aacdb69..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java
|
|
+++ /dev/null
|
|
@@ -1,255 +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.bootstrap.Bootstrap;
|
|
-import io.netty.bootstrap.ServerBootstrap;
|
|
-import io.netty.channel.Channel;
|
|
-import io.netty.channel.ChannelHandlerContext;
|
|
-import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
-import io.netty.channel.ChannelInitializer;
|
|
-import io.netty.channel.EventLoopGroup;
|
|
-import io.netty.channel.nio.NioEventLoopGroup;
|
|
-import io.netty.channel.socket.nio.NioServerSocketChannel;
|
|
-import io.netty.channel.socket.nio.NioSocketChannel;
|
|
-import io.netty.handler.logging.LogLevel;
|
|
-import io.netty.handler.logging.LoggingHandler;
|
|
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.handler.ssl.util.SimpleTrustManagerFactory;
|
|
-import io.netty.util.ReferenceCountUtil;
|
|
-import io.netty.util.concurrent.Promise;
|
|
-import io.netty.util.internal.EmptyArrays;
|
|
-import org.junit.Assume;
|
|
-import org.junit.Test;
|
|
-import org.junit.runner.RunWith;
|
|
-import org.junit.runners.Parameterized;
|
|
-
|
|
-import javax.net.ssl.ManagerFactoryParameters;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.TrustManager;
|
|
-import javax.net.ssl.X509TrustManager;
|
|
-import javax.security.auth.x500.X500Principal;
|
|
-import java.io.File;
|
|
-import java.security.KeyStore;
|
|
-import java.security.cert.CRLReason;
|
|
-import java.security.cert.CertPathValidatorException;
|
|
-import java.security.cert.CertificateException;
|
|
-import java.security.cert.CertificateExpiredException;
|
|
-import java.security.cert.CertificateNotYetValidException;
|
|
-import java.security.cert.CertificateRevokedException;
|
|
-import java.security.cert.Extension;
|
|
-import java.security.cert.X509Certificate;
|
|
-import java.util.ArrayList;
|
|
-import java.util.Collection;
|
|
-import java.util.Collections;
|
|
-import java.util.Date;
|
|
-import java.util.List;
|
|
-import java.util.Locale;
|
|
-
|
|
-
|
|
-@RunWith(Parameterized.class)
|
|
-public class SslErrorTest {
|
|
-
|
|
- @Parameterized.Parameters(name = "{index}: serverProvider = {0}, clientProvider = {1}, exception = {2}")
|
|
- public static Collection<Object[]> data() {
|
|
- List<SslProvider> serverProviders = new ArrayList<SslProvider>(2);
|
|
- List<SslProvider> clientProviders = new ArrayList<SslProvider>(3);
|
|
-
|
|
- if (OpenSsl.isAvailable()) {
|
|
- serverProviders.add(SslProvider.OPENSSL);
|
|
- serverProviders.add(SslProvider.OPENSSL_REFCNT);
|
|
- clientProviders.add(SslProvider.OPENSSL);
|
|
- clientProviders.add(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
- // We not test with SslProvider.JDK on the server side as the JDK implementation currently just send the same
|
|
- // alert all the time, sigh.....
|
|
- clientProviders.add(SslProvider.JDK);
|
|
-
|
|
- List<CertificateException> exceptions = new ArrayList<CertificateException>(6);
|
|
- exceptions.add(new CertificateExpiredException());
|
|
- exceptions.add(new CertificateNotYetValidException());
|
|
- exceptions.add(new CertificateRevokedException(
|
|
- new Date(), CRLReason.AA_COMPROMISE, new X500Principal(""),
|
|
- Collections.<String, Extension>emptyMap()));
|
|
-
|
|
- // Also use wrapped exceptions as this is what the JDK implementation of X509TrustManagerFactory is doing.
|
|
- exceptions.add(newCertificateException(CertPathValidatorException.BasicReason.EXPIRED));
|
|
- exceptions.add(newCertificateException(CertPathValidatorException.BasicReason.NOT_YET_VALID));
|
|
- exceptions.add(newCertificateException(CertPathValidatorException.BasicReason.REVOKED));
|
|
-
|
|
- List<Object[]> params = new ArrayList<Object[]>();
|
|
- for (SslProvider serverProvider: serverProviders) {
|
|
- for (SslProvider clientProvider: clientProviders) {
|
|
- for (CertificateException exception: exceptions) {
|
|
- params.add(new Object[] { serverProvider, clientProvider, exception});
|
|
- }
|
|
- }
|
|
- }
|
|
- return params;
|
|
- }
|
|
-
|
|
- private static CertificateException newCertificateException(CertPathValidatorException.Reason reason) {
|
|
- return new TestCertificateException(
|
|
- new CertPathValidatorException("x", null, null, -1, reason));
|
|
- }
|
|
-
|
|
- private final SslProvider serverProvider;
|
|
- private final SslProvider clientProvider;
|
|
- private final CertificateException exception;
|
|
-
|
|
- public SslErrorTest(SslProvider serverProvider, SslProvider clientProvider, CertificateException exception) {
|
|
- this.serverProvider = serverProvider;
|
|
- this.clientProvider = clientProvider;
|
|
- this.exception = exception;
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
- public void testCorrectAlert() throws Exception {
|
|
- // As this only works correctly at the moment when OpenSslEngine is used on the server-side there is
|
|
- // no need to run it if there is no openssl is available at all.
|
|
- Assume.assumeTrue(OpenSsl.isAvailable());
|
|
-
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(serverProvider)
|
|
- .trustManager(new SimpleTrustManagerFactory() {
|
|
- @Override
|
|
- protected void engineInit(KeyStore keyStore) { }
|
|
- @Override
|
|
- protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { }
|
|
-
|
|
- @Override
|
|
- protected TrustManager[] engineGetTrustManagers() {
|
|
- return new TrustManager[] { new X509TrustManager() {
|
|
-
|
|
- @Override
|
|
- public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
|
|
- throws CertificateException {
|
|
- throw exception;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
|
|
- throws CertificateException {
|
|
- // NOOP
|
|
- }
|
|
-
|
|
- @Override
|
|
- public X509Certificate[] getAcceptedIssuers() {
|
|
- return EmptyArrays.EMPTY_X509_CERTIFICATES;
|
|
- }
|
|
- } };
|
|
- }
|
|
- }).clientAuth(ClientAuth.REQUIRE).build();
|
|
-
|
|
- final SslContext sslClientCtx = SslContextBuilder.forClient()
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .keyManager(new File(getClass().getResource("test.crt").getFile()),
|
|
- new File(getClass().getResource("test_unencrypted.pem").getFile()))
|
|
- .sslProvider(clientProvider).build();
|
|
-
|
|
- Channel serverChannel = null;
|
|
- Channel clientChannel = null;
|
|
- EventLoopGroup group = new NioEventLoopGroup();
|
|
- try {
|
|
- serverChannel = new ServerBootstrap().group(group)
|
|
- .channel(NioServerSocketChannel.class)
|
|
- .handler(new LoggingHandler(LogLevel.INFO))
|
|
- .childHandler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc()));
|
|
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
|
|
-
|
|
- @Override
|
|
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
- ctx.close();
|
|
- }
|
|
- });
|
|
- }
|
|
- }).bind(0).sync().channel();
|
|
-
|
|
- final Promise<Void> promise = group.next().newPromise();
|
|
-
|
|
- clientChannel = new Bootstrap().group(group)
|
|
- .channel(NioSocketChannel.class)
|
|
- .handler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc()));
|
|
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
- // Unwrap as its wrapped by a DecoderException
|
|
- Throwable unwrappedCause = cause.getCause();
|
|
- if (unwrappedCause instanceof SSLException) {
|
|
- if (exception instanceof TestCertificateException) {
|
|
- CertPathValidatorException.Reason reason =
|
|
- ((CertPathValidatorException) exception.getCause()).getReason();
|
|
- if (reason == CertPathValidatorException.BasicReason.EXPIRED) {
|
|
- verifyException(unwrappedCause, "expired", promise);
|
|
- } else if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) {
|
|
- verifyException(unwrappedCause, "bad", promise);
|
|
- } else if (reason == CertPathValidatorException.BasicReason.REVOKED) {
|
|
- verifyException(unwrappedCause, "revoked", promise);
|
|
- }
|
|
- } else if (exception instanceof CertificateExpiredException) {
|
|
- verifyException(unwrappedCause, "expired", promise);
|
|
- } else if (exception instanceof CertificateNotYetValidException) {
|
|
- verifyException(unwrappedCause, "bad", promise);
|
|
- } else if (exception instanceof CertificateRevokedException) {
|
|
- verifyException(unwrappedCause, "revoked", promise);
|
|
- }
|
|
- }
|
|
- }
|
|
- });
|
|
- }
|
|
- }).connect(serverChannel.localAddress()).syncUninterruptibly().channel();
|
|
- // Block until we received the correct exception
|
|
- promise.syncUninterruptibly();
|
|
- } finally {
|
|
- if (clientChannel != null) {
|
|
- clientChannel.close().syncUninterruptibly();
|
|
- }
|
|
- if (serverChannel != null) {
|
|
- serverChannel.close().syncUninterruptibly();
|
|
- }
|
|
- group.shutdownGracefully();
|
|
-
|
|
- ReferenceCountUtil.release(sslServerCtx);
|
|
- ReferenceCountUtil.release(sslClientCtx);
|
|
- }
|
|
- }
|
|
-
|
|
- // Its a bit hacky to verify against the message that is part of the exception but there is no other way
|
|
- // at the moment as there are no different exceptions for the different alerts.
|
|
- private static void verifyException(Throwable cause, String messagePart, Promise<Void> promise) {
|
|
- String message = cause.getMessage();
|
|
- if (message.toLowerCase(Locale.UK).contains(messagePart.toLowerCase(Locale.UK))) {
|
|
- promise.setSuccess(null);
|
|
- } else {
|
|
- promise.setFailure(new AssertionError("message not contains '" + messagePart + "': " + message));
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class TestCertificateException extends CertificateException {
|
|
-
|
|
- public TestCertificateException(Throwable cause) {
|
|
- super(cause);
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java
|
|
index 5ef43de..52c4d22 100644
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java
|
|
+++ b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java
|
|
@@ -121,35 +121,6 @@ public class SslHandlerTest {
|
|
}
|
|
}
|
|
|
|
- @Test
|
|
- public void testReleaseSslEngine() throws Exception {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
-
|
|
- SelfSignedCertificate cert = new SelfSignedCertificate();
|
|
- try {
|
|
- SslContext sslContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey())
|
|
- .sslProvider(SslProvider.OPENSSL)
|
|
- .build();
|
|
- try {
|
|
- SSLEngine sslEngine = sslContext.newEngine(ByteBufAllocator.DEFAULT);
|
|
- EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(sslEngine));
|
|
-
|
|
- assertEquals(1, ((ReferenceCounted) sslContext).refCnt());
|
|
- assertEquals(1, ((ReferenceCounted) sslEngine).refCnt());
|
|
-
|
|
- assertTrue(ch.finishAndReleaseAll());
|
|
- ch.close().syncUninterruptibly();
|
|
-
|
|
- assertEquals(1, ((ReferenceCounted) sslContext).refCnt());
|
|
- assertEquals(0, ((ReferenceCounted) sslEngine).refCnt());
|
|
- } finally {
|
|
- ReferenceCountUtil.release(sslContext);
|
|
- }
|
|
- } finally {
|
|
- cert.delete();
|
|
- }
|
|
- }
|
|
-
|
|
private static final class TlsReadTest extends ChannelOutboundHandlerAdapter {
|
|
private volatile boolean readIssued;
|
|
|
|
@@ -279,13 +250,6 @@ public class SslHandlerTest {
|
|
testAlertProducedAndSend(SslProvider.JDK);
|
|
}
|
|
|
|
- @Test(timeout = 30000)
|
|
- public void testAlertProducedAndSendOpenSsl() throws Exception {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- testAlertProducedAndSend(SslProvider.OPENSSL);
|
|
- testAlertProducedAndSend(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
private void testAlertProducedAndSend(SslProvider provider) throws Exception {
|
|
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
|
|
@@ -425,12 +389,6 @@ public class SslHandlerTest {
|
|
testCloseNotify(SslProvider.JDK, 5000, false);
|
|
}
|
|
|
|
- @Test(timeout = 30000)
|
|
- public void testCloseNotifyReceivedOpenSsl() throws Exception {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- testCloseNotify(SslProvider.OPENSSL, 5000, false);
|
|
- testCloseNotify(SslProvider.OPENSSL_REFCNT, 5000, false);
|
|
- }
|
|
|
|
@Test(timeout = 30000)
|
|
public void testCloseNotifyReceivedJdkTimeout() throws Exception {
|
|
@@ -438,24 +396,10 @@ public class SslHandlerTest {
|
|
}
|
|
|
|
@Test(timeout = 30000)
|
|
- public void testCloseNotifyReceivedOpenSslTimeout() throws Exception {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- testCloseNotify(SslProvider.OPENSSL, 100, true);
|
|
- testCloseNotify(SslProvider.OPENSSL_REFCNT, 100, true);
|
|
- }
|
|
-
|
|
- @Test(timeout = 30000)
|
|
public void testCloseNotifyNotWaitForResponseJdk() throws Exception {
|
|
testCloseNotify(SslProvider.JDK, 0, false);
|
|
}
|
|
|
|
- @Test(timeout = 30000)
|
|
- public void testCloseNotifyNotWaitForResponseOpenSsl() throws Exception {
|
|
- assumeTrue(OpenSsl.isAvailable());
|
|
- testCloseNotify(SslProvider.OPENSSL, 0, false);
|
|
- testCloseNotify(SslProvider.OPENSSL_REFCNT, 0, false);
|
|
- }
|
|
-
|
|
private static void testCloseNotify(SslProvider provider, final long closeNotifyReadTimeout, final boolean timeout)
|
|
throws Exception {
|
|
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
@@ -720,7 +664,7 @@ public class SslHandlerTest {
|
|
switch (provider) {
|
|
case OPENSSL:
|
|
case OPENSSL_REFCNT:
|
|
- return OpenSsl.isAvailable();
|
|
+ return false;
|
|
default:
|
|
return true;
|
|
}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java b/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java
|
|
deleted file mode 100644
|
|
index 4aecc74..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java
|
|
+++ /dev/null
|
|
@@ -1,501 +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.bootstrap.Bootstrap;
|
|
-import io.netty.bootstrap.ServerBootstrap;
|
|
-import io.netty.buffer.ByteBufAllocator;
|
|
-import io.netty.buffer.Unpooled;
|
|
-import io.netty.channel.Channel;
|
|
-import io.netty.channel.ChannelHandler;
|
|
-import io.netty.channel.ChannelHandlerContext;
|
|
-import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
-import io.netty.channel.ChannelInitializer;
|
|
-import io.netty.channel.ChannelPipeline;
|
|
-import io.netty.channel.DefaultEventLoopGroup;
|
|
-import io.netty.channel.EventLoopGroup;
|
|
-import io.netty.channel.local.LocalAddress;
|
|
-import io.netty.channel.local.LocalChannel;
|
|
-import io.netty.channel.local.LocalServerChannel;
|
|
-import io.netty.handler.ssl.OpenSsl;
|
|
-import io.netty.handler.ssl.ReferenceCountedOpenSslEngine;
|
|
-import io.netty.handler.ssl.SslContext;
|
|
-import io.netty.handler.ssl.SslContextBuilder;
|
|
-import io.netty.handler.ssl.SslHandler;
|
|
-import io.netty.handler.ssl.SslProvider;
|
|
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.CharsetUtil;
|
|
-import io.netty.util.ReferenceCountUtil;
|
|
-
|
|
-import java.net.SocketAddress;
|
|
-import java.util.concurrent.CountDownLatch;
|
|
-import java.util.concurrent.TimeUnit;
|
|
-import java.util.concurrent.TimeoutException;
|
|
-import java.util.concurrent.atomic.AtomicReference;
|
|
-
|
|
-import javax.net.ssl.SSLHandshakeException;
|
|
-
|
|
-import org.junit.BeforeClass;
|
|
-import org.junit.Test;
|
|
-
|
|
-import static org.junit.Assert.assertArrayEquals;
|
|
-import static org.junit.Assert.assertNotNull;
|
|
-import static org.junit.Assert.assertNotSame;
|
|
-import static org.junit.Assert.assertNull;
|
|
-import static org.junit.Assert.assertSame;
|
|
-import static org.junit.Assert.assertTrue;
|
|
-import static org.junit.Assume.assumeTrue;
|
|
-
|
|
-public class OcspTest {
|
|
-
|
|
- @BeforeClass
|
|
- public static void checkOcspSupported() {
|
|
- assumeTrue(OpenSsl.isOcspSupported());
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalArgumentException.class)
|
|
- public void testJdkClientEnableOcsp() throws Exception {
|
|
- SslContextBuilder.forClient()
|
|
- .sslProvider(SslProvider.JDK)
|
|
- .enableOcsp(true)
|
|
- .build();
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalArgumentException.class)
|
|
- public void testJdkServerEnableOcsp() throws Exception {
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- try {
|
|
- SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(SslProvider.JDK)
|
|
- .enableOcsp(true)
|
|
- .build();
|
|
- } finally {
|
|
- ssc.delete();
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalStateException.class)
|
|
- public void testClientOcspNotEnabledOpenSsl() throws Exception {
|
|
- testClientOcspNotEnabled(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalStateException.class)
|
|
- public void testClientOcspNotEnabledOpenSslRefCnt() throws Exception {
|
|
- testClientOcspNotEnabled(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- private void testClientOcspNotEnabled(SslProvider sslProvider) throws Exception {
|
|
- SslContext context = SslContextBuilder.forClient()
|
|
- .sslProvider(sslProvider)
|
|
- .build();
|
|
- try {
|
|
- SslHandler sslHandler = context.newHandler(ByteBufAllocator.DEFAULT);
|
|
- ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine();
|
|
- try {
|
|
- engine.getOcspResponse();
|
|
- } finally {
|
|
- engine.release();
|
|
- }
|
|
- } finally {
|
|
- ReferenceCountUtil.release(context);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalStateException.class)
|
|
- public void testServerOcspNotEnabledOpenSsl() throws Exception {
|
|
- testServerOcspNotEnabled(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(expected = IllegalStateException.class)
|
|
- public void testServerOcspNotEnabledOpenSslRefCnt() throws Exception {
|
|
- testServerOcspNotEnabled(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- private void testServerOcspNotEnabled(SslProvider sslProvider) throws Exception {
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- try {
|
|
- SslContext context = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslProvider)
|
|
- .build();
|
|
- try {
|
|
- SslHandler sslHandler = context.newHandler(ByteBufAllocator.DEFAULT);
|
|
- ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine();
|
|
- try {
|
|
- engine.setOcspResponse(new byte[] { 1, 2, 3 });
|
|
- } finally {
|
|
- engine.release();
|
|
- }
|
|
- } finally {
|
|
- ReferenceCountUtil.release(context);
|
|
- }
|
|
- } finally {
|
|
- ssc.delete();
|
|
- }
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testClientAcceptingOcspStapleOpenSsl() throws Exception {
|
|
- testClientAcceptingOcspStaple(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testClientAcceptingOcspStapleOpenSslRefCnt() throws Exception {
|
|
- testClientAcceptingOcspStaple(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- /**
|
|
- * The Server provides an OCSP staple and the Client accepts it.
|
|
- */
|
|
- private void testClientAcceptingOcspStaple(SslProvider sslProvider) throws Exception {
|
|
- final CountDownLatch latch = new CountDownLatch(1);
|
|
- ChannelInboundHandlerAdapter serverHandler = new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
- ctx.writeAndFlush(Unpooled.wrappedBuffer("Hello, World!".getBytes()));
|
|
- ctx.fireChannelActive();
|
|
- }
|
|
- };
|
|
-
|
|
- ChannelInboundHandlerAdapter clientHandler = new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
|
- try {
|
|
- ReferenceCountUtil.release(msg);
|
|
- } finally {
|
|
- latch.countDown();
|
|
- }
|
|
- }
|
|
- };
|
|
-
|
|
- byte[] response = newOcspResponse();
|
|
- TestClientOcspContext callback = new TestClientOcspContext(true);
|
|
-
|
|
- handshake(sslProvider, latch, serverHandler, response, clientHandler, callback);
|
|
-
|
|
- byte[] actual = callback.response();
|
|
-
|
|
- assertNotNull(actual);
|
|
- assertNotSame(response, actual);
|
|
- assertArrayEquals(response, actual);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testClientRejectingOcspStapleOpenSsl() throws Exception {
|
|
- testClientRejectingOcspStaple(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testClientRejectingOcspStapleOpenSslRefCnt() throws Exception {
|
|
- testClientRejectingOcspStaple(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- /**
|
|
- * The Server provides an OCSP staple and the Client rejects it.
|
|
- */
|
|
- private void testClientRejectingOcspStaple(SslProvider sslProvider) throws Exception {
|
|
- final AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>();
|
|
- final CountDownLatch latch = new CountDownLatch(1);
|
|
-
|
|
- ChannelInboundHandlerAdapter clientHandler = new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
- try {
|
|
- causeRef.set(cause);
|
|
- } finally {
|
|
- latch.countDown();
|
|
- }
|
|
- }
|
|
- };
|
|
-
|
|
- byte[] response = newOcspResponse();
|
|
- TestClientOcspContext callback = new TestClientOcspContext(false);
|
|
-
|
|
- handshake(sslProvider, latch, null, response, clientHandler, callback);
|
|
-
|
|
- byte[] actual = callback.response();
|
|
-
|
|
- assertNotNull(actual);
|
|
- assertNotSame(response, actual);
|
|
- assertArrayEquals(response, actual);
|
|
-
|
|
- Throwable cause = causeRef.get();
|
|
- assertTrue("" + cause, cause instanceof SSLHandshakeException);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testServerHasNoStapleOpenSsl() throws Exception {
|
|
- testServerHasNoStaple(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testServerHasNoStapleOpenSslRefCnt() throws Exception {
|
|
- testServerHasNoStaple(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- /**
|
|
- * The server has OCSP stapling enabled but doesn't provide a staple.
|
|
- */
|
|
- private void testServerHasNoStaple(SslProvider sslProvider) throws Exception {
|
|
- final CountDownLatch latch = new CountDownLatch(1);
|
|
- ChannelInboundHandlerAdapter serverHandler = new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
|
- ctx.writeAndFlush(Unpooled.wrappedBuffer("Hello, World!".getBytes()));
|
|
- ctx.fireChannelActive();
|
|
- }
|
|
- };
|
|
-
|
|
- ChannelInboundHandlerAdapter clientHandler = new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
|
- try {
|
|
- ReferenceCountUtil.release(msg);
|
|
- } finally {
|
|
- latch.countDown();
|
|
- }
|
|
- }
|
|
- };
|
|
-
|
|
- byte[] response = null;
|
|
- TestClientOcspContext callback = new TestClientOcspContext(true);
|
|
-
|
|
- handshake(sslProvider, latch, serverHandler, response, clientHandler, callback);
|
|
-
|
|
- byte[] actual = callback.response();
|
|
-
|
|
- assertNull(response);
|
|
- assertNull(actual);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testClientExceptionOpenSsl() throws Exception {
|
|
- testClientException(SslProvider.OPENSSL);
|
|
- }
|
|
-
|
|
- @Test(timeout = 10000L)
|
|
- public void testClientExceptionOpenSslRefCnt() throws Exception {
|
|
- testClientException(SslProvider.OPENSSL_REFCNT);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Testing what happens if the {@link OcspClientCallback} throws an {@link Exception}.
|
|
- *
|
|
- * The exception should bubble up on the client side and the connection should get closed.
|
|
- */
|
|
- private void testClientException(SslProvider sslProvider) throws Exception {
|
|
- final AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>();
|
|
- final CountDownLatch latch = new CountDownLatch(1);
|
|
-
|
|
- ChannelInboundHandlerAdapter clientHandler = new ChannelInboundHandlerAdapter() {
|
|
- @Override
|
|
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
- try {
|
|
- causeRef.set(cause);
|
|
- } finally {
|
|
- latch.countDown();
|
|
- }
|
|
- }
|
|
- };
|
|
-
|
|
- final OcspTestException clientException = new OcspTestException("testClientException");
|
|
- byte[] response = newOcspResponse();
|
|
- OcspClientCallback callback = new OcspClientCallback() {
|
|
- @Override
|
|
- public boolean verify(byte[] response) throws Exception {
|
|
- throw clientException;
|
|
- }
|
|
- };
|
|
-
|
|
- handshake(sslProvider, latch, null, response, clientHandler, callback);
|
|
-
|
|
- assertSame(clientException, causeRef.get());
|
|
- }
|
|
-
|
|
- private static void handshake(SslProvider sslProvider, CountDownLatch latch, ChannelHandler serverHandler,
|
|
- byte[] response, ChannelHandler clientHandler, OcspClientCallback callback) throws Exception {
|
|
-
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- try {
|
|
- SslContext serverSslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
|
- .sslProvider(sslProvider)
|
|
- .enableOcsp(true)
|
|
- .build();
|
|
-
|
|
- try {
|
|
- SslContext clientSslContext = SslContextBuilder.forClient()
|
|
- .sslProvider(sslProvider)
|
|
- .enableOcsp(true)
|
|
- .trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
- .build();
|
|
-
|
|
- try {
|
|
- EventLoopGroup group = new DefaultEventLoopGroup();
|
|
- try {
|
|
- LocalAddress address = new LocalAddress("handshake-" + Math.random());
|
|
- Channel server = newServer(group, address, serverSslContext, response, serverHandler);
|
|
- Channel client = newClient(group, address, clientSslContext, callback, clientHandler);
|
|
- try {
|
|
- assertTrue("Something went wrong.", latch.await(10L, TimeUnit.SECONDS));
|
|
- } finally {
|
|
- client.close().syncUninterruptibly();
|
|
- server.close().syncUninterruptibly();
|
|
- }
|
|
- } finally {
|
|
- group.shutdownGracefully(1L, 1L, TimeUnit.SECONDS);
|
|
- }
|
|
- } finally {
|
|
- ReferenceCountUtil.release(clientSslContext);
|
|
- }
|
|
- } finally {
|
|
- ReferenceCountUtil.release(serverSslContext);
|
|
- }
|
|
- } finally {
|
|
- ssc.delete();
|
|
- }
|
|
- }
|
|
-
|
|
- private static Channel newServer(EventLoopGroup group, SocketAddress address,
|
|
- SslContext context, byte[] response, ChannelHandler handler) {
|
|
-
|
|
- ServerBootstrap bootstrap = new ServerBootstrap()
|
|
- .channel(LocalServerChannel.class)
|
|
- .group(group)
|
|
- .childHandler(newServerHandler(context, response, handler));
|
|
-
|
|
- return bootstrap.bind(address)
|
|
- .syncUninterruptibly()
|
|
- .channel();
|
|
- }
|
|
-
|
|
- private static Channel newClient(EventLoopGroup group, SocketAddress address,
|
|
- SslContext context, OcspClientCallback callback, ChannelHandler handler) {
|
|
-
|
|
- Bootstrap bootstrap = new Bootstrap()
|
|
- .channel(LocalChannel.class)
|
|
- .group(group)
|
|
- .handler(newClientHandler(context, callback, handler));
|
|
-
|
|
- return bootstrap.connect(address)
|
|
- .syncUninterruptibly()
|
|
- .channel();
|
|
- }
|
|
-
|
|
- private static ChannelHandler newServerHandler(final SslContext context,
|
|
- final byte[] response, final ChannelHandler handler) {
|
|
- return new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ChannelPipeline pipeline = ch.pipeline();
|
|
- SslHandler sslHandler = context.newHandler(ch.alloc());
|
|
-
|
|
- if (response != null) {
|
|
- ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine();
|
|
- engine.setOcspResponse(response);
|
|
- }
|
|
-
|
|
- pipeline.addLast(sslHandler);
|
|
-
|
|
- if (handler != null) {
|
|
- pipeline.addLast(handler);
|
|
- }
|
|
- }
|
|
- };
|
|
- }
|
|
-
|
|
- private static ChannelHandler newClientHandler(final SslContext context,
|
|
- final OcspClientCallback callback, final ChannelHandler handler) {
|
|
- return new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ChannelPipeline pipeline = ch.pipeline();
|
|
-
|
|
- SslHandler sslHandler = context.newHandler(ch.alloc());
|
|
- ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine();
|
|
-
|
|
- pipeline.addLast(sslHandler);
|
|
- pipeline.addLast(new OcspClientCallbackHandler(engine, callback));
|
|
-
|
|
- if (handler != null) {
|
|
- pipeline.addLast(handler);
|
|
- }
|
|
- }
|
|
- };
|
|
- }
|
|
-
|
|
- private static byte[] newOcspResponse() {
|
|
- // Assume we got the OCSP staple from somewhere. Using a bogus byte[]
|
|
- // in the test because getting a true staple from the CA is quite involved.
|
|
- // It requires HttpCodec and Bouncycastle and the test may be very unreliable
|
|
- // because the OCSP responder servers are basically being DDoS'd by the
|
|
- // Internet.
|
|
-
|
|
- return "I am a bogus OCSP staple. OpenSSL does not care about the format of the byte[]!"
|
|
- .getBytes(CharsetUtil.US_ASCII);
|
|
- }
|
|
-
|
|
- private interface OcspClientCallback {
|
|
- boolean verify(byte[] staple) throws Exception;
|
|
- }
|
|
-
|
|
- private static final class TestClientOcspContext implements OcspClientCallback {
|
|
-
|
|
- private final CountDownLatch latch = new CountDownLatch(1);
|
|
- private final boolean valid;
|
|
-
|
|
- private volatile byte[] response;
|
|
-
|
|
- public TestClientOcspContext(boolean valid) {
|
|
- this.valid = valid;
|
|
- }
|
|
-
|
|
- public byte[] response() throws InterruptedException, TimeoutException {
|
|
- assertTrue(latch.await(10L, TimeUnit.SECONDS));
|
|
- return response;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean verify(byte[] response) throws Exception {
|
|
- this.response = response;
|
|
- latch.countDown();
|
|
-
|
|
- return valid;
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class OcspClientCallbackHandler extends OcspClientHandler {
|
|
-
|
|
- private final OcspClientCallback callback;
|
|
-
|
|
- public OcspClientCallbackHandler(ReferenceCountedOpenSslEngine engine, OcspClientCallback callback) {
|
|
- super(engine);
|
|
- this.callback = callback;
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected boolean verify(ChannelHandlerContext ctx, ReferenceCountedOpenSslEngine engine) throws Exception {
|
|
- byte[] response = engine.getOcspResponse();
|
|
- return callback.verify(response);
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class OcspTestException extends IllegalStateException {
|
|
- public OcspTestException(String message) {
|
|
- super(message);
|
|
- }
|
|
- }
|
|
-}
|
|
--
|
|
2.9.4
|
|
|