4385 lines
181 KiB
Diff
4385 lines
181 KiB
Diff
commit b654e334d8df083dca71f330c6d9d7306ab78649
|
|
Author: Severin Gehwolf <sgehwolf@redhat.com>
|
|
Date: Wed May 20 13:19:37 2015 +0200
|
|
|
|
Remove optional NPN/ALPN/OpenSSL handlers.
|
|
|
|
diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkAlpnApplicationProtocolNegotiator.java
|
|
deleted file mode 100644
|
|
index aaaf5b7..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnApplicationProtocolNegotiator.java
|
|
+++ /dev/null
|
|
@@ -1,120 +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 javax.net.ssl.SSLEngine;
|
|
-
|
|
-/**
|
|
- * The {@link JdkApplicationProtocolNegotiator} to use if you need ALPN and are using {@link SslProvider#JDK}.
|
|
- */
|
|
-public final class JdkAlpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator {
|
|
- private static final SslEngineWrapperFactory ALPN_WRAPPER = new SslEngineWrapperFactory() {
|
|
- {
|
|
- if (!JdkAlpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("ALPN unsupported. Is your classpatch configured correctly?"
|
|
- + " See http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-starting");
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
|
- boolean isServer) {
|
|
- return new JdkAlpnSslEngine(engine, applicationNegotiator, isServer);
|
|
- }
|
|
- };
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(Iterable<String> protocols) {
|
|
- this(false, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(String... protocols) {
|
|
- this(false, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, Iterable<String> protocols) {
|
|
- this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, String... protocols) {
|
|
- this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
|
- * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
|
- boolean serverFailIfNoCommonProtocols, Iterable<String> protocols) {
|
|
- this(serverFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
|
- clientFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
|
- protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
|
- * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
|
- boolean serverFailIfNoCommonProtocols, String... protocols) {
|
|
- this(serverFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
|
- clientFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
|
- protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
|
- * @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
|
- ProtocolSelectionListenerFactory listenerFactory, Iterable<String> protocols) {
|
|
- super(ALPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
|
- * @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkAlpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
|
- ProtocolSelectionListenerFactory listenerFactory, String... protocols) {
|
|
- super(ALPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java
|
|
deleted file mode 100644
|
|
index 6cfacb8..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java
|
|
+++ /dev/null
|
|
@@ -1,117 +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 io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
-import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
|
|
-import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
|
|
-import io.netty.util.internal.PlatformDependent;
|
|
-
|
|
-import java.util.LinkedHashSet;
|
|
-import java.util.List;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-import javax.net.ssl.SSLException;
|
|
-
|
|
-import org.eclipse.jetty.alpn.ALPN;
|
|
-import org.eclipse.jetty.alpn.ALPN.ClientProvider;
|
|
-import org.eclipse.jetty.alpn.ALPN.ServerProvider;
|
|
-
|
|
-final class JdkAlpnSslEngine extends JdkSslEngine {
|
|
- private static boolean available;
|
|
-
|
|
- static boolean isAvailable() {
|
|
- updateAvailability();
|
|
- return available;
|
|
- }
|
|
-
|
|
- private static void updateAvailability() {
|
|
- if (available) {
|
|
- return;
|
|
- }
|
|
-
|
|
- try {
|
|
- // Always use bootstrap class loader.
|
|
- Class.forName("sun.security.ssl.ALPNExtension", true, null);
|
|
- available = true;
|
|
- } catch (Exception ignore) {
|
|
- // alpn-boot was not loaded.
|
|
- }
|
|
- }
|
|
-
|
|
- JdkAlpnSslEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator, boolean server) {
|
|
- super(engine);
|
|
- checkNotNull(applicationNegotiator, "applicationNegotiator");
|
|
-
|
|
- if (server) {
|
|
- final ProtocolSelector protocolSelector = checkNotNull(applicationNegotiator.protocolSelectorFactory()
|
|
- .newSelector(this, new LinkedHashSet<String>(applicationNegotiator.protocols())),
|
|
- "protocolSelector");
|
|
- ALPN.put(engine, new ServerProvider() {
|
|
- @Override
|
|
- public String select(List<String> protocols) {
|
|
- try {
|
|
- return protocolSelector.select(protocols);
|
|
- } catch (Throwable t) {
|
|
- PlatformDependent.throwException(t);
|
|
- return null;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void unsupported() {
|
|
- protocolSelector.unsupported();
|
|
- }
|
|
- });
|
|
- } else {
|
|
- final ProtocolSelectionListener protocolListener = checkNotNull(applicationNegotiator
|
|
- .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()),
|
|
- "protocolListener");
|
|
- ALPN.put(engine, new ClientProvider() {
|
|
- @Override
|
|
- public List<String> protocols() {
|
|
- return applicationNegotiator.protocols();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void selected(String protocol) {
|
|
- try {
|
|
- protocolListener.selected(protocol);
|
|
- } catch (Throwable t) {
|
|
- PlatformDependent.throwException(t);
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void unsupported() {
|
|
- protocolListener.unsupported();
|
|
- }
|
|
- });
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void closeInbound() throws SSLException {
|
|
- ALPN.remove(getWrappedEngine());
|
|
- super.closeInbound();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void closeOutbound() {
|
|
- ALPN.remove(getWrappedEngine());
|
|
- super.closeOutbound();
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkNpnApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkNpnApplicationProtocolNegotiator.java
|
|
deleted file mode 100644
|
|
index c893f05..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/JdkNpnApplicationProtocolNegotiator.java
|
|
+++ /dev/null
|
|
@@ -1,120 +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 javax.net.ssl.SSLEngine;
|
|
-
|
|
-/**
|
|
- * The {@link JdkApplicationProtocolNegotiator} to use if you need NPN and are using {@link SslProvider#JDK}.
|
|
- */
|
|
-public final class JdkNpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator {
|
|
- private static final SslEngineWrapperFactory NPN_WRAPPER = new SslEngineWrapperFactory() {
|
|
- {
|
|
- if (!JdkNpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("NPN unsupported. Is your classpatch configured correctly?"
|
|
- + " See http://www.eclipse.org/jetty/documentation/current/npn-chapter.html#npn-starting");
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
|
- boolean isServer) {
|
|
- return new JdkNpnSslEngine(engine, applicationNegotiator, isServer);
|
|
- }
|
|
- };
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(Iterable<String> protocols) {
|
|
- this(false, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(String... protocols) {
|
|
- this(false, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, Iterable<String> protocols) {
|
|
- this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, String... protocols) {
|
|
- this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
|
- * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
|
- boolean serverFailIfNoCommonProtocols, Iterable<String> protocols) {
|
|
- this(clientFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
|
- serverFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
|
- protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected.
|
|
- * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols,
|
|
- boolean serverFailIfNoCommonProtocols, String... protocols) {
|
|
- this(clientFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY,
|
|
- serverFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY,
|
|
- protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
|
- * @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
|
- ProtocolSelectionListenerFactory listenerFactory, Iterable<String> protocols) {
|
|
- super(NPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Create a new instance.
|
|
- * @param selectorFactory The factory which provides classes responsible for selecting the protocol.
|
|
- * @param listenerFactory The factory which provides to be notified of which protocol was selected.
|
|
- * @param protocols The order of iteration determines the preference of support for protocols.
|
|
- */
|
|
- public JdkNpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory,
|
|
- ProtocolSelectionListenerFactory listenerFactory, String... protocols) {
|
|
- super(NPN_WRAPPER, selectorFactory, listenerFactory, protocols);
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkNpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/JdkNpnSslEngine.java
|
|
deleted file mode 100644
|
|
index 422727a..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/JdkNpnSslEngine.java
|
|
+++ /dev/null
|
|
@@ -1,122 +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 io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
-import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
|
|
-import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
|
|
-import io.netty.util.internal.PlatformDependent;
|
|
-
|
|
-import java.util.LinkedHashSet;
|
|
-import java.util.List;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-import javax.net.ssl.SSLException;
|
|
-
|
|
-import org.eclipse.jetty.npn.NextProtoNego;
|
|
-import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
|
-import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
|
-
|
|
-final class JdkNpnSslEngine extends JdkSslEngine {
|
|
- private static boolean available;
|
|
-
|
|
- static boolean isAvailable() {
|
|
- updateAvailability();
|
|
- return available;
|
|
- }
|
|
-
|
|
- private static void updateAvailability() {
|
|
- if (available) {
|
|
- return;
|
|
- }
|
|
- try {
|
|
- // Always use bootstrap class loader.
|
|
- Class.forName("sun.security.ssl.NextProtoNegoExtension", true, null);
|
|
- available = true;
|
|
- } catch (Exception ignore) {
|
|
- // npn-boot was not loaded.
|
|
- }
|
|
- }
|
|
-
|
|
- JdkNpnSslEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator, boolean server) {
|
|
- super(engine);
|
|
- checkNotNull(applicationNegotiator, "applicationNegotiator");
|
|
-
|
|
- if (server) {
|
|
- final ProtocolSelectionListener protocolListener = checkNotNull(applicationNegotiator
|
|
- .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()),
|
|
- "protocolListener");
|
|
- NextProtoNego.put(engine, new ServerProvider() {
|
|
- @Override
|
|
- public void unsupported() {
|
|
- protocolListener.unsupported();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public List<String> protocols() {
|
|
- return applicationNegotiator.protocols();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void protocolSelected(String protocol) {
|
|
- try {
|
|
- protocolListener.selected(protocol);
|
|
- } catch (Throwable t) {
|
|
- PlatformDependent.throwException(t);
|
|
- }
|
|
- }
|
|
- });
|
|
- } else {
|
|
- final ProtocolSelector protocolSelector = checkNotNull(applicationNegotiator.protocolSelectorFactory()
|
|
- .newSelector(this, new LinkedHashSet<String>(applicationNegotiator.protocols())),
|
|
- "protocolSelector");
|
|
- NextProtoNego.put(engine, new ClientProvider() {
|
|
- @Override
|
|
- public boolean supports() {
|
|
- return true;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void unsupported() {
|
|
- protocolSelector.unsupported();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String selectProtocol(List<String> protocols) {
|
|
- try {
|
|
- return protocolSelector.select(protocols);
|
|
- } catch (Throwable t) {
|
|
- PlatformDependent.throwException(t);
|
|
- return null;
|
|
- }
|
|
- }
|
|
- });
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void closeInbound() throws SSLException {
|
|
- NextProtoNego.remove(getWrappedEngine());
|
|
- super.closeInbound();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void closeOutbound() {
|
|
- NextProtoNego.remove(getWrappedEngine());
|
|
- super.closeOutbound();
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
|
|
index 54ee2be..e3ffb67 100644
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
|
|
+++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
|
|
@@ -220,50 +220,6 @@ public abstract class JdkSslContext extends SslContext {
|
|
switch(config.protocol()) {
|
|
case NONE:
|
|
return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
|
|
- case ALPN:
|
|
- if (isServer) {
|
|
- switch(config.selectorFailureBehavior()) {
|
|
- case FATAL_ALERT:
|
|
- return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
|
- case NO_ADVERTISE:
|
|
- return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
|
- default:
|
|
- throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
|
- .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
|
|
- }
|
|
- } else {
|
|
- switch(config.selectedListenerFailureBehavior()) {
|
|
- case ACCEPT:
|
|
- return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
|
- case FATAL_ALERT:
|
|
- return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
|
- default:
|
|
- throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
|
- .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
|
|
- }
|
|
- }
|
|
- case NPN:
|
|
- if (isServer) {
|
|
- switch(config.selectedListenerFailureBehavior()) {
|
|
- case ACCEPT:
|
|
- return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
|
- case FATAL_ALERT:
|
|
- return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
|
- default:
|
|
- throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
|
- .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString());
|
|
- }
|
|
- } else {
|
|
- switch(config.selectorFailureBehavior()) {
|
|
- case FATAL_ALERT:
|
|
- return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols());
|
|
- case NO_ADVERTISE:
|
|
- return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols());
|
|
- default:
|
|
- throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
|
- .append(config.selectorFailureBehavior()).append(" failure behavior").toString());
|
|
- }
|
|
- }
|
|
default:
|
|
throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ")
|
|
.append(config.protocol()).append(" protocol").toString());
|
|
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 619768a..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java
|
|
+++ /dev/null
|
|
@@ -1,194 +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.NativeLibraryLoader;
|
|
-import io.netty.util.internal.logging.InternalLogger;
|
|
-import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
-import org.apache.tomcat.jni.Library;
|
|
-import org.apache.tomcat.jni.Pool;
|
|
-import org.apache.tomcat.jni.SSL;
|
|
-import org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-import java.util.Collections;
|
|
-import java.util.LinkedHashSet;
|
|
-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 Throwable UNAVAILABILITY_CAUSE;
|
|
-
|
|
- private static final Set<String> AVAILABLE_CIPHER_SUITES;
|
|
-
|
|
- static {
|
|
- Throwable cause = null;
|
|
-
|
|
- // Test if netty-tcnative is in the classpath first.
|
|
- try {
|
|
- Class.forName("org.apache.tomcat.jni.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 {
|
|
- NativeLibraryLoader.load("netty-tcnative", SSL.class.getClassLoader());
|
|
- Library.initialize("provided");
|
|
- SSL.initialize(null);
|
|
- } catch (Throwable t) {
|
|
- cause = t;
|
|
- logger.debug(
|
|
- "Failed to load 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) {
|
|
- final Set<String> availableCipherSuites = new LinkedHashSet<String>(128);
|
|
- final long aprPool = Pool.create(0);
|
|
- try {
|
|
- final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
|
- try {
|
|
- SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL);
|
|
- 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.length() == 0 || availableCipherSuites.contains(c)) {
|
|
- continue;
|
|
- }
|
|
- availableCipherSuites.add(c);
|
|
- }
|
|
- } finally {
|
|
- SSL.freeSSL(ssl);
|
|
- }
|
|
- } finally {
|
|
- SSLContext.free(sslCtx);
|
|
- }
|
|
- } catch (Exception e) {
|
|
- logger.warn("Failed to get the list of available OpenSSL cipher suites.", e);
|
|
- } finally {
|
|
- Pool.destroy(aprPool);
|
|
- }
|
|
-
|
|
- AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites);
|
|
- } else {
|
|
- AVAILABLE_CIPHER_SUITES = Collections.emptySet();
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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 the version of the used available OpenSSL library or {@code -1} if {@link #isAvailable()}
|
|
- * returns {@code false}.
|
|
- */
|
|
- public static int version() {
|
|
- if (isAvailable()) {
|
|
- return SSL.version();
|
|
- }
|
|
- return -1;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the version string of the used available OpenSSL library or {@code null} if {@link #isAvailable()}
|
|
- * returns {@code false}.
|
|
- */
|
|
- public static String versionString() {
|
|
- if (isAvailable()) {
|
|
- return SSL.versionString();
|
|
- }
|
|
- return 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;
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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> availableCipherSuites() {
|
|
- return AVAILABLE_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_CIPHER_SUITES.contains(cipherSuite);
|
|
- }
|
|
-
|
|
- static boolean isError(long errorCode) {
|
|
- return errorCode != SSL.SSL_ERROR_NONE;
|
|
- }
|
|
-
|
|
- private OpenSsl() { }
|
|
-}
|
|
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 85f15d1..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java
|
|
+++ /dev/null
|
|
@@ -1,333 +0,0 @@
|
|
-/*
|
|
- * Copyright 2014 The Netty Project
|
|
- *
|
|
- * The Netty Project licenses this file to you under the Apache License,
|
|
- * version 2.0 (the "License"); you may not use this file except in compliance
|
|
- * with the License. You may obtain a copy of the License at:
|
|
- *
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
- *
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
- * License for the specific language governing permissions and limitations
|
|
- * under the License.
|
|
- */
|
|
-package io.netty.handler.ssl;
|
|
-
|
|
-import io.netty.buffer.ByteBuf;
|
|
-import io.netty.buffer.ByteBufInputStream;
|
|
-import org.apache.tomcat.jni.SSL;
|
|
-import org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-import javax.net.ssl.KeyManagerFactory;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.TrustManager;
|
|
-import javax.net.ssl.TrustManagerFactory;
|
|
-import javax.net.ssl.X509ExtendedTrustManager;
|
|
-import javax.net.ssl.X509TrustManager;
|
|
-import javax.security.auth.x500.X500Principal;
|
|
-import java.io.File;
|
|
-import java.io.IOException;
|
|
-import java.security.KeyStore;
|
|
-import java.security.KeyStoreException;
|
|
-import java.security.NoSuchAlgorithmException;
|
|
-import java.security.cert.CertificateException;
|
|
-import java.security.cert.X509Certificate;
|
|
-
|
|
-/**
|
|
- * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
|
- */
|
|
-public final class OpenSslClientContext extends OpenSslContext {
|
|
- private final OpenSslSessionContext sessionContext;
|
|
-
|
|
- /**
|
|
- * Creates a new instance.
|
|
- */
|
|
- public OpenSslClientContext() throws SSLException {
|
|
- this(null, null, null, null, null, null, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Creates a new instance.
|
|
- *
|
|
- * @param certChainFile an X.509 certificate chain file in PEM format.
|
|
- * {@code null} to use the system default
|
|
- */
|
|
- 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.
|
|
- */
|
|
- 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.
|
|
- */
|
|
- public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
|
- this(certChainFile, trustManagerFactory, null, null, null, null, null,
|
|
- IdentityCipherSuiteFilter.INSTANCE, null, 0, 0);
|
|
- }
|
|
-
|
|
- /**
|
|
- * @deprecated use {@link #OpenSslClientContext(File, TrustManagerFactory, Iterable,
|
|
- * CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
|
|
- *
|
|
- * 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
|
|
- 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);
|
|
- }
|
|
-
|
|
- /**
|
|
- * @deprecated use {@link #OpenSslClientContext(File, TrustManagerFactory, File, File, String,
|
|
- * KeyManagerFactory, Iterable, CipherSuiteFilter, ApplicationProtocolConfig,long, long)}
|
|
- *
|
|
- * 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
|
|
- 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 trustCertChainFile 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 or the results of parsing {@code trustCertChainFile}
|
|
- * @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.
|
|
- */
|
|
- public OpenSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
|
|
- File keyCertChainFile, File keyFile, String keyPassword,
|
|
- KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
|
|
- CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
|
- long sessionCacheSize, long sessionTimeout)
|
|
- throws SSLException {
|
|
- super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT);
|
|
- boolean success = false;
|
|
- try {
|
|
- if (trustCertChainFile != null && !trustCertChainFile.isFile()) {
|
|
- throw new IllegalArgumentException("trustCertChainFile is not a file: " + trustCertChainFile);
|
|
- }
|
|
-
|
|
- if (keyCertChainFile != null && !keyCertChainFile.isFile()) {
|
|
- throw new IllegalArgumentException("keyCertChainFile is not a file: " + keyCertChainFile);
|
|
- }
|
|
-
|
|
- if (keyFile != null && !keyFile.isFile()) {
|
|
- throw new IllegalArgumentException("keyFile is not a file: " + keyFile);
|
|
- }
|
|
- if (keyFile == null && keyCertChainFile != null || keyFile != null && keyCertChainFile == null) {
|
|
- throw new IllegalArgumentException(
|
|
- "Either both keyCertChainFile and keyFile needs to be null or none of them");
|
|
- }
|
|
- synchronized (OpenSslContext.class) {
|
|
- if (trustCertChainFile != null) {
|
|
- /* Load the certificate chain. We must skip the first cert when server mode */
|
|
- if (!SSLContext.setCertificateChainFile(ctx, trustCertChainFile.getPath(), true)) {
|
|
- long error = SSL.getLastErrorNumber();
|
|
- if (OpenSsl.isError(error)) {
|
|
- throw new SSLException(
|
|
- "failed to set certificate chain: "
|
|
- + trustCertChainFile + " (" + SSL.getErrorString(error) + ')');
|
|
- }
|
|
- }
|
|
- }
|
|
- if (keyCertChainFile != null && keyFile != null) {
|
|
- /* Load the certificate file and private key. */
|
|
- try {
|
|
- if (!SSLContext.setCertificate(
|
|
- ctx, keyCertChainFile.getPath(), keyFile.getPath(), keyPassword, SSL.SSL_AIDX_RSA)) {
|
|
- long error = SSL.getLastErrorNumber();
|
|
- if (OpenSsl.isError(error)) {
|
|
- throw new SSLException("failed to set certificate: " +
|
|
- keyCertChainFile + " and " + keyFile +
|
|
- " (" + SSL.getErrorString(error) + ')');
|
|
- }
|
|
- }
|
|
- } catch (SSLException e) {
|
|
- throw e;
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to set certificate: " + keyCertChainFile + " and " + keyFile, e);
|
|
- }
|
|
- }
|
|
-
|
|
- SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
|
|
-
|
|
- try {
|
|
- // Set up trust manager factory to use our key store.
|
|
- if (trustManagerFactory == null) {
|
|
- trustManagerFactory = TrustManagerFactory.getInstance(
|
|
- TrustManagerFactory.getDefaultAlgorithm());
|
|
- }
|
|
- initTrustManagerFactory(trustCertChainFile, trustManagerFactory);
|
|
- final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
|
|
-
|
|
- // Use this to prevent an error when running on java < 7
|
|
- if (useExtendedTrustManager(manager)) {
|
|
- final X509ExtendedTrustManager extendedManager = (X509ExtendedTrustManager) manager;
|
|
- SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
|
|
- @Override
|
|
- void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
|
|
- throws Exception {
|
|
- extendedManager.checkServerTrusted(peerCerts, auth, engine);
|
|
- }
|
|
- });
|
|
- } else {
|
|
- SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
|
|
- @Override
|
|
- void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
|
|
- throws Exception {
|
|
- manager.checkServerTrusted(peerCerts, auth);
|
|
- }
|
|
- });
|
|
- }
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("unable to setup trustmanager", e);
|
|
- }
|
|
- }
|
|
- sessionContext = new OpenSslClientSessionContext(ctx);
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- destroyPools();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private static void initTrustManagerFactory(File certChainFile, TrustManagerFactory trustManagerFactory)
|
|
- throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
|
- KeyStore ks = KeyStore.getInstance("JKS");
|
|
- ks.load(null, null);
|
|
- if (certChainFile != null) {
|
|
- ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
|
- try {
|
|
- for (ByteBuf buf: certs) {
|
|
- X509Certificate cert = (X509Certificate) X509_CERT_FACTORY.generateCertificate(
|
|
- new ByteBufInputStream(buf));
|
|
- X500Principal principal = cert.getSubjectX500Principal();
|
|
- ks.setCertificateEntry(principal.getName("RFC2253"), cert);
|
|
- }
|
|
- } finally {
|
|
- for (ByteBuf buf: certs) {
|
|
- buf.release();
|
|
- }
|
|
- }
|
|
- }
|
|
- trustManagerFactory.init(ks);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public OpenSslSessionContext sessionContext() {
|
|
- return sessionContext;
|
|
- }
|
|
-
|
|
- // No cache is currently supported for client side mode.
|
|
- private static final class OpenSslClientSessionContext extends OpenSslSessionContext {
|
|
- private OpenSslClientSessionContext(long 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;
|
|
- }
|
|
- }
|
|
-}
|
|
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 6cfdc57..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java
|
|
+++ /dev/null
|
|
@@ -1,456 +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 io.netty.util.internal.PlatformDependent;
|
|
-import io.netty.util.internal.SystemPropertyUtil;
|
|
-import io.netty.util.internal.logging.InternalLogger;
|
|
-import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
-import org.apache.tomcat.jni.CertificateVerifier;
|
|
-import org.apache.tomcat.jni.Pool;
|
|
-import org.apache.tomcat.jni.SSL;
|
|
-import org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.SSLHandshakeException;
|
|
-import javax.net.ssl.TrustManager;
|
|
-import javax.net.ssl.X509ExtendedTrustManager;
|
|
-import javax.net.ssl.X509TrustManager;
|
|
-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.atomic.AtomicIntegerFieldUpdater;
|
|
-
|
|
-import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
-import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
|
-import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
|
-
|
|
-public abstract class OpenSslContext extends SslContext {
|
|
-
|
|
- private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslContext.class);
|
|
- /**
|
|
- * To make it easier for users to replace JDK implemention with OpenSsl version we also use
|
|
- * {@code jdk.tls.rejectClientInitiatedRenegotiation} to allow disabling client initiated renegotiation.
|
|
- * Java8+ uses this system property as well.
|
|
- *
|
|
- * 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 =
|
|
- SystemPropertyUtil.getBoolean("jdk.tls.rejectClientInitiatedRenegotiation", false);
|
|
- private static final List<String> DEFAULT_CIPHERS;
|
|
- private static final AtomicIntegerFieldUpdater<OpenSslContext> DESTROY_UPDATER;
|
|
-
|
|
- // TODO: Maybe make configurable ?
|
|
- protected static final int VERIFY_DEPTH = 10;
|
|
-
|
|
- private final long aprPool;
|
|
- @SuppressWarnings({ "unused", "FieldMayBeFinal" })
|
|
- private volatile int aprPoolDestroyed;
|
|
- private volatile boolean rejectRemoteInitiatedRenegotiation;
|
|
- private final List<String> unmodifiableCiphers;
|
|
- private final long sessionCacheSize;
|
|
- private final long sessionTimeout;
|
|
- private final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
|
|
-
|
|
- private final OpenSslApplicationProtocolNegotiator apn;
|
|
- /** The OpenSSL SSL_CTX object */
|
|
- protected final long ctx;
|
|
- private final int mode;
|
|
-
|
|
- 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 SelectorFailureBehavior selectorFailureBehavior() {
|
|
- return SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
|
|
- return SelectedListenerFailureBehavior.ACCEPT;
|
|
- }
|
|
- };
|
|
-
|
|
- static {
|
|
- List<String> ciphers = new ArrayList<String>();
|
|
- // XXX: Make sure to sync this list with JdkSslEngineFactory.
|
|
- Collections.addAll(
|
|
- ciphers,
|
|
- "ECDHE-RSA-AES128-GCM-SHA256",
|
|
- "ECDHE-RSA-AES128-SHA",
|
|
- "ECDHE-RSA-AES256-SHA",
|
|
- "AES128-GCM-SHA256",
|
|
- "AES128-SHA",
|
|
- "AES256-SHA",
|
|
- "DES-CBC3-SHA",
|
|
- "RC4-SHA");
|
|
- DEFAULT_CIPHERS = Collections.unmodifiableList(ciphers);
|
|
-
|
|
- if (logger.isDebugEnabled()) {
|
|
- logger.debug("Default cipher suite (OpenSSL): " + ciphers);
|
|
- }
|
|
-
|
|
- AtomicIntegerFieldUpdater<OpenSslContext> updater =
|
|
- PlatformDependent.newAtomicIntegerFieldUpdater(OpenSslContext.class, "aprPoolDestroyed");
|
|
- if (updater == null) {
|
|
- updater = AtomicIntegerFieldUpdater.newUpdater(OpenSslContext.class, "aprPoolDestroyed");
|
|
- }
|
|
- DESTROY_UPDATER = updater;
|
|
- }
|
|
-
|
|
- OpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg,
|
|
- long sessionCacheSize, long sessionTimeout, int mode) throws SSLException {
|
|
- this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode);
|
|
- }
|
|
-
|
|
- OpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
|
|
- OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize,
|
|
- long sessionTimeout, int mode) throws SSLException {
|
|
- OpenSsl.ensureAvailability();
|
|
-
|
|
- 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");
|
|
- }
|
|
- this.mode = mode;
|
|
-
|
|
- if (mode == SSL.SSL_MODE_SERVER) {
|
|
- rejectRemoteInitiatedRenegotiation =
|
|
- JDK_REJECT_CLIENT_INITIATED_RENEGOTIATION;
|
|
- }
|
|
- 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.availableCipherSuites()));
|
|
-
|
|
- this.apn = checkNotNull(apn, "apn");
|
|
-
|
|
- // Allocate a new APR pool.
|
|
- aprPool = Pool.create(0);
|
|
-
|
|
- // Create a new SSL_CTX and configure it.
|
|
- boolean success = false;
|
|
- try {
|
|
- synchronized (OpenSslContext.class) {
|
|
- try {
|
|
- ctx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, mode);
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to create an SSL_CTX", e);
|
|
- }
|
|
-
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_ALL);
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_NO_SSLv2);
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_NO_SSLv3);
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_SINGLE_ECDH_USE);
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_SINGLE_DH_USE);
|
|
- SSLContext.setOptions(ctx, SSL.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
|
-
|
|
- /* 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[] protocols = nextProtoList.toArray(new String[nextProtoList.size()]);
|
|
- int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
|
|
-
|
|
- switch (apn.protocol()) {
|
|
- case NPN:
|
|
- SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
|
|
- break;
|
|
- case ALPN:
|
|
- SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
|
|
- break;
|
|
- case NPN_AND_ALPN:
|
|
- SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
|
|
- SSLContext.setAlpnProtos(ctx, protocols, 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);
|
|
- }
|
|
- }
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- destroyPools();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private static int opensslSelectorFailureBehavior(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) {
|
|
- throw new UnsupportedOperationException();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns a new server-side {@link javax.net.ssl.SSLEngine} with the current configuration.
|
|
- */
|
|
- @Override
|
|
- public final SSLEngine newEngine(ByteBufAllocator alloc) {
|
|
- final OpenSslEngine engine = new OpenSslEngine(
|
|
- ctx, alloc, isClient(), sessionContext(), apn, engineMap, rejectRemoteInitiatedRenegotiation);
|
|
- engineMap.add(engine);
|
|
- return engine;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the {@code SSL_CTX} object of this context.
|
|
- */
|
|
- public final long context() {
|
|
- return ctx;
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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;
|
|
- }
|
|
-
|
|
- @Override
|
|
- @SuppressWarnings("FinalizeDeclaration")
|
|
- protected final void finalize() throws Throwable {
|
|
- super.finalize();
|
|
- synchronized (OpenSslContext.class) {
|
|
- if (ctx != 0) {
|
|
- SSLContext.free(ctx);
|
|
- }
|
|
- }
|
|
-
|
|
- destroyPools();
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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();
|
|
-
|
|
- protected final void destroyPools() {
|
|
- // Guard against multiple destroyPools() calls triggered by construction exception and finalize() later
|
|
- if (aprPool != 0 && DESTROY_UPDATER.compareAndSet(this, 0, 1)) {
|
|
- Pool.destroy(aprPool);
|
|
- }
|
|
- }
|
|
-
|
|
- 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");
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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;
|
|
- }
|
|
-
|
|
- abstract class AbstractCertificateVerifier implements CertificateVerifier {
|
|
- @Override
|
|
- public final boolean verify(long ssl, byte[][] chain, String auth) {
|
|
- X509Certificate[] peerCerts = certificates(chain);
|
|
- final OpenSslEngine engine = engineMap.remove(ssl);
|
|
- try {
|
|
- verify(engine, peerCerts, auth);
|
|
- return true;
|
|
- } catch (Throwable cause) {
|
|
- logger.debug("verification of certificate failed", cause);
|
|
- SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
|
|
- e.initCause(cause);
|
|
- engine.handshakeException = e;
|
|
- }
|
|
- return false;
|
|
- }
|
|
-
|
|
- abstract void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth) throws Exception;
|
|
- }
|
|
-
|
|
- private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
|
|
- private final Map<Long, OpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
|
|
- @Override
|
|
- public OpenSslEngine remove(long ssl) {
|
|
- return engines.remove(ssl);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void add(OpenSslEngine engine) {
|
|
- engines.put(engine.ssl(), engine);
|
|
- }
|
|
- }
|
|
-}
|
|
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 b5c8e6c..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java
|
|
+++ /dev/null
|
|
@@ -1,1525 +0,0 @@
|
|
-/*
|
|
- * Copyright 2014 The Netty Project
|
|
- *
|
|
- * The Netty Project licenses this file to you under the Apache License,
|
|
- * version 2.0 (the "License"); you may not use this file except in compliance
|
|
- * with the License. You may obtain a copy of the License at:
|
|
- *
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
- *
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
- * License for the specific language governing permissions and limitations
|
|
- * under the License.
|
|
- */
|
|
-package io.netty.handler.ssl;
|
|
-
|
|
-import io.netty.buffer.ByteBuf;
|
|
-import io.netty.buffer.ByteBufAllocator;
|
|
-import io.netty.buffer.Unpooled;
|
|
-import io.netty.util.internal.EmptyArrays;
|
|
-import io.netty.util.internal.PlatformDependent;
|
|
-import io.netty.util.internal.StringUtil;
|
|
-import io.netty.util.internal.logging.InternalLogger;
|
|
-import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
-import org.apache.tomcat.jni.Buffer;
|
|
-import org.apache.tomcat.jni.SSL;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-import javax.net.ssl.SSLEngineResult;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.SSLHandshakeException;
|
|
-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.CertificateException;
|
|
-import javax.security.cert.X509Certificate;
|
|
-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.HashMap;
|
|
-import java.util.HashSet;
|
|
-import java.util.List;
|
|
-import java.util.Map;
|
|
-import java.util.Set;
|
|
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
|
-
|
|
-import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
|
-import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
-import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
|
|
-import static javax.net.ssl.SSLEngineResult.Status.*;
|
|
-
|
|
-/**
|
|
- * Implements a {@link SSLEngine} using
|
|
- * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL BIO abstractions</a>.
|
|
- */
|
|
-public final class OpenSslEngine extends SSLEngine {
|
|
-
|
|
- private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslEngine.class);
|
|
-
|
|
- private static final Certificate[] EMPTY_CERTIFICATES = EmptyArrays.EMPTY_CERTIFICATES;
|
|
- private static final X509Certificate[] EMPTY_X509_CERTIFICATES = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES;
|
|
-
|
|
- private static final SSLException ENGINE_CLOSED = new SSLException("engine closed");
|
|
- private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException("renegotiation unsupported");
|
|
- private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException("encrypted packet oversized");
|
|
- static {
|
|
- ENGINE_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
|
- RENEGOTIATION_UNSUPPORTED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
|
- ENCRYPTED_PACKET_OVERSIZED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
|
-
|
|
- AtomicIntegerFieldUpdater<OpenSslEngine> destroyedUpdater =
|
|
- PlatformDependent.newAtomicIntegerFieldUpdater(OpenSslEngine.class, "destroyed");
|
|
- if (destroyedUpdater == null) {
|
|
- destroyedUpdater = AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
|
|
- }
|
|
- DESTROYED_UPDATER = destroyedUpdater;
|
|
- }
|
|
-
|
|
- 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;
|
|
-
|
|
- // Protocols
|
|
- private static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
|
|
- private static final String PROTOCOL_SSL_V2 = "SSLv2";
|
|
- private static final String PROTOCOL_SSL_V3 = "SSLv3";
|
|
- private static final String PROTOCOL_TLS_V1 = "TLSv1";
|
|
- private static final String PROTOCOL_TLS_V1_1 = "TLSv1.1";
|
|
- private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
|
|
-
|
|
- private static final String[] SUPPORTED_PROTOCOLS = {
|
|
- PROTOCOL_SSL_V2_HELLO,
|
|
- PROTOCOL_SSL_V2,
|
|
- PROTOCOL_SSL_V3,
|
|
- PROTOCOL_TLS_V1,
|
|
- PROTOCOL_TLS_V1_1,
|
|
- PROTOCOL_TLS_V1_2
|
|
- };
|
|
- private static final Set<String> SUPPORTED_PROTOCOLS_SET = new HashSet<String>(Arrays.asList(SUPPORTED_PROTOCOLS));
|
|
-
|
|
- // 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;
|
|
-
|
|
- enum ClientAuthMode {
|
|
- NONE,
|
|
- OPTIONAL,
|
|
- REQUIRE,
|
|
- }
|
|
-
|
|
- private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER;
|
|
-
|
|
- private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
|
|
-
|
|
- private static final long EMPTY_ADDR = Buffer.address(Unpooled.EMPTY_BUFFER.nioBuffer());
|
|
-
|
|
- 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;
|
|
-
|
|
- /**
|
|
- * 0 - not accepted, 1 - accepted implicitly via wrap()/unwrap(), 2 - accepted explicitly via beginHandshake() call
|
|
- */
|
|
- private int accepted;
|
|
- private boolean handshakeFinished;
|
|
- private boolean receivedShutdown;
|
|
- @SuppressWarnings("UnusedDeclaration")
|
|
- private volatile int destroyed;
|
|
-
|
|
- // Use an invalid cipherSuite until the handshake is completed
|
|
- // See http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html#getSession()
|
|
- private volatile String cipher;
|
|
- private volatile String applicationProtocol;
|
|
-
|
|
- // We store this outside of the SslSession so we not need to create an instance during verifyCertificates(...)
|
|
- private volatile Certificate[] peerCerts;
|
|
- private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
|
|
-
|
|
- // SSL Engine status variables
|
|
- private boolean isInboundDone;
|
|
- private boolean isOutboundDone;
|
|
- private boolean engineClosed;
|
|
-
|
|
- private final boolean clientMode;
|
|
- private final ByteBufAllocator alloc;
|
|
- private final OpenSslSessionContext sessionContext;
|
|
- private final OpenSslEngineMap engineMap;
|
|
- private final OpenSslApplicationProtocolNegotiator apn;
|
|
- private final boolean rejectRemoteInitiatedRenegation;
|
|
- private final SSLSession session = new OpenSslSession();
|
|
-
|
|
- // This is package-private as we set it from OpenSslContext if an exception is thrown during
|
|
- // the verification step.
|
|
- SSLHandshakeException handshakeException;
|
|
-
|
|
- /**
|
|
- * Creates a new instance
|
|
- *
|
|
- * @param sslCtx an OpenSSL {@code SSL_CTX} object
|
|
- * @param alloc the {@link ByteBufAllocator} that will be used by this engine
|
|
- */
|
|
- @Deprecated
|
|
- public OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
|
|
- @SuppressWarnings("unused") String fallbackApplicationProtocol) {
|
|
- this(sslCtx, alloc, false, null, OpenSslContext.NONE_PROTOCOL_NEGOTIATOR, OpenSslEngineMap.EMPTY, false);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Creates a new instance
|
|
- *
|
|
- * @param sslCtx an OpenSSL {@code SSL_CTX} object
|
|
- * @param alloc the {@link ByteBufAllocator} that will be used by this engine
|
|
- * @param clientMode {@code true} if this is used for clients, {@code false} otherwise
|
|
- * @param sessionContext the {@link OpenSslSessionContext} this {@link SSLEngine} belongs to.
|
|
- */
|
|
- OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
|
|
- boolean clientMode, OpenSslSessionContext sessionContext,
|
|
- OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap,
|
|
- boolean rejectRemoteInitiatedRenegation) {
|
|
- OpenSsl.ensureAvailability();
|
|
- if (sslCtx == 0) {
|
|
- throw new NullPointerException("sslCtx");
|
|
- }
|
|
-
|
|
- this.alloc = checkNotNull(alloc, "alloc");
|
|
- this.apn = checkNotNull(apn, "apn");
|
|
- ssl = SSL.newSSL(sslCtx, !clientMode);
|
|
- networkBIO = SSL.makeNetworkBIO(ssl);
|
|
- this.clientMode = clientMode;
|
|
- this.sessionContext = sessionContext;
|
|
- this.engineMap = engineMap;
|
|
- this.rejectRemoteInitiatedRenegation = rejectRemoteInitiatedRenegation;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLSession getHandshakeSession() {
|
|
- if (accepted > 0) {
|
|
- // handshake started we are able to return the session.
|
|
- return session;
|
|
- }
|
|
- // As stated by the javadocs of getHandshakeSession() we should return null if the handshake not started yet.
|
|
- return null;
|
|
- }
|
|
-
|
|
- long ssl() {
|
|
- return ssl;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Destroys this engine.
|
|
- */
|
|
- public synchronized void shutdown() {
|
|
- if (DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
|
|
- engineMap.remove(ssl);
|
|
- SSL.freeSSL(ssl);
|
|
- SSL.freeBIO(networkBIO);
|
|
- ssl = networkBIO = 0;
|
|
-
|
|
- // internal errors can cause shutdown without marking the engine closed
|
|
- isInboundDone = isOutboundDone = engineClosed = true;
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Write plaintext data to the OpenSSL internal BIO
|
|
- *
|
|
- * Calling this function with src.remaining == 0 is undefined.
|
|
- */
|
|
- private int writePlaintextData(final ByteBuffer src) {
|
|
- final int pos = src.position();
|
|
- final int limit = src.limit();
|
|
- final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
|
|
- final int sslWrote;
|
|
-
|
|
- if (src.isDirect()) {
|
|
- final long addr = Buffer.address(src) + pos;
|
|
- sslWrote = SSL.writeToSSL(ssl, addr, len);
|
|
- if (sslWrote > 0) {
|
|
- src.position(pos + sslWrote);
|
|
- return sslWrote;
|
|
- }
|
|
- } else {
|
|
- ByteBuf buf = alloc.directBuffer(len);
|
|
- try {
|
|
- final long addr = memoryAddress(buf);
|
|
-
|
|
- src.limit(pos + len);
|
|
-
|
|
- buf.setBytes(0, src);
|
|
- src.limit(limit);
|
|
-
|
|
- sslWrote = SSL.writeToSSL(ssl, addr, len);
|
|
- if (sslWrote > 0) {
|
|
- src.position(pos + sslWrote);
|
|
- return sslWrote;
|
|
- } else {
|
|
- src.position(pos);
|
|
- }
|
|
- } finally {
|
|
- buf.release();
|
|
- }
|
|
- }
|
|
-
|
|
- throw new IllegalStateException("SSL.writeToSSL() returned a non-positive value: " + sslWrote);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Write encrypted data to the OpenSSL network BIO.
|
|
- */
|
|
- private int writeEncryptedData(final ByteBuffer src) {
|
|
- final int pos = src.position();
|
|
- final int len = src.remaining();
|
|
- if (src.isDirect()) {
|
|
- final long addr = Buffer.address(src) + pos;
|
|
- final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
|
|
- if (netWrote >= 0) {
|
|
- src.position(pos + netWrote);
|
|
- return netWrote;
|
|
- }
|
|
- } else {
|
|
- final ByteBuf buf = alloc.directBuffer(len);
|
|
- try {
|
|
- final long addr = memoryAddress(buf);
|
|
-
|
|
- buf.setBytes(0, src);
|
|
-
|
|
- final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
|
|
- if (netWrote >= 0) {
|
|
- src.position(pos + netWrote);
|
|
- return netWrote;
|
|
- } else {
|
|
- src.position(pos);
|
|
- }
|
|
- } finally {
|
|
- buf.release();
|
|
- }
|
|
- }
|
|
-
|
|
- return -1;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Read plaintext data from the OpenSSL internal BIO
|
|
- */
|
|
- private int readPlaintextData(final ByteBuffer dst) {
|
|
- if (dst.isDirect()) {
|
|
- final int pos = dst.position();
|
|
- final long addr = Buffer.address(dst) + pos;
|
|
- final int len = dst.limit() - pos;
|
|
- final int sslRead = SSL.readFromSSL(ssl, addr, len);
|
|
- if (sslRead > 0) {
|
|
- dst.position(pos + sslRead);
|
|
- return sslRead;
|
|
- }
|
|
- } else {
|
|
- final int pos = dst.position();
|
|
- final int limit = dst.limit();
|
|
- final int len = Math.min(MAX_ENCRYPTED_PACKET_LENGTH, limit - pos);
|
|
- final ByteBuf buf = alloc.directBuffer(len);
|
|
- try {
|
|
- final long addr = memoryAddress(buf);
|
|
-
|
|
- final int sslRead = SSL.readFromSSL(ssl, addr, len);
|
|
- if (sslRead > 0) {
|
|
- dst.limit(pos + sslRead);
|
|
- buf.getBytes(0, dst);
|
|
- dst.limit(limit);
|
|
- return sslRead;
|
|
- }
|
|
- } finally {
|
|
- buf.release();
|
|
- }
|
|
- }
|
|
-
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Read encrypted data from the OpenSSL network BIO
|
|
- */
|
|
- private int readEncryptedData(final ByteBuffer dst, final int pending) {
|
|
- if (dst.isDirect() && dst.remaining() >= pending) {
|
|
- final int pos = dst.position();
|
|
- final long addr = Buffer.address(dst) + pos;
|
|
- final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
|
|
- if (bioRead > 0) {
|
|
- dst.position(pos + bioRead);
|
|
- return bioRead;
|
|
- }
|
|
- } else {
|
|
- final ByteBuf buf = alloc.directBuffer(pending);
|
|
- try {
|
|
- final long addr = memoryAddress(buf);
|
|
-
|
|
- final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
|
|
- if (bioRead > 0) {
|
|
- int oldLimit = dst.limit();
|
|
- dst.limit(dst.position() + bioRead);
|
|
- buf.getBytes(0, dst);
|
|
- dst.limit(oldLimit);
|
|
- return bioRead;
|
|
- }
|
|
- } finally {
|
|
- buf.release();
|
|
- }
|
|
- }
|
|
-
|
|
- return 0;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public synchronized SSLEngineResult wrap(
|
|
- final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
|
|
-
|
|
- // Check to make sure the engine has not been closed
|
|
- if (destroyed != 0) {
|
|
- return CLOSED_NOT_HANDSHAKING;
|
|
- }
|
|
-
|
|
- // 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();
|
|
- }
|
|
-
|
|
- // Prepare OpenSSL to work in server mode and receive handshake
|
|
- if (accepted == 0) {
|
|
- beginHandshakeImplicitly();
|
|
- }
|
|
-
|
|
- // In handshake or close_notify stages, check if call to wrap was made
|
|
- // without regard to the handshake status.
|
|
- SSLEngineResult.HandshakeStatus handshakeStatus = handshakeStatus0();
|
|
-
|
|
- if (handshakeStatus == NEED_UNWRAP) {
|
|
- if (!handshakeFinished) {
|
|
- return NEED_UNWRAP_OK;
|
|
- }
|
|
- if (engineClosed) {
|
|
- return NEED_UNWRAP_CLOSED;
|
|
- }
|
|
- }
|
|
-
|
|
- int bytesProduced = 0;
|
|
- int pendingNet;
|
|
-
|
|
- // Check for pending data in the network BIO
|
|
- pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
|
|
- if (pendingNet > 0) {
|
|
- // Do we have enough room in dst to write encrypted data?
|
|
- int capacity = dst.remaining();
|
|
- if (capacity < pendingNet) {
|
|
- return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus, 0, bytesProduced);
|
|
- }
|
|
-
|
|
- // Write the pending data from the network BIO into the dst buffer
|
|
- try {
|
|
- bytesProduced += readEncryptedData(dst, pendingNet);
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- }
|
|
-
|
|
- // If isOuboundDone is set, then the data from the network BIO
|
|
- // was the close_notify message -- we are not required to wait
|
|
- // for the receipt the peer's close_notify message -- shutdown.
|
|
- if (isOutboundDone) {
|
|
- shutdown();
|
|
- }
|
|
-
|
|
- return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), 0, bytesProduced);
|
|
- }
|
|
-
|
|
- // There was no pending data in the network BIO -- encrypt any application data
|
|
- int bytesConsumed = 0;
|
|
- 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");
|
|
- }
|
|
- while (src.hasRemaining()) {
|
|
-
|
|
- // Write plaintext application data to the SSL engine
|
|
- try {
|
|
- bytesConsumed += writePlaintextData(src);
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- }
|
|
-
|
|
- // Check to see if the engine wrote data into the network BIO
|
|
- pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
|
|
- if (pendingNet > 0) {
|
|
- // Do we have enough room in dst to write encrypted data?
|
|
- int capacity = dst.remaining();
|
|
- if (capacity < pendingNet) {
|
|
- return new SSLEngineResult(
|
|
- BUFFER_OVERFLOW, handshakeStatus0(), bytesConsumed, bytesProduced);
|
|
- }
|
|
-
|
|
- // Write the pending data from the network BIO into the dst buffer
|
|
- try {
|
|
- bytesProduced += readEncryptedData(dst, pendingNet);
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- }
|
|
-
|
|
- return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), bytesConsumed, bytesProduced);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), bytesConsumed, bytesProduced);
|
|
- }
|
|
-
|
|
- private SSLException newSSLException(String msg) {
|
|
- if (!handshakeFinished) {
|
|
- return new SSLHandshakeException(msg);
|
|
- }
|
|
- return new SSLException(msg);
|
|
- }
|
|
-
|
|
- private void checkPendingHandshakeException() throws SSLHandshakeException {
|
|
- if (handshakeException != null) {
|
|
- SSLHandshakeException exception = handshakeException;
|
|
- handshakeException = null;
|
|
- shutdown();
|
|
- throw exception;
|
|
- }
|
|
- }
|
|
-
|
|
- public synchronized SSLEngineResult unwrap(
|
|
- final ByteBuffer[] srcs, int srcsOffset, final int srcsLength,
|
|
- final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength) throws SSLException {
|
|
-
|
|
- // Check to make sure the engine has not been closed
|
|
- if (destroyed != 0) {
|
|
- return CLOSED_NOT_HANDSHAKING;
|
|
- }
|
|
-
|
|
- // Throw requried 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 + "))");
|
|
- }
|
|
- int capacity = 0;
|
|
- final int endOffset = dstsOffset + dstsLength;
|
|
- for (int i = dstsOffset; i < endOffset; i ++) {
|
|
- ByteBuffer dst = dsts[i];
|
|
- if (dst == null) {
|
|
- throw new IllegalArgumentException("dsts[" + i + "] is null");
|
|
- }
|
|
- if (dst.isReadOnly()) {
|
|
- throw new ReadOnlyBufferException();
|
|
- }
|
|
- capacity += dst.remaining();
|
|
- }
|
|
-
|
|
- // Prepare OpenSSL to work in server mode and receive handshake
|
|
- if (accepted == 0) {
|
|
- beginHandshakeImplicitly();
|
|
- }
|
|
-
|
|
- // In handshake or close_notify stages, check if call to unwrap was made
|
|
- // without regard to the handshake status.
|
|
- SSLEngineResult.HandshakeStatus handshakeStatus = handshakeStatus0();
|
|
- if (handshakeStatus == NEED_WRAP) {
|
|
- if (!handshakeFinished) {
|
|
- return NEED_WRAP_OK;
|
|
- }
|
|
- if (engineClosed) {
|
|
- return NEED_WRAP_CLOSED;
|
|
- }
|
|
- }
|
|
-
|
|
- final int srcsEndOffset = srcsOffset + srcsLength;
|
|
- int 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();
|
|
- }
|
|
-
|
|
- // protect against protocol overflow attack vector
|
|
- if (len > MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
- isInboundDone = true;
|
|
- isOutboundDone = true;
|
|
- engineClosed = true;
|
|
- shutdown();
|
|
- throw ENCRYPTED_PACKET_OVERSIZED;
|
|
- }
|
|
-
|
|
- // Write encrypted data to network BIO
|
|
- int bytesConsumed = -1;
|
|
- try {
|
|
- while (srcsOffset < srcsEndOffset) {
|
|
- ByteBuffer src = srcs[srcsOffset];
|
|
- int remaining = src.remaining();
|
|
- int written = writeEncryptedData(src);
|
|
- if (written >= 0) {
|
|
- if (bytesConsumed == -1) {
|
|
- bytesConsumed = written;
|
|
- } else {
|
|
- bytesConsumed += written;
|
|
- }
|
|
- if (written == remaining) {
|
|
- srcsOffset ++;
|
|
- } else if (written == 0) {
|
|
- break;
|
|
- }
|
|
- } else {
|
|
- break;
|
|
- }
|
|
- }
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- }
|
|
- if (bytesConsumed >= 0) {
|
|
- int lastPrimingReadResult = SSL.readFromSSL(ssl, EMPTY_ADDR, 0); // priming read
|
|
-
|
|
- // check if SSL_read returned <= 0. In this case we need to check the error and see if it was something
|
|
- // fatal.
|
|
- if (lastPrimingReadResult <= 0) {
|
|
- // Check for OpenSSL errors caused by the priming read
|
|
- long error = SSL.getLastErrorNumber();
|
|
- if (OpenSsl.isError(error)) {
|
|
- String err = SSL.getErrorString(error);
|
|
- if (logger.isDebugEnabled()) {
|
|
- logger.debug(
|
|
- "SSL_read failed: primingReadResult: " + lastPrimingReadResult +
|
|
- "; OpenSSL error: '" + err + '\'');
|
|
- }
|
|
-
|
|
- // There was an internal error -- shutdown
|
|
- shutdown();
|
|
- throw newSSLException(err);
|
|
- } else {
|
|
- checkPendingHandshakeException();
|
|
- }
|
|
- }
|
|
-
|
|
- rejectRemoteInitiatedRenegation();
|
|
- } else {
|
|
- // Reset to 0 as -1 is used to signal that nothing was written and no priming read needs to be done
|
|
- bytesConsumed = 0;
|
|
- }
|
|
-
|
|
- // There won't be any application data until we're done handshaking
|
|
- //
|
|
- // We first check handshakeFinished to eliminate the overhead of extra JNI call if possible.
|
|
- int pendingApp = (handshakeFinished || SSL.isInInit(ssl) == 0) ? SSL.pendingReadableBytesInSSL(ssl) : 0;
|
|
- int bytesProduced = 0;
|
|
-
|
|
- if (pendingApp > 0) {
|
|
- // Do we have enough room in dsts to write decrypted data?
|
|
- if (capacity < pendingApp) {
|
|
- return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus0(), bytesConsumed, 0);
|
|
- }
|
|
-
|
|
- // Write decrypted data to dsts buffers
|
|
- int idx = dstsOffset;
|
|
- while (idx < endOffset) {
|
|
- ByteBuffer dst = dsts[idx];
|
|
- if (!dst.hasRemaining()) {
|
|
- idx ++;
|
|
- continue;
|
|
- }
|
|
-
|
|
- if (pendingApp <= 0) {
|
|
- break;
|
|
- }
|
|
-
|
|
- int bytesRead;
|
|
- try {
|
|
- bytesRead = readPlaintextData(dst);
|
|
- } catch (Exception e) {
|
|
- throw new SSLException(e);
|
|
- }
|
|
-
|
|
- rejectRemoteInitiatedRenegation();
|
|
-
|
|
- if (bytesRead == 0) {
|
|
- break;
|
|
- }
|
|
- bytesProduced += bytesRead;
|
|
- pendingApp -= bytesRead;
|
|
-
|
|
- if (!dst.hasRemaining()) {
|
|
- idx ++;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- // 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) {
|
|
- receivedShutdown = true;
|
|
- closeOutbound();
|
|
- closeInbound();
|
|
- }
|
|
-
|
|
- return new SSLEngineResult(getEngineStatus(), handshakeStatus0(), bytesConsumed, bytesProduced);
|
|
- }
|
|
-
|
|
- private void rejectRemoteInitiatedRenegation() throws SSLHandshakeException {
|
|
- if (rejectRemoteInitiatedRenegation && 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 renegotation not allowed");
|
|
- }
|
|
- }
|
|
-
|
|
- public SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException {
|
|
- return unwrap(srcs, 0, srcs.length, dsts, 0, dsts.length);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLEngineResult unwrap(
|
|
- final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException {
|
|
- return unwrap(new ByteBuffer[] { src }, 0, 1, dsts, offset, length);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public 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 synchronized void closeInbound() throws SSLException {
|
|
- if (isInboundDone) {
|
|
- return;
|
|
- }
|
|
-
|
|
- isInboundDone = true;
|
|
- engineClosed = true;
|
|
-
|
|
- shutdown();
|
|
-
|
|
- if (accepted != 0 && !receivedShutdown) {
|
|
- throw new SSLException(
|
|
- "Inbound closed before receiving peer's close_notify: possible truncation attack?");
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public synchronized boolean isInboundDone() {
|
|
- return isInboundDone || engineClosed;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public synchronized void closeOutbound() {
|
|
- if (isOutboundDone) {
|
|
- return;
|
|
- }
|
|
-
|
|
- isOutboundDone = true;
|
|
- engineClosed = true;
|
|
-
|
|
- if (accepted != 0 && destroyed == 0) {
|
|
- int mode = SSL.getShutdown(ssl);
|
|
- if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
|
|
- SSL.shutdownSSL(ssl);
|
|
- }
|
|
- } else {
|
|
- // engine closing before initial handshake
|
|
- shutdown();
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public synchronized boolean isOutboundDone() {
|
|
- return isOutboundDone;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String[] getSupportedCipherSuites() {
|
|
- Set<String> availableCipherSuites = OpenSsl.availableCipherSuites();
|
|
- return availableCipherSuites.toArray(new String[availableCipherSuites.size()]);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String[] getEnabledCipherSuites() {
|
|
- final String[] enabled;
|
|
- synchronized (this) {
|
|
- if (destroyed == 0) {
|
|
- enabled = SSL.getCiphers(ssl);
|
|
- } else {
|
|
- return EmptyArrays.EMPTY_STRINGS;
|
|
- }
|
|
- }
|
|
- if (enabled == null) {
|
|
- return EmptyArrays.EMPTY_STRINGS;
|
|
- } else {
|
|
- for (int i = 0; i < enabled.length; i++) {
|
|
- String mapped = toJavaCipherSuite(enabled[i]);
|
|
- if (mapped != null) {
|
|
- enabled[i] = mapped;
|
|
- }
|
|
- }
|
|
- return enabled;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public 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 (destroyed == 0) {
|
|
- 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 String[] getSupportedProtocols() {
|
|
- return SUPPORTED_PROTOCOLS.clone();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String[] getEnabledProtocols() {
|
|
- List<String> enabled = new ArrayList<String>();
|
|
- // Seems like there is no way to explict disable SSLv2Hello in openssl so it is always enabled
|
|
- enabled.add(PROTOCOL_SSL_V2_HELLO);
|
|
-
|
|
- int opts;
|
|
- synchronized (this) {
|
|
- if (destroyed == 0) {
|
|
- opts = SSL.getOptions(ssl);
|
|
- } else {
|
|
- return enabled.toArray(new String[1]);
|
|
- }
|
|
- }
|
|
- if ((opts & SSL.SSL_OP_NO_TLSv1) == 0) {
|
|
- enabled.add(PROTOCOL_TLS_V1);
|
|
- }
|
|
- if ((opts & SSL.SSL_OP_NO_TLSv1_1) == 0) {
|
|
- enabled.add(PROTOCOL_TLS_V1_1);
|
|
- }
|
|
- if ((opts & SSL.SSL_OP_NO_TLSv1_2) == 0) {
|
|
- enabled.add(PROTOCOL_TLS_V1_2);
|
|
- }
|
|
- if ((opts & SSL.SSL_OP_NO_SSLv2) == 0) {
|
|
- enabled.add(PROTOCOL_SSL_V2);
|
|
- }
|
|
- if ((opts & SSL.SSL_OP_NO_SSLv3) == 0) {
|
|
- enabled.add(PROTOCOL_SSL_V3);
|
|
- }
|
|
- return enabled.toArray(new String[enabled.size()]);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public 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 (!SUPPORTED_PROTOCOLS_SET.contains(p)) {
|
|
- throw new IllegalArgumentException("Protocol " + p + " is not supported.");
|
|
- }
|
|
- if (p.equals(PROTOCOL_SSL_V2)) {
|
|
- sslv2 = true;
|
|
- } else if (p.equals(PROTOCOL_SSL_V3)) {
|
|
- sslv3 = true;
|
|
- } else if (p.equals(PROTOCOL_TLS_V1)) {
|
|
- tlsv1 = true;
|
|
- } else if (p.equals(PROTOCOL_TLS_V1_1)) {
|
|
- tlsv1_1 = true;
|
|
- } else if (p.equals(PROTOCOL_TLS_V1_2)) {
|
|
- tlsv1_2 = true;
|
|
- }
|
|
- }
|
|
- synchronized (this) {
|
|
- if (destroyed == 0) {
|
|
- // Enable all and then disable what we not want
|
|
- SSL.setOptions(ssl, SSL.SSL_OP_ALL);
|
|
-
|
|
- if (!sslv2) {
|
|
- SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv2);
|
|
- }
|
|
- if (!sslv3) {
|
|
- SSL.setOptions(ssl, SSL.SSL_OP_NO_SSLv3);
|
|
- }
|
|
- if (!tlsv1) {
|
|
- SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1);
|
|
- }
|
|
- if (!tlsv1_1) {
|
|
- SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_1);
|
|
- }
|
|
- if (!tlsv1_2) {
|
|
- SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2);
|
|
- }
|
|
- } else {
|
|
- throw new IllegalStateException("failed to enable protocols: " + protocols);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLSession getSession() {
|
|
- return session;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public synchronized void beginHandshake() throws SSLException {
|
|
- if (engineClosed || destroyed != 0) {
|
|
- throw ENGINE_CLOSED;
|
|
- }
|
|
- switch (accepted) {
|
|
- case 0:
|
|
- handshake();
|
|
- accepted = 2;
|
|
- break;
|
|
- case 1:
|
|
- // 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.
|
|
-
|
|
- accepted = 2; // Next time this method is invoked by the user, we should raise an exception.
|
|
- break;
|
|
- case 2:
|
|
- throw RENEGOTIATION_UNSUPPORTED;
|
|
- default:
|
|
- throw new Error();
|
|
- }
|
|
- }
|
|
-
|
|
- private void beginHandshakeImplicitly() throws SSLException {
|
|
- if (engineClosed || destroyed != 0) {
|
|
- throw ENGINE_CLOSED;
|
|
- }
|
|
-
|
|
- if (accepted == 0) {
|
|
- handshake();
|
|
- accepted = 1;
|
|
- }
|
|
- }
|
|
-
|
|
- private void handshake() throws SSLException {
|
|
- int code = SSL.doHandshake(ssl);
|
|
- if (code <= 0) {
|
|
- // Check for OpenSSL errors caused by the handshake
|
|
- long error = SSL.getLastErrorNumber();
|
|
- if (OpenSsl.isError(error)) {
|
|
- String err = SSL.getErrorString(error);
|
|
- if (logger.isDebugEnabled()) {
|
|
- logger.debug(
|
|
- "SSL_do_handshake failed: OpenSSL error: '" + err + '\'');
|
|
- }
|
|
-
|
|
- // There was an internal error -- shutdown
|
|
- shutdown();
|
|
- throw newSSLException(err);
|
|
- }
|
|
- checkPendingHandshakeException();
|
|
- } else {
|
|
- // if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update
|
|
- // handshakeFinished directly and so eliminate uncessary calls to SSL.isInInit(...)
|
|
- handshakeFinished();
|
|
- }
|
|
- }
|
|
-
|
|
- private static long memoryAddress(ByteBuf buf) {
|
|
- if (buf.hasMemoryAddress()) {
|
|
- return buf.memoryAddress();
|
|
- } else {
|
|
- return Buffer.address(buf.nioBuffer());
|
|
- }
|
|
- }
|
|
-
|
|
- private void handshakeFinished() throws SSLException {
|
|
- 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();
|
|
- }
|
|
- handshakeFinished = true;
|
|
- }
|
|
-
|
|
- private static String selectApplicationProtocol(List<String> protocols,
|
|
- SelectedListenerFailureBehavior behavior,
|
|
- String applicationProtocol) throws SSLException {
|
|
- applicationProtocol = applicationProtocol.replace(':', '_');
|
|
- if (behavior == SelectedListenerFailureBehavior.ACCEPT) {
|
|
- return applicationProtocol;
|
|
- } else {
|
|
- int size = protocols.size();
|
|
- assert size > 0;
|
|
- if (protocols.contains(applicationProtocol)) {
|
|
- return applicationProtocol;
|
|
- } else {
|
|
- if (behavior == SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL) {
|
|
- return protocols.get(size - 1);
|
|
- } else {
|
|
- throw new SSLException("Unknown protocol " + applicationProtocol);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private SSLEngineResult.Status getEngineStatus() {
|
|
- return engineClosed? CLOSED : OK;
|
|
- }
|
|
-
|
|
- private SSLEngineResult.HandshakeStatus handshakeStatus0() throws SSLException {
|
|
- SSLEngineResult.HandshakeStatus status = getHandshakeStatus();
|
|
- if (status == FINISHED) {
|
|
- handshakeFinished();
|
|
- }
|
|
- checkPendingHandshakeException();
|
|
-
|
|
- return status;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
|
|
- if (accepted == 0 || destroyed != 0) {
|
|
- return NOT_HANDSHAKING;
|
|
- }
|
|
-
|
|
- // Check if we are in the initial handshake phase
|
|
- if (!handshakeFinished) {
|
|
- // There is pending data in the network BIO -- call wrap
|
|
- if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
|
|
- return NEED_WRAP;
|
|
- }
|
|
-
|
|
- // No pending data to be sent to the peer
|
|
- // Check to see if we have finished handshaking
|
|
- if (SSL.isInInit(ssl) == 0) {
|
|
- return FINISHED;
|
|
- }
|
|
-
|
|
- // No pending data and still handshaking
|
|
- // Must be waiting on the peer to send more data
|
|
- return NEED_UNWRAP;
|
|
- }
|
|
-
|
|
- // Check if we are in the shutdown phase
|
|
- if (engineClosed) {
|
|
- // Waiting to send the close_notify message
|
|
- if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
|
|
- return NEED_WRAP;
|
|
- }
|
|
-
|
|
- // Must be waiting to receive the close_notify message
|
|
- return NEED_UNWRAP;
|
|
- }
|
|
-
|
|
- return NOT_HANDSHAKING;
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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.length() == 0) {
|
|
- c = 0;
|
|
- } else {
|
|
- c = protocolVersion.charAt(0);
|
|
- }
|
|
-
|
|
- switch (c) {
|
|
- case 'T':
|
|
- return "TLS";
|
|
- case 'S':
|
|
- return "SSL";
|
|
- default:
|
|
- return "UNKNOWN";
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setUseClientMode(boolean clientMode) {
|
|
- if (clientMode != this.clientMode) {
|
|
- throw new UnsupportedOperationException();
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean getUseClientMode() {
|
|
- return clientMode;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setNeedClientAuth(boolean b) {
|
|
- setClientAuth(b ? ClientAuthMode.REQUIRE : ClientAuthMode.NONE);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean getNeedClientAuth() {
|
|
- return clientAuth == ClientAuthMode.REQUIRE;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setWantClientAuth(boolean b) {
|
|
- setClientAuth(b ? ClientAuthMode.OPTIONAL : ClientAuthMode.NONE);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean getWantClientAuth() {
|
|
- return clientAuth == ClientAuthMode.OPTIONAL;
|
|
- }
|
|
-
|
|
- private void setClientAuth(ClientAuthMode 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, OpenSslContext.VERIFY_DEPTH);
|
|
- break;
|
|
- case REQUIRE:
|
|
- SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRE, OpenSslContext.VERIFY_DEPTH);
|
|
- break;
|
|
- case OPTIONAL:
|
|
- SSL.setVerify(ssl, SSL.SSL_CVERIFY_OPTIONAL, OpenSslContext.VERIFY_DEPTH);
|
|
- break;
|
|
- }
|
|
- clientAuth = mode;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setEnableSessionCreation(boolean b) {
|
|
- if (b) {
|
|
- throw new UnsupportedOperationException();
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean getEnableSessionCreation() {
|
|
- return false;
|
|
- }
|
|
-
|
|
- @Override
|
|
- @SuppressWarnings("FinalizeDeclaration")
|
|
- protected void finalize() throws Throwable {
|
|
- super.finalize();
|
|
- // Call shutdown as the user may have created the OpenSslEngine and not used it at all.
|
|
- shutdown();
|
|
- }
|
|
-
|
|
- private final class OpenSslSession implements SSLSession {
|
|
- // SSLSession implementation seems to not need to be thread-safe so no need for volatile etc.
|
|
- private X509Certificate[] x509PeerCerts;
|
|
-
|
|
- // lazy init for memory reasons
|
|
- private Map<String, Object> values;
|
|
-
|
|
- @Override
|
|
- public byte[] getId() {
|
|
- final byte[] id;
|
|
- synchronized (OpenSslEngine.this) {
|
|
- if (destroyed == 0) {
|
|
- id = SSL.getSessionId(ssl);
|
|
- } else {
|
|
- id = EmptyArrays.EMPTY_BYTES;
|
|
- }
|
|
- }
|
|
- // We don't cache that to keep memory usage to a minimum.
|
|
- if (id == null) {
|
|
- // The id should never be null, if it was null then the SESSION itself was not valid.
|
|
- throw new IllegalStateException("SSL session ID not available");
|
|
- }
|
|
- return id;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public SSLSessionContext getSessionContext() {
|
|
- return sessionContext;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public long getCreationTime() {
|
|
- synchronized (OpenSslEngine.this) {
|
|
- if (destroyed == 0) {
|
|
- // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
|
|
- return SSL.getTime(ssl) * 1000L;
|
|
- }
|
|
- return 0;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public long getLastAccessedTime() {
|
|
- // TODO: Add proper implementation
|
|
- return getCreationTime();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void invalidate() {
|
|
- // NOOP
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean isValid() {
|
|
- 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));
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
|
|
- // these are lazy created to reduce memory overhead
|
|
- Certificate[] c = peerCerts;
|
|
- if (c == null) {
|
|
- synchronized (OpenSslEngine.this) {
|
|
- if (destroyed == 0) {
|
|
- if (SSL.isInInit(ssl) != 0) {
|
|
- throw new SSLPeerUnverifiedException("peer not verified");
|
|
- }
|
|
- c = peerCerts = initPeerCertChain();
|
|
- } else {
|
|
- c = peerCerts = EMPTY_CERTIFICATES;
|
|
- }
|
|
- }
|
|
- }
|
|
- return c;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Certificate[] getLocalCertificates() {
|
|
- // TODO: Find out how to get these
|
|
- return EMPTY_CERTIFICATES;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
|
|
- // these are lazy created to reduce memory overhead
|
|
- X509Certificate[] c = x509PeerCerts;
|
|
- if (c == null) {
|
|
- final byte[][] chain;
|
|
- synchronized (OpenSslEngine.this) {
|
|
- if (destroyed == 0) {
|
|
- if (SSL.isInInit(ssl) != 0) {
|
|
- throw new SSLPeerUnverifiedException("peer not verified");
|
|
- }
|
|
- chain = SSL.getPeerCertChain(ssl);
|
|
- } else {
|
|
- c = x509PeerCerts = EMPTY_X509_CERTIFICATES;
|
|
- return c;
|
|
- }
|
|
- }
|
|
- if (chain == null) {
|
|
- throw new SSLPeerUnverifiedException("peer not verified");
|
|
- }
|
|
- X509Certificate[] peerCerts = new X509Certificate[chain.length];
|
|
- for (int i = 0; i < peerCerts.length; i++) {
|
|
- try {
|
|
- peerCerts[i] = X509Certificate.getInstance(chain[i]);
|
|
- } catch (CertificateException e) {
|
|
- throw new IllegalStateException(e);
|
|
- }
|
|
- }
|
|
- c = x509PeerCerts = peerCerts;
|
|
- }
|
|
- return c;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
|
|
- Certificate[] peer = getPeerCertificates();
|
|
- if (peer == null || peer.length == 0) {
|
|
- return null;
|
|
- }
|
|
- return principal(peer);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public Principal getLocalPrincipal() {
|
|
- Certificate[] local = getLocalCertificates();
|
|
- if (local == null || local.length == 0) {
|
|
- return null;
|
|
- }
|
|
- return principal(local);
|
|
- }
|
|
-
|
|
- private Principal principal(Certificate[] certs) {
|
|
- return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String getCipherSuite() {
|
|
- if (!handshakeFinished) {
|
|
- return INVALID_CIPHER;
|
|
- }
|
|
- if (cipher == null) {
|
|
- final String c;
|
|
- synchronized (OpenSslEngine.this) {
|
|
- if (destroyed == 0) {
|
|
- c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
|
|
- } else {
|
|
- c = INVALID_CIPHER;
|
|
- }
|
|
- }
|
|
- if (c != null) {
|
|
- cipher = c;
|
|
- }
|
|
- }
|
|
- return cipher;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String getProtocol() {
|
|
- String applicationProtocol = OpenSslEngine.this.applicationProtocol;
|
|
- final String version;
|
|
- synchronized (OpenSslEngine.this) {
|
|
- if (destroyed == 0) {
|
|
- version = SSL.getVersion(ssl);
|
|
- } else {
|
|
- return StringUtil.EMPTY_STRING;
|
|
- }
|
|
- }
|
|
- if (applicationProtocol == null || applicationProtocol.isEmpty()) {
|
|
- return version;
|
|
- } else {
|
|
- return version + ':' + applicationProtocol;
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String getPeerHost() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getPeerPort() {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getPacketBufferSize() {
|
|
- return MAX_ENCRYPTED_PACKET_LENGTH;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getApplicationBufferSize() {
|
|
- return MAX_PLAINTEXT_LENGTH;
|
|
- }
|
|
-
|
|
- private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
|
|
- byte[][] chain = SSL.getPeerCertChain(ssl);
|
|
- final byte[] clientCert;
|
|
- if (!clientMode) {
|
|
- // 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
|
|
- clientCert = SSL.getPeerCertificate(ssl);
|
|
- } else {
|
|
- clientCert = null;
|
|
- }
|
|
-
|
|
- if (chain == null && clientCert == null) {
|
|
- throw new SSLPeerUnverifiedException("peer not verified");
|
|
- }
|
|
- int len = 0;
|
|
- if (chain != null) {
|
|
- len += chain.length;
|
|
- }
|
|
-
|
|
- int i = 0;
|
|
- Certificate[] peerCerts;
|
|
- if (clientCert != null) {
|
|
- len++;
|
|
- peerCerts = new Certificate[len];
|
|
- peerCerts[i++] = new OpenSslX509Certificate(clientCert);
|
|
- } else {
|
|
- peerCerts = new Certificate[len];
|
|
- }
|
|
- if (chain != null) {
|
|
- int a = 0;
|
|
- for (; i < peerCerts.length; i++) {
|
|
- peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
|
|
- }
|
|
- }
|
|
- return peerCerts;
|
|
- }
|
|
- }
|
|
-}
|
|
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 382a28d..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java
|
|
+++ /dev/null
|
|
@@ -1,42 +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 {
|
|
-
|
|
- OpenSslEngineMap EMPTY = new OpenSslEngineMap() {
|
|
- @Override
|
|
- public OpenSslEngine remove(long ssl) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void add(OpenSslEngine engine) {
|
|
- // NOOP
|
|
- }
|
|
- };
|
|
-
|
|
- /**
|
|
- * Remove the {@link OpenSslEngine} with the given {@code ssl} address and
|
|
- * return it.
|
|
- */
|
|
- OpenSslEngine remove(long ssl);
|
|
-
|
|
- /**
|
|
- * Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}.
|
|
- */
|
|
- void add(OpenSslEngine engine);
|
|
-}
|
|
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 83ee505..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java
|
|
+++ /dev/null
|
|
@@ -1,418 +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.EmptyArrays;
|
|
-import org.apache.tomcat.jni.SSL;
|
|
-import org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-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 javax.net.ssl.X509ExtendedTrustManager;
|
|
-import javax.net.ssl.X509TrustManager;
|
|
-import java.io.File;
|
|
-import java.security.KeyStore;
|
|
-import java.security.cert.X509Certificate;
|
|
-
|
|
-import static io.netty.util.internal.ObjectUtil.*;
|
|
-
|
|
-/**
|
|
- * A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
|
- */
|
|
-public final class OpenSslServerContext extends OpenSslContext {
|
|
- private final OpenSslServerSessionContext sessionContext;
|
|
-
|
|
- /**
|
|
- * Creates a new instance.
|
|
- *
|
|
- * @param certChainFile an X.509 certificate chain file in PEM format
|
|
- * @param keyFile a PKCS#8 private key file in PEM format
|
|
- */
|
|
- 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.
|
|
- */
|
|
- public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
|
- this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
|
|
- ApplicationProtocolConfig.DISABLED, 0, 0);
|
|
- }
|
|
-
|
|
- /**
|
|
- * @deprecated use {@link #OpenSslServerContext(
|
|
- * File, File, String, Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
|
|
- *
|
|
- * 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
|
|
- 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);
|
|
- }
|
|
-
|
|
- /**
|
|
- * @deprecated Use the constructors that accepts {@link ApplicationProtocolConfig} or
|
|
- * {@link ApplicationProtocolNegotiator} instead.
|
|
- *
|
|
- * 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
|
|
- 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 #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory,
|
|
- * Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
|
|
- */
|
|
- @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 #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory,
|
|
- * Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
|
|
- */
|
|
- @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.
|
|
- */
|
|
- 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 trustCertChainFile an X.509 certificate chain file in PEM format.
|
|
- * This provides the certificate chains 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 trustCertChainFile}.
|
|
- * @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.
|
|
- */
|
|
- public OpenSslServerContext(
|
|
- File trustCertChainFile, TrustManagerFactory trustManagerFactory,
|
|
- File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
|
|
- Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config,
|
|
- long sessionCacheSize, long sessionTimeout) throws SSLException {
|
|
- this(trustCertChainFile, 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
|
|
- 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 #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory,
|
|
- * Iterable, CipherSuiteFilter, OpenSslApplicationProtocolNegotiator, long, long)}
|
|
- */
|
|
- @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 trustCertChainFile an X.509 certificate chain file in PEM format.
|
|
- * This provides the certificate chains 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 trustCertChainFile}.
|
|
- * @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.
|
|
- */
|
|
- public OpenSslServerContext(
|
|
- File trustCertChainFile, TrustManagerFactory trustManagerFactory,
|
|
- File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
|
|
- Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
|
|
- long sessionCacheSize, long sessionTimeout) throws SSLException {
|
|
- super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER);
|
|
- OpenSsl.ensureAvailability();
|
|
-
|
|
- checkNotNull(keyCertChainFile, "keyCertChainFile");
|
|
- if (!keyCertChainFile.isFile()) {
|
|
- throw new IllegalArgumentException("keyCertChainFile is not a file: " + keyCertChainFile);
|
|
- }
|
|
- checkNotNull(keyFile, "keyFile");
|
|
- if (!keyFile.isFile()) {
|
|
- throw new IllegalArgumentException("keyFile is not a file: " + keyFile);
|
|
- }
|
|
- if (keyPassword == null) {
|
|
- keyPassword = "";
|
|
- }
|
|
-
|
|
- // Create a new SSL_CTX and configure it.
|
|
- boolean success = false;
|
|
- try {
|
|
- synchronized (OpenSslContext.class) {
|
|
- /* Set certificate verification policy. */
|
|
- SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
|
|
-
|
|
- /* Load the certificate chain. We must skip the first cert when server mode */
|
|
- if (!SSLContext.setCertificateChainFile(ctx, keyCertChainFile.getPath(), true)) {
|
|
- long error = SSL.getLastErrorNumber();
|
|
- if (OpenSsl.isError(error)) {
|
|
- String err = SSL.getErrorString(error);
|
|
- throw new SSLException(
|
|
- "failed to set certificate chain: " + keyCertChainFile + " (" + err + ')');
|
|
- }
|
|
- }
|
|
-
|
|
- /* Load the certificate file and private key. */
|
|
- try {
|
|
- if (!SSLContext.setCertificate(
|
|
- ctx, keyCertChainFile.getPath(), keyFile.getPath(), keyPassword, SSL.SSL_AIDX_RSA)) {
|
|
- long error = SSL.getLastErrorNumber();
|
|
- if (OpenSsl.isError(error)) {
|
|
- String err = SSL.getErrorString(error);
|
|
- throw new SSLException("failed to set certificate: " +
|
|
- keyCertChainFile + " and " + keyFile + " (" + err + ')');
|
|
- }
|
|
- }
|
|
- } catch (SSLException e) {
|
|
- throw e;
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("failed to set certificate: " + keyCertChainFile + " and " + keyFile, e);
|
|
- }
|
|
- try {
|
|
- if (trustManagerFactory == null) {
|
|
- // Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
|
|
- trustManagerFactory = TrustManagerFactory.getInstance(
|
|
- TrustManagerFactory.getDefaultAlgorithm());
|
|
- }
|
|
- if (trustCertChainFile != null) {
|
|
- trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
|
|
- } else {
|
|
- char[] keyPasswordChars =
|
|
- keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray();
|
|
-
|
|
- KeyStore ks = buildKeyStore(keyCertChainFile, keyFile, keyPasswordChars);
|
|
- trustManagerFactory.init(ks);
|
|
- }
|
|
-
|
|
- final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
|
|
-
|
|
- // Use this to prevent an error when running on java < 7
|
|
- if (useExtendedTrustManager(manager)) {
|
|
- final X509ExtendedTrustManager extendedManager = (X509ExtendedTrustManager) manager;
|
|
- SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
|
|
- @Override
|
|
- void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
|
|
- throws Exception {
|
|
- extendedManager.checkClientTrusted(peerCerts, auth, engine);
|
|
- }
|
|
- });
|
|
- } else {
|
|
- SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
|
|
- @Override
|
|
- void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
|
|
- throws Exception {
|
|
- manager.checkClientTrusted(peerCerts, auth);
|
|
- }
|
|
- });
|
|
- }
|
|
- } catch (Exception e) {
|
|
- throw new SSLException("unable to setup trustmanager", e);
|
|
- }
|
|
- }
|
|
- sessionContext = new OpenSslServerSessionContext(ctx);
|
|
- success = true;
|
|
- } finally {
|
|
- if (!success) {
|
|
- destroyPools();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- @Override
|
|
- public OpenSslServerSessionContext sessionContext() {
|
|
- return sessionContext;
|
|
- }
|
|
-}
|
|
diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java
|
|
deleted file mode 100644
|
|
index 693801f..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java
|
|
+++ /dev/null
|
|
@@ -1,79 +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 org.apache.tomcat.jni.SSL;
|
|
-import org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-
|
|
-/**
|
|
- * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side.
|
|
- */
|
|
-public final class OpenSslServerSessionContext extends OpenSslSessionContext {
|
|
- OpenSslServerSessionContext(long context) {
|
|
- super(context);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setSessionTimeout(int seconds) {
|
|
- if (seconds < 0) {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- SSLContext.setSessionCacheTimeout(context, seconds);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getSessionTimeout() {
|
|
- return (int) SSLContext.getSessionCacheTimeout(context);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setSessionCacheSize(int size) {
|
|
- if (size < 0) {
|
|
- throw new IllegalArgumentException();
|
|
- }
|
|
- SSLContext.setSessionCacheSize(context, size);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public int getSessionCacheSize() {
|
|
- return (int) SSLContext.getSessionCacheSize(context);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void setSessionCacheEnabled(boolean enabled) {
|
|
- long mode = enabled ? SSL.SSL_SESS_CACHE_SERVER : SSL.SSL_SESS_CACHE_OFF;
|
|
- SSLContext.setSessionCacheMode(context, mode);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean isSessionCacheEnabled() {
|
|
- return SSLContext.getSessionCacheMode(context) == SSL.SSL_SESS_CACHE_SERVER;
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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) {
|
|
- return SSLContext.setSessionIdContext(context, sidCtx);
|
|
- }
|
|
-}
|
|
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 fd17821..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java
|
|
+++ /dev/null
|
|
@@ -1,90 +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 org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-import javax.net.ssl.SSLSession;
|
|
-import javax.net.ssl.SSLSessionContext;
|
|
-import java.util.Enumeration;
|
|
-import java.util.NoSuchElementException;
|
|
-
|
|
-/**
|
|
- * OpenSSL specific {@link SSLSessionContext} implementation.
|
|
- */
|
|
-public abstract class OpenSslSessionContext implements SSLSessionContext {
|
|
- private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
|
|
-
|
|
- private final OpenSslSessionStats stats;
|
|
- final long context;
|
|
-
|
|
- OpenSslSessionContext(long 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.
|
|
- */
|
|
- public void setTicketKeys(byte[] keys) {
|
|
- if (keys == null) {
|
|
- throw new NullPointerException("keys");
|
|
- }
|
|
- SSLContext.setSessionTicketKeys(context, keys);
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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 2ec5146..0000000
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java
|
|
+++ /dev/null
|
|
@@ -1,122 +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 org.apache.tomcat.jni.SSLContext;
|
|
-
|
|
-/**
|
|
- * Stats exposed by an OpenSSL session context.
|
|
- *
|
|
- * @see <a href="https://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html"><code>SSL_CTX_sess_number</code></a>
|
|
- */
|
|
-public final class OpenSslSessionStats {
|
|
-
|
|
- private final long context;
|
|
-
|
|
- OpenSslSessionStats(long context) {
|
|
- this.context = context;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the current number of sessions in the internal session cache.
|
|
- */
|
|
- public long number() {
|
|
- return SSLContext.sessionNumber(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of started SSL/TLS handshakes in client mode.
|
|
- */
|
|
- public long connect() {
|
|
- return SSLContext.sessionConnect(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of successfully established SSL/TLS sessions in client mode.
|
|
- */
|
|
- public long connectGood() {
|
|
- return SSLContext.sessionConnectGood(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of start renegotiations in client mode.
|
|
- */
|
|
- public long connectRenegotiate() {
|
|
- return SSLContext.sessionConnectRenegotiate(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of started SSL/TLS handshakes in server mode.
|
|
- */
|
|
- public long accept() {
|
|
- return SSLContext.sessionAccept(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of successfully established SSL/TLS sessions in server mode.
|
|
- */
|
|
- public long acceptGood() {
|
|
- return SSLContext.sessionAcceptGood(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of start renegotiations in server mode.
|
|
- */
|
|
- public long acceptRenegotiate() {
|
|
- return SSLContext.sessionAcceptRenegotiate(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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() {
|
|
- return SSLContext.sessionHits(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of successfully retrieved sessions from the external session cache in server mode.
|
|
- */
|
|
- public long cbHits() {
|
|
- return SSLContext.sessionCbHits(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of sessions proposed by clients that were not found in the internal session cache
|
|
- * in server mode.
|
|
- */
|
|
- public long misses() {
|
|
- return SSLContext.sessionMisses(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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() {
|
|
- return SSLContext.sessionTimeouts(context);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the number of sessions that were removed because the maximum session cache size was exceeded.
|
|
- */
|
|
- public long cacheFull() {
|
|
- return SSLContext.sessionCacheFull(context);
|
|
- }
|
|
-}
|
|
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 890b362..42abc14 100644
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java
|
|
+++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java
|
|
@@ -110,11 +110,7 @@ public abstract class SslContext {
|
|
}
|
|
|
|
private static SslProvider defaultProvider() {
|
|
- if (OpenSsl.isAvailable()) {
|
|
- return SslProvider.OPENSSL;
|
|
- } else {
|
|
- return SslProvider.JDK;
|
|
- }
|
|
+ return SslProvider.JDK;
|
|
}
|
|
|
|
/**
|
|
@@ -399,10 +395,6 @@ public abstract class SslContext {
|
|
return new JdkSslServerContext(
|
|
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
|
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
|
- case OPENSSL:
|
|
- return new OpenSslServerContext(
|
|
- trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
|
- keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
|
default:
|
|
throw new Error(provider.toString());
|
|
}
|
|
@@ -729,10 +721,6 @@ public abstract class SslContext {
|
|
return new JdkSslClientContext(
|
|
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
|
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
|
- case OPENSSL:
|
|
- return new OpenSslClientContext(
|
|
- trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
|
- keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
|
}
|
|
// Should never happen!!
|
|
throw new Error();
|
|
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 a3b1716..c492dc4 100644
|
|
--- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
|
|
+++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
|
|
@@ -161,6 +161,15 @@ import java.util.regex.Pattern;
|
|
*/
|
|
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);
|
|
|
|
@@ -290,7 +299,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
this.startTls = startTls;
|
|
maxPacketBufferSize = engine.getSession().getPacketBufferSize();
|
|
|
|
- boolean opensslEngine = engine instanceof OpenSslEngine;
|
|
+ boolean opensslEngine = false;
|
|
wantsDirectBuffer = opensslEngine;
|
|
wantsLargeOutboundNetworkBuffer = !opensslEngine;
|
|
|
|
@@ -883,7 +892,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
|
|
boolean nonSslRecord = false;
|
|
|
|
- while (totalLength < OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
+ while (totalLength < MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
final int readableBytes = endOffset - offset;
|
|
if (readableBytes < 5) {
|
|
break;
|
|
@@ -904,7 +913,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
}
|
|
|
|
int newTotalLength = totalLength + packetLength;
|
|
- if (newTotalLength > OpenSslEngine.MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
+ if (newTotalLength > MAX_ENCRYPTED_PACKET_LENGTH) {
|
|
// Don't read too much.
|
|
break;
|
|
}
|
|
@@ -1077,47 +1086,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
private SSLEngineResult unwrap(
|
|
SSLEngine engine, ByteBuf in, int readerIndex, int len, ByteBuf out) throws SSLException {
|
|
int nioBufferCount = in.nioBufferCount();
|
|
- if (engine instanceof OpenSslEngine && 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.
|
|
- */
|
|
- OpenSslEngine opensslEngine = (OpenSslEngine) engine;
|
|
- int overflows = 0;
|
|
- ByteBuffer[] in0 = in.nioBuffers(readerIndex, len);
|
|
- try {
|
|
- for (;;) {
|
|
- int writerIndex = out.writerIndex();
|
|
- int writableBytes = out.writableBytes();
|
|
- ByteBuffer out0;
|
|
- if (out.nioBufferCount() == 1) {
|
|
- out0 = out.internalNioBuffer(writerIndex, writableBytes);
|
|
- } else {
|
|
- out0 = out.nioBuffer(writerIndex, writableBytes);
|
|
- }
|
|
- singleBuffer[0] = out0;
|
|
- SSLEngineResult result = opensslEngine.unwrap(in0, singleBuffer);
|
|
- out.writerIndex(out.writerIndex() + result.bytesProduced());
|
|
- switch (result.getStatus()) {
|
|
- case BUFFER_OVERFLOW:
|
|
- int max = engine.getSession().getApplicationBufferSize();
|
|
- switch (overflows ++) {
|
|
- case 0:
|
|
- out.ensureWritable(Math.min(max, in.readableBytes()));
|
|
- break;
|
|
- default:
|
|
- out.ensureWritable(max);
|
|
- }
|
|
- break;
|
|
- default:
|
|
- return result;
|
|
- }
|
|
- }
|
|
- } finally {
|
|
- singleBuffer[0] = null;
|
|
- }
|
|
- } else {
|
|
+
|
|
int overflows = 0;
|
|
ByteBuffer in0;
|
|
if (nioBufferCount == 1) {
|
|
@@ -1154,7 +1123,6 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
return result;
|
|
}
|
|
}
|
|
- }
|
|
}
|
|
|
|
/**
|
|
@@ -1514,7 +1482,7 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
|
|
return allocate(ctx, maxPacketBufferSize);
|
|
} else {
|
|
return allocate(ctx, Math.min(
|
|
- pendingBytes + OpenSslEngine.MAX_ENCRYPTION_OVERHEAD_LENGTH,
|
|
+ pendingBytes + MAX_ENCRYPTION_OVERHEAD_LENGTH,
|
|
maxPacketBufferSize));
|
|
}
|
|
}
|
|
diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java
|
|
deleted file mode 100644
|
|
index 9482f2b..0000000
|
|
--- a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java
|
|
+++ /dev/null
|
|
@@ -1,349 +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.junit.Assert.assertNull;
|
|
-import static org.junit.Assert.assertTrue;
|
|
-import static org.junit.Assume.assumeNoException;
|
|
-import io.netty.bootstrap.Bootstrap;
|
|
-import io.netty.bootstrap.ServerBootstrap;
|
|
-import io.netty.channel.Channel;
|
|
-import io.netty.channel.ChannelFuture;
|
|
-import io.netty.channel.ChannelHandlerAdapter;
|
|
-import io.netty.channel.ChannelHandlerContext;
|
|
-import io.netty.channel.ChannelInitializer;
|
|
-import io.netty.channel.ChannelPipeline;
|
|
-import io.netty.channel.nio.NioEventLoopGroup;
|
|
-import io.netty.channel.socket.nio.NioServerSocketChannel;
|
|
-import io.netty.channel.socket.nio.NioSocketChannel;
|
|
-import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
|
|
-import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectorFactory;
|
|
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
-import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
-import io.netty.util.NetUtil;
|
|
-
|
|
-import java.net.InetSocketAddress;
|
|
-import java.security.cert.CertificateException;
|
|
-import java.util.List;
|
|
-import java.util.Set;
|
|
-import java.util.concurrent.TimeUnit;
|
|
-
|
|
-import javax.net.ssl.SSLEngine;
|
|
-import javax.net.ssl.SSLException;
|
|
-import javax.net.ssl.SSLHandshakeException;
|
|
-
|
|
-import org.junit.Test;
|
|
-
|
|
-public class JdkSslEngineTest 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";
|
|
- private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO";
|
|
-
|
|
- @Test
|
|
- public void testNpn() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkNpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("NPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator apn = new JdkNpnApplicationProtocolNegotiator(true, true,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- mySetup(apn);
|
|
- runTest();
|
|
- } catch (RuntimeException e) {
|
|
- // NPN availability is dependent on the java version. If NPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testNpnNoCompatibleProtocolsNoHandshakeFailure() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkNpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("NPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
|
- APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
|
- mySetup(serverApn, clientApn);
|
|
- runTest(null);
|
|
- } catch (Exception e) {
|
|
- // ALPN availability is dependent on the java version. If ALPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testNpnNoCompatibleProtocolsClientHandshakeFailure() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkNpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("NPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkNpnApplicationProtocolNegotiator(true, true,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
|
- APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
|
- mySetup(serverApn, clientApn);
|
|
- assertTrue(clientLatch.await(2, TimeUnit.SECONDS));
|
|
- assertTrue(clientException instanceof SSLHandshakeException);
|
|
- } catch (RuntimeException e) {
|
|
- // NPN availability is dependent on the java version. If NPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testNpnNoCompatibleProtocolsServerHandshakeFailure() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkNpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("NPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkNpnApplicationProtocolNegotiator(false, false,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkNpnApplicationProtocolNegotiator(true, true,
|
|
- APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
|
- mySetup(serverApn, clientApn);
|
|
- assertTrue(serverLatch.await(2, TimeUnit.SECONDS));
|
|
- assertTrue(serverException instanceof SSLHandshakeException);
|
|
- } catch (RuntimeException e) {
|
|
- // NPN availability is dependent on the java version. If NPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpn() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkAlpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("ALPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator apn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- mySetup(apn);
|
|
- runTest();
|
|
- } catch (Exception e) {
|
|
- // ALPN availability is dependent on the java version. If ALPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpnNoCompatibleProtocolsNoHandshakeFailure() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkAlpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("ALPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
|
- APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
|
- mySetup(serverApn, clientApn);
|
|
- runTest(null);
|
|
- } catch (Exception e) {
|
|
- // ALPN availability is dependent on the java version. If ALPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpnNoCompatibleProtocolsServerHandshakeFailure() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkAlpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("ALPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
|
- APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
|
- mySetup(serverApn, clientApn);
|
|
- assertTrue(serverLatch.await(2, TimeUnit.SECONDS));
|
|
- assertTrue(serverException instanceof SSLHandshakeException);
|
|
- } catch (Exception e) {
|
|
- // ALPN availability is dependent on the java version. If ALPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpnCompatibleProtocolsDifferentClientOrder() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkAlpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("ALPN not on classpath");
|
|
- }
|
|
- // Even the preferred application protocol appears second in the client's list, it will be picked
|
|
- // because it's the first one on server's list.
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(false, false,
|
|
- FALLBACK_APPLICATION_LEVEL_PROTOCOL, PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL, FALLBACK_APPLICATION_LEVEL_PROTOCOL);
|
|
- mySetup(serverApn, clientApn);
|
|
- assertNull(serverException);
|
|
- runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- } catch (Exception e) {
|
|
- // ALPN availability is dependent on the java version. If ALPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- @Test
|
|
- public void testAlpnNoCompatibleProtocolsClientHandshakeFailure() throws Exception {
|
|
- try {
|
|
- // Typical code will not have to check this, but will get a initialization error on class load.
|
|
- // Check in this test just in case we have multiple tests that just the class and we already ignored the
|
|
- // initialization error.
|
|
- if (!JdkAlpnSslEngine.isAvailable()) {
|
|
- throw new RuntimeException("ALPN not on classpath");
|
|
- }
|
|
- JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(true, true,
|
|
- PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator(
|
|
- new ProtocolSelectorFactory() {
|
|
- @Override
|
|
- public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
|
|
- return new ProtocolSelector() {
|
|
- @Override
|
|
- public void unsupported() {
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String select(List<String> protocols) {
|
|
- return APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE;
|
|
- }
|
|
- };
|
|
- }
|
|
- }, JdkBaseApplicationProtocolNegotiator.FAIL_SELECTION_LISTENER_FACTORY,
|
|
- APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE);
|
|
- mySetup(serverApn, clientApn);
|
|
- assertTrue(clientLatch.await(2, TimeUnit.SECONDS));
|
|
- assertTrue(clientException instanceof SSLHandshakeException);
|
|
- } catch (Exception e) {
|
|
- // ALPN availability is dependent on the java version. If ALPN is not available because of
|
|
- // java version incompatibility don't fail the test, but instead just skip the test
|
|
- assumeNoException(e);
|
|
- }
|
|
- }
|
|
-
|
|
- private void mySetup(JdkApplicationProtocolNegotiator apn) throws InterruptedException, SSLException,
|
|
- CertificateException {
|
|
- mySetup(apn, apn);
|
|
- }
|
|
-
|
|
- private void mySetup(JdkApplicationProtocolNegotiator serverApn, JdkApplicationProtocolNegotiator clientApn)
|
|
- throws InterruptedException, SSLException, CertificateException {
|
|
- SelfSignedCertificate ssc = new SelfSignedCertificate();
|
|
- serverSslCtx = new JdkSslServerContext(ssc.certificate(), ssc.privateKey(), null, null,
|
|
- IdentityCipherSuiteFilter.INSTANCE, serverApn, 0, 0);
|
|
- clientSslCtx = new JdkSslClientContext(null, InsecureTrustManagerFactory.INSTANCE, null,
|
|
- IdentityCipherSuiteFilter.INSTANCE, clientApn, 0, 0);
|
|
-
|
|
- serverConnectedChannel = null;
|
|
- sb = new ServerBootstrap();
|
|
- cb = new Bootstrap();
|
|
-
|
|
- sb.group(new NioEventLoopGroup(), new NioEventLoopGroup());
|
|
- sb.channel(NioServerSocketChannel.class);
|
|
- sb.childHandler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ChannelPipeline p = ch.pipeline();
|
|
- p.addLast(serverSslCtx.newHandler(ch.alloc()));
|
|
- p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch));
|
|
- p.addLast(new ChannelHandlerAdapter() {
|
|
- @Override
|
|
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
- if (cause.getCause() instanceof SSLHandshakeException) {
|
|
- serverException = cause.getCause();
|
|
- serverLatch.countDown();
|
|
- } else {
|
|
- ctx.fireExceptionCaught(cause);
|
|
- }
|
|
- }
|
|
- });
|
|
- serverConnectedChannel = ch;
|
|
- }
|
|
- });
|
|
-
|
|
- cb.group(new NioEventLoopGroup());
|
|
- cb.channel(NioSocketChannel.class);
|
|
- cb.handler(new ChannelInitializer<Channel>() {
|
|
- @Override
|
|
- protected void initChannel(Channel ch) throws Exception {
|
|
- ChannelPipeline p = ch.pipeline();
|
|
- p.addLast(clientSslCtx.newHandler(ch.alloc()));
|
|
- p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch));
|
|
- p.addLast(new ChannelHandlerAdapter() {
|
|
- @Override
|
|
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
- if (cause.getCause() instanceof SSLHandshakeException) {
|
|
- clientException = cause.getCause();
|
|
- clientLatch.countDown();
|
|
- } else {
|
|
- ctx.fireExceptionCaught(cause);
|
|
- }
|
|
- }
|
|
- });
|
|
- }
|
|
- });
|
|
-
|
|
- serverChannel = sb.bind(new InetSocketAddress(0)).sync().channel();
|
|
- int port = ((InetSocketAddress) serverChannel.localAddress()).getPort();
|
|
-
|
|
- ChannelFuture ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port));
|
|
- assertTrue(ccf.awaitUninterruptibly().isSuccess());
|
|
- clientChannel = ccf.channel();
|
|
- }
|
|
-
|
|
- private void runTest() throws Exception {
|
|
- runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL);
|
|
- }
|
|
-
|
|
- @Override
|
|
- protected SslProvider sslProvider() {
|
|
- return SslProvider.JDK;
|
|
- }
|
|
-}
|