Backport ALPN and Mutual Auth SSL
Motivation: Improvements were made on the main line to support ALPN and mutual authentication for TLS. These should be backported. Modifications: - Backport commits from the master branch - f8af84d5993456426a63ad0146479147b1a4a5e5 - e74c8edba3fcbfd2e895ed6aac440efeb3aa637f Result: Support for ALPN and mutual authentication.
This commit is contained in:
parent
746c8cab32
commit
04f77b76f8
35
common/src/main/java/io/netty/util/internal/ObjectUtil.java
Normal file
35
common/src/main/java/io/netty/util/internal/ObjectUtil.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.util.internal;
|
||||
|
||||
/**
|
||||
* A grab-bag of useful utility methods.
|
||||
*/
|
||||
public final class ObjectUtil {
|
||||
|
||||
private ObjectUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the given argument is not null. If it is, throws {@link NullPointerException}.
|
||||
* Otherwise, returns the argument.
|
||||
*/
|
||||
public static <T> T checkNotNull(T arg, String text) {
|
||||
if (arg == null) {
|
||||
throw new NullPointerException(text);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
}
|
@ -26,6 +26,12 @@ import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec;
|
||||
import io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator;
|
||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
@ -45,7 +51,15 @@ public final class MemcacheClient {
|
||||
// Configure SSL.
|
||||
final SslContext sslCtx;
|
||||
if (SSL) {
|
||||
sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE);
|
||||
sslCtx = SslContext.newClientContext(
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
IdentityCipherSuiteFilter.INSTANCE,
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.ALPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
} else {
|
||||
sslCtx = null;
|
||||
}
|
||||
|
@ -27,11 +27,14 @@ import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An SPDY client that allows you to send HTTP GET to a SPDY server.
|
||||
* <p>
|
||||
@ -54,8 +57,13 @@ public final class SpdyClient {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Configure SSL.
|
||||
final SslContext sslCtx = SslContext.newClientContext(
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null,
|
||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
null, InsecureTrustManagerFactory.INSTANCE, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.NPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
|
||||
HttpResponseClientHandler httpResponseHandler = new HttpResponseClientHandler();
|
||||
|
@ -24,11 +24,14 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A SPDY Server that responds to a GET request with a Hello World.
|
||||
* <p>
|
||||
@ -55,8 +58,13 @@ public final class SpdyServer {
|
||||
// Configure SSL.
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SslContext sslCtx = SslContext.newServerContext(
|
||||
ssc.certificate(), ssc.privateKey(), null, null,
|
||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
ssc.certificate(), ssc.privateKey(), null, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
new ApplicationProtocolConfig(
|
||||
Protocol.NPN,
|
||||
SelectorFailureBehavior.FATAL_ALERT,
|
||||
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||
0, 0);
|
||||
|
||||
// Configure the server.
|
||||
|
@ -66,6 +66,22 @@
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.handler.ssl.ApplicationProtocolUtil.toList;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Provides an {@link SSLEngine} agnostic way to configure a {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
public final class ApplicationProtocolConfig {
|
||||
private final List<String> supportedProtocols;
|
||||
private final Protocol protocol;
|
||||
private final SelectorFailureBehavior selectorBehavior;
|
||||
private final SelectedListenerFailureBehavior selectedBehavior;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocol The application protocol functionality to use.
|
||||
* @param selectorBehavior How the peer selecting the protocol should behave.
|
||||
* @param selectedBehavior How the peer being notified of the selected protocol should behave.
|
||||
* @param supportedProtocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior,
|
||||
SelectedListenerFailureBehavior selectedBehavior, Iterable<String> supportedProtocols) {
|
||||
this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocol The application protocol functionality to use.
|
||||
* @param selectorBehavior How the peer selecting the protocol should behave.
|
||||
* @param selectedBehavior How the peer being notified of the selected protocol should behave.
|
||||
* @param supportedProtocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior,
|
||||
SelectedListenerFailureBehavior selectedBehavior, String... supportedProtocols) {
|
||||
this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param protocol The application protocol functionality to use.
|
||||
* @param selectorBehavior How the peer selecting the protocol should behave.
|
||||
* @param selectedBehavior How the peer being notified of the selected protocol should behave.
|
||||
* @param supportedProtocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
private ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior,
|
||||
SelectedListenerFailureBehavior selectedBehavior, List<String> supportedProtocols) {
|
||||
this.supportedProtocols = Collections.unmodifiableList(checkNotNull(supportedProtocols, "supportedProtocols"));
|
||||
this.protocol = checkNotNull(protocol, "protocol");
|
||||
this.selectorBehavior = checkNotNull(selectorBehavior, "selectorBehavior");
|
||||
this.selectedBehavior = checkNotNull(selectedBehavior, "selectedBehavior");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines which application level protocol negotiation to use.
|
||||
*/
|
||||
public enum Protocol {
|
||||
NONE, NPN, ALPN, NPN_AND_ALPN
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the most common behaviors for the peer that selects the application protocol.
|
||||
*/
|
||||
public enum SelectorFailureBehavior {
|
||||
FATAL_ALERT, NO_ADVERTISE, CHOOSE_MY_LAST_PROTOCOL
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the most common behaviors for the peer which is notified of the selected protocol.
|
||||
*/
|
||||
public enum SelectedListenerFailureBehavior {
|
||||
ACCEPT, FATAL_ALERT, CHOOSE_MY_LAST_PROTOCOL
|
||||
}
|
||||
|
||||
/**
|
||||
* The application level protocols supported.
|
||||
*/
|
||||
public List<String> supportedProtocols() {
|
||||
return supportedProtocols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get which application level protocol negotiation to use.
|
||||
*/
|
||||
public Protocol protocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the desired behavior for the peer who selects the application protocol.
|
||||
*/
|
||||
public SelectorFailureBehavior selectorFailureBehavior() {
|
||||
return selectorBehavior;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the desired behavior for the peer who is notified of the selected protocol.
|
||||
*/
|
||||
public SelectedListenerFailureBehavior selectedListenerFailureBehavior() {
|
||||
return selectedBehavior;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
/**
|
||||
* Interface to support Application Protocol Negotiation.
|
||||
* <p>
|
||||
* Default implementations are provided for:
|
||||
* <ul>
|
||||
* <li><a href="https://technotes.googlecode.com/git/nextprotoneg.html">Next Protocol Negotiation</a></li>
|
||||
* <li><a href="http://tools.ietf.org/html/rfc7301">Application-Layer Protocol Negotiation</a></li>
|
||||
* </ul>
|
||||
*/
|
||||
public interface ApplicationProtocolNegotiator {
|
||||
/**
|
||||
* Get the collection of application protocols supported by this application (in preference order).
|
||||
*/
|
||||
List<String> protocols();
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class for application protocol common operations.
|
||||
*/
|
||||
final class ApplicationProtocolUtil {
|
||||
private static final int DEFAULT_LIST_SIZE = 2;
|
||||
|
||||
private ApplicationProtocolUtil() {
|
||||
}
|
||||
|
||||
static List<String> toList(Iterable<String> protocols) {
|
||||
return toList(DEFAULT_LIST_SIZE, protocols);
|
||||
}
|
||||
|
||||
static List<String> toList(int initialListSize, Iterable<String> protocols) {
|
||||
if (protocols == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<String>(initialListSize);
|
||||
for (String p : protocols) {
|
||||
if (p == null || p.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocol cannot be null or empty");
|
||||
}
|
||||
result.add(p);
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocols cannot empty");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<String> toList(String... protocols) {
|
||||
return toList(DEFAULT_LIST_SIZE, protocols);
|
||||
}
|
||||
|
||||
static List<String> toList(int initialListSize, String... protocols) {
|
||||
if (protocols == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<String>(initialListSize);
|
||||
for (String p : protocols) {
|
||||
if (p == null || p.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocol cannot be null or empty");
|
||||
}
|
||||
result.add(p);
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
throw new IllegalArgumentException("protocols cannot empty");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides a means to filter the supplied cipher suite based upon the supported and default cipher suites.
|
||||
*/
|
||||
public interface CipherSuiteFilter {
|
||||
/**
|
||||
* Filter the requested {@code ciphers} based upon other cipher characteristics.
|
||||
* @param ciphers The requested ciphers
|
||||
* @param defaultCiphers The default recommended ciphers for the current {@link SSLEngine} as determined by Netty
|
||||
* @param supportedCiphers The supported ciphers for the current {@link SSLEngine}
|
||||
* @return The filter list of ciphers. Must not return {@code null}.
|
||||
*/
|
||||
String[] filterCipherSuites(Iterable<String> ciphers, List<String> defaultCiphers, Set<String> supportedCiphers);
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class will not do any filtering of ciphers suites.
|
||||
*/
|
||||
public final class IdentityCipherSuiteFilter implements CipherSuiteFilter {
|
||||
public static final IdentityCipherSuiteFilter INSTANCE = new IdentityCipherSuiteFilter();
|
||||
|
||||
private IdentityCipherSuiteFilter() { }
|
||||
|
||||
@Override
|
||||
public String[] filterCipherSuites(Iterable<String> ciphers, List<String> defaultCiphers,
|
||||
Set<String> supportedCiphers) {
|
||||
if (ciphers == null) {
|
||||
return defaultCiphers.toArray(new String[defaultCiphers.size()]);
|
||||
} else {
|
||||
List<String> newCiphers = new ArrayList<String>(supportedCiphers.size());
|
||||
for (String c : ciphers) {
|
||||
if (c == null) {
|
||||
break;
|
||||
}
|
||||
newCiphers.add(c);
|
||||
}
|
||||
return newCiphers.toArray(new String[newCiphers.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
122
handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java
Normal file
122
handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.HashSet;
|
||||
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 {
|
||||
// Try to get the bootstrap class loader.
|
||||
ClassLoader bootloader = ClassLoader.getSystemClassLoader().getParent();
|
||||
if (bootloader == null) {
|
||||
// If failed, use the system class loader,
|
||||
// although it's not perfect to tell if APLN extension has been loaded.
|
||||
bootloader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
Class.forName("sun.security.ssl.ALPNExtension", true, bootloader);
|
||||
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 HashSet<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();
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* JDK extension methods to support {@link ApplicationProtocolNegotiator}
|
||||
*/
|
||||
public interface JdkApplicationProtocolNegotiator extends ApplicationProtocolNegotiator {
|
||||
/**
|
||||
* Abstract factory pattern for wrapping an {@link SSLEngine} object. This is useful for NPN/APLN JDK support.
|
||||
*/
|
||||
public interface SslEngineWrapperFactory {
|
||||
/**
|
||||
* Abstract factory pattern for wrapping an {@link SSLEngine} object. This is useful for NPN/APLN support.
|
||||
*
|
||||
* @param engine The engine to wrap.
|
||||
* @param applicationNegotiator The application level protocol negotiator
|
||||
* @param isServer <ul>
|
||||
* <li>{@code true} if the engine is for server side of connections</li>
|
||||
* <li>{@code false} if the engine is for client side of connections</li>
|
||||
* </ul>
|
||||
* @return The resulting wrapped engine. This may just be {@code engine}.
|
||||
*/
|
||||
SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
||||
boolean isServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to define the role of an application protocol selector in the SSL handshake process. Either
|
||||
* {@link ProtocolSelector#unsupported()} OR {@link ProtocolSelector#select(List)} will be called for each SSL
|
||||
* handshake.
|
||||
*/
|
||||
public interface ProtocolSelector {
|
||||
/**
|
||||
* Callback invoked to let the application know that the peer does not support this
|
||||
* {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
void unsupported();
|
||||
|
||||
/**
|
||||
* Callback invoked to select the application level protocol from the {@code protocols} provided.
|
||||
*
|
||||
* @param protocols the protocols sent by the protocol advertiser
|
||||
* @return the protocol selected by this {@link ProtocolSelector}. A {@code null} value will indicate the no
|
||||
* protocols were selected but the handshake should not fail. The decision to fail the handshake is left to the
|
||||
* other end negotiating the SSL handshake.
|
||||
* @throws Exception If the {@code protocols} provide warrant failing the SSL handshake with a fatal alert.
|
||||
*/
|
||||
String select(List<String> protocols) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener to be notified by which protocol was select by its peer. Either the
|
||||
* {@link ProtocolSelectionListener#unsupported()} OR the {@link ProtocolSelectionListener#selected(String)} method
|
||||
* will be called for each SSL handshake.
|
||||
*/
|
||||
public interface ProtocolSelectionListener {
|
||||
/**
|
||||
* Callback invoked to let the application know that the peer does not support this
|
||||
* {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
void unsupported();
|
||||
|
||||
/**
|
||||
* Callback invoked to let this application know the protocol chosen by the peer.
|
||||
*
|
||||
* @param protocol the protocol selected by the peer. May be {@code null} or empty as supported by the
|
||||
* application negotiation protocol.
|
||||
* @throws Exception This may be thrown if the selected protocol is not acceptable and the desired behavior is
|
||||
* to fail the handshake with a fatal alert.
|
||||
*/
|
||||
void selected(String protocol) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory interface for {@link ProtocolSelector} objects.
|
||||
*/
|
||||
public interface ProtocolSelectorFactory {
|
||||
/**
|
||||
* Generate a new instance of {@link ProtocolSelector}.
|
||||
* @param engine The {@link SSLEngine} that the returned {@link ProtocolSelector} will be used to create an
|
||||
* instance for.
|
||||
* @param supportedProtocols The protocols that are supported.
|
||||
* @return A new instance of {@link ProtocolSelector}.
|
||||
*/
|
||||
ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory interface for {@link ProtocolSelectionListener} objects.
|
||||
*/
|
||||
public interface ProtocolSelectionListenerFactory {
|
||||
/**
|
||||
* Generate a new instance of {@link ProtocolSelectionListener}.
|
||||
* @param engine The {@link SSLEngine} that the returned {@link ProtocolSelectionListener} will be used to
|
||||
* create an instance for.
|
||||
* @param supportedProtocols The protocols that are supported in preference order.
|
||||
* @return A new instance of {@link ProtocolSelectionListener}.
|
||||
*/
|
||||
ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link SslEngineWrapperFactory}.
|
||||
*/
|
||||
SslEngineWrapperFactory wrapperFactory();
|
||||
|
||||
/**
|
||||
* Get the {@link ProtocolSelectorFactory}.
|
||||
*/
|
||||
ProtocolSelectorFactory protocolSelectorFactory();
|
||||
|
||||
/**
|
||||
* Get the {@link ProtocolSelectionListenerFactory}.
|
||||
*/
|
||||
ProtocolSelectionListenerFactory protocolListenerFactory();
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.handler.ssl.ApplicationProtocolUtil.toList;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
/**
|
||||
* Common base class for {@link JdkApplicationProtocolNegotiator} classes to inherit from.
|
||||
*/
|
||||
class JdkBaseApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator {
|
||||
private final List<String> protocols;
|
||||
private final ProtocolSelectorFactory selectorFactory;
|
||||
private final ProtocolSelectionListenerFactory listenerFactory;
|
||||
private final SslEngineWrapperFactory wrapperFactory;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
|
||||
* @param selectorFactory How the peer selecting the protocol should behave.
|
||||
* @param listenerFactory How the peer being notified of the selected protocol should behave.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
protected JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
|
||||
ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
|
||||
Iterable<String> protocols) {
|
||||
this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
|
||||
* @param selectorFactory How the peer selecting the protocol should behave.
|
||||
* @param listenerFactory How the peer being notified of the selected protocol should behave.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
protected JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
|
||||
ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
|
||||
String... protocols) {
|
||||
this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use.
|
||||
* @param selectorFactory How the peer selecting the protocol should behave.
|
||||
* @param listenerFactory How the peer being notified of the selected protocol should behave.
|
||||
* @param protocols The order of iteration determines the preference of support for protocols.
|
||||
*/
|
||||
private JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory,
|
||||
ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory,
|
||||
List<String> protocols) {
|
||||
this.wrapperFactory = checkNotNull(wrapperFactory, "wrapperFactory");
|
||||
this.selectorFactory = checkNotNull(selectorFactory, "selectorFactory");
|
||||
this.listenerFactory = checkNotNull(listenerFactory, "listenerFactory");
|
||||
this.protocols = Collections.unmodifiableList(checkNotNull(protocols, "protocols"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectorFactory protocolSelectorFactory() {
|
||||
return selectorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectionListenerFactory protocolListenerFactory() {
|
||||
return listenerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslEngineWrapperFactory wrapperFactory() {
|
||||
return wrapperFactory;
|
||||
}
|
||||
|
||||
static final ProtocolSelectorFactory FAIL_SELECTOR_FACTORY = new ProtocolSelectorFactory() {
|
||||
@Override
|
||||
public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
|
||||
return new FailProtocolSelector((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
static final ProtocolSelectorFactory NO_FAIL_SELECTOR_FACTORY = new ProtocolSelectorFactory() {
|
||||
@Override
|
||||
public ProtocolSelector newSelector(SSLEngine engine, Set<String> supportedProtocols) {
|
||||
return new NoFailProtocolSelector((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
static final ProtocolSelectionListenerFactory FAIL_SELECTION_LISTENER_FACTORY =
|
||||
new ProtocolSelectionListenerFactory() {
|
||||
@Override
|
||||
public ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols) {
|
||||
return new FailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
static final ProtocolSelectionListenerFactory NO_FAIL_SELECTION_LISTENER_FACTORY =
|
||||
new ProtocolSelectionListenerFactory() {
|
||||
@Override
|
||||
public ProtocolSelectionListener newListener(SSLEngine engine, List<String> supportedProtocols) {
|
||||
return new NoFailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols);
|
||||
}
|
||||
};
|
||||
|
||||
protected static class NoFailProtocolSelector implements ProtocolSelector {
|
||||
private final JdkSslEngine jettyWrapper;
|
||||
private final Set<String> supportedProtocols;
|
||||
|
||||
public NoFailProtocolSelector(JdkSslEngine jettyWrapper, Set<String> supportedProtocols) {
|
||||
this.jettyWrapper = jettyWrapper;
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
jettyWrapper.getSession().setApplicationProtocol(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String select(List<String> protocols) throws Exception {
|
||||
for (int i = 0; i < protocols.size(); ++i) {
|
||||
String p = protocols.get(i);
|
||||
if (supportedProtocols.contains(p)) {
|
||||
jettyWrapper.getSession().setApplicationProtocol(p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return noSelectMatchFound();
|
||||
}
|
||||
|
||||
public String noSelectMatchFound() throws Exception {
|
||||
jettyWrapper.getSession().setApplicationProtocol(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class FailProtocolSelector extends NoFailProtocolSelector {
|
||||
public FailProtocolSelector(JdkSslEngine jettyWrapper, Set<String> supportedProtocols) {
|
||||
super(jettyWrapper, supportedProtocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String noSelectMatchFound() throws Exception {
|
||||
throw new SSLHandshakeException("Selected protocol is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
protected static class NoFailProtocolSelectionListener implements ProtocolSelectionListener {
|
||||
private final JdkSslEngine jettyWrapper;
|
||||
private final List<String> supportedProtocols;
|
||||
|
||||
public NoFailProtocolSelectionListener(JdkSslEngine jettyWrapper, List<String> supportedProtocols) {
|
||||
this.jettyWrapper = jettyWrapper;
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
jettyWrapper.getSession().setApplicationProtocol(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selected(String protocol) throws Exception {
|
||||
if (supportedProtocols.contains(protocol)) {
|
||||
jettyWrapper.getSession().setApplicationProtocol(protocol);
|
||||
} else {
|
||||
noSelectedMatchFound(protocol);
|
||||
}
|
||||
}
|
||||
|
||||
public void noSelectedMatchFound(String protocol) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class FailProtocolSelectionListener extends NoFailProtocolSelectionListener {
|
||||
public FailProtocolSelectionListener(JdkSslEngine jettyWrapper, List<String> supportedProtocols) {
|
||||
super(jettyWrapper, supportedProtocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noSelectedMatchFound(String protocol) throws Exception {
|
||||
throw new SSLHandshakeException("No compatible protocols found");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* The {@link JdkApplicationProtocolNegotiator} to use if you do not care about NPN or ALPN and are using
|
||||
* {@link SslProvider#JDK}.
|
||||
*/
|
||||
final class JdkDefaultApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator {
|
||||
public static final JdkDefaultApplicationProtocolNegotiator INSTANCE =
|
||||
new JdkDefaultApplicationProtocolNegotiator();
|
||||
private static final SslEngineWrapperFactory DEFAULT_SSL_ENGINE_WRAPPER_FACTORY = new SslEngineWrapperFactory() {
|
||||
@Override
|
||||
public SSLEngine wrapSslEngine(SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator,
|
||||
boolean isServer) {
|
||||
return engine;
|
||||
}
|
||||
};
|
||||
|
||||
private JdkDefaultApplicationProtocolNegotiator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslEngineWrapperFactory wrapperFactory() {
|
||||
return DEFAULT_SSL_ENGINE_WRAPPER_FACTORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectorFactory protocolSelectorFactory() {
|
||||
throw new UnsupportedOperationException("Application protocol negotiation unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolSelectionListenerFactory protocolListenerFactory() {
|
||||
throw new UnsupportedOperationException("Application protocol negotiation unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
127
handler/src/main/java/io/netty/handler/ssl/JdkNpnSslEngine.java
Normal file
127
handler/src/main/java/io/netty/handler/ssl/JdkNpnSslEngine.java
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.HashSet;
|
||||
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 {
|
||||
// Try to get the bootstrap class loader.
|
||||
ClassLoader bootloader = ClassLoader.getSystemClassLoader().getParent();
|
||||
if (bootloader == null) {
|
||||
// If failed, use the system class loader,
|
||||
// although it's not perfect to tell if NPN extension has been loaded.
|
||||
bootloader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
Class.forName("sun.security.ssl.NextProtoNegoExtension", true, bootloader);
|
||||
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 HashSet<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();
|
||||
}
|
||||
}
|
@ -16,22 +16,16 @@
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.File;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A client-side {@link SslContext} which uses JDK's SSL/TLS implementation.
|
||||
@ -39,13 +33,12 @@ import java.util.List;
|
||||
public final class JdkSslClientContext extends JdkSslContext {
|
||||
|
||||
private final SSLContext ctx;
|
||||
private final List<String> nextProtocols;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public JdkSslClientContext() throws SSLException {
|
||||
this(null, null, null, null, 0, 0);
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +72,8 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
* {@code null} to use the default.
|
||||
*/
|
||||
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
this(certChainFile, trustManagerFactory, null, null, 0, 0);
|
||||
this(certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,8 +86,8 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
* {@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 nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @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.
|
||||
@ -101,65 +95,120 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
*/
|
||||
public JdkSslClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, trustManagerFactory, ciphers, cipherFilter,
|
||||
toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
super(ciphers);
|
||||
/**
|
||||
* 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.
|
||||
* @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 JdkSslClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, trustManagerFactory, null, null, null, null,
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
||||
if (!JettyNpnSslEngine.isAvailable()) {
|
||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
||||
}
|
||||
/**
|
||||
* 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 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 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 JdkSslClientContext(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 {
|
||||
this(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
|
||||
ciphers, cipherFilter, toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
List<String> nextProtoList = new ArrayList<String>();
|
||||
for (String p: nextProtocols) {
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
nextProtoList.add(p);
|
||||
}
|
||||
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
|
||||
} else {
|
||||
this.nextProtocols = Collections.emptyList();
|
||||
}
|
||||
/**
|
||||
* 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 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 JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
|
||||
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
super(ciphers, cipherFilter, apn);
|
||||
|
||||
try {
|
||||
if (certChainFile == null) {
|
||||
ctx = SSLContext.getInstance(PROTOCOL);
|
||||
if (trustManagerFactory == null) {
|
||||
ctx.init(null, null, null);
|
||||
} else {
|
||||
trustManagerFactory.init((KeyStore) null);
|
||||
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||
}
|
||||
} else {
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(null, null);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
|
||||
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
||||
try {
|
||||
for (ByteBuf buf: certs) {
|
||||
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(buf));
|
||||
X500Principal principal = cert.getSubjectX500Principal();
|
||||
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
|
||||
}
|
||||
} finally {
|
||||
for (ByteBuf buf: certs) {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Set up trust manager factory to use our key store.
|
||||
if (trustManagerFactory == null) {
|
||||
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
}
|
||||
trustManagerFactory.init(ks);
|
||||
|
||||
// Initialize the SSLContext to work with the trust managers.
|
||||
ctx = SSLContext.getInstance(PROTOCOL);
|
||||
ctx.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||
if (trustCertChainFile != null) {
|
||||
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
|
||||
}
|
||||
if (keyFile != null) {
|
||||
keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory);
|
||||
}
|
||||
ctx = SSLContext.getInstance(PROTOCOL);
|
||||
ctx.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(),
|
||||
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
|
||||
null);
|
||||
|
||||
SSLSessionContext sessCtx = ctx.getClientSessionContext();
|
||||
if (sessionCacheSize > 0) {
|
||||
@ -169,7 +218,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SSLException("failed to initialize the server-side SSL context", e);
|
||||
throw new SSLException("failed to initialize the client-side SSL context", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,11 +227,6 @@ public final class JdkSslClientContext extends JdkSslContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return nextProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLContext context() {
|
||||
return ctx;
|
||||
|
@ -16,17 +16,50 @@
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
/**
|
||||
* An {@link SslContext} which uses JDK's SSL/TLS implementation.
|
||||
@ -38,9 +71,11 @@ public abstract class JdkSslContext extends SslContext {
|
||||
static final String PROTOCOL = "TLS";
|
||||
static final String[] PROTOCOLS;
|
||||
static final List<String> DEFAULT_CIPHERS;
|
||||
static final Set<String> SUPPORTED_CIPHERS;
|
||||
|
||||
static {
|
||||
SSLContext context;
|
||||
int i;
|
||||
try {
|
||||
context = SSLContext.getInstance(PROTOCOL);
|
||||
context.init(null, null, null);
|
||||
@ -51,10 +86,14 @@ public abstract class JdkSslContext extends SslContext {
|
||||
SSLEngine engine = context.createSSLEngine();
|
||||
|
||||
// Choose the sensible default list of protocols.
|
||||
String[] supportedProtocols = engine.getSupportedProtocols();
|
||||
final String[] supportedProtocols = engine.getSupportedProtocols();
|
||||
Set<String> supportedProtocolsSet = new HashSet<String>(supportedProtocols.length);
|
||||
for (i = 0; i < supportedProtocols.length; ++i) {
|
||||
supportedProtocolsSet.add(supportedProtocols[i]);
|
||||
}
|
||||
List<String> protocols = new ArrayList<String>();
|
||||
addIfSupported(
|
||||
supportedProtocols, protocols,
|
||||
supportedProtocolsSet, protocols,
|
||||
"TLSv1.2", "TLSv1.1", "TLSv1");
|
||||
|
||||
if (!protocols.isEmpty()) {
|
||||
@ -64,10 +103,14 @@ public abstract class JdkSslContext extends SslContext {
|
||||
}
|
||||
|
||||
// Choose the sensible default list of cipher suites.
|
||||
String[] supportedCiphers = engine.getSupportedCipherSuites();
|
||||
final String[] supportedCiphers = engine.getSupportedCipherSuites();
|
||||
SUPPORTED_CIPHERS = new HashSet<String>(supportedCiphers.length);
|
||||
for (i = 0; i < supportedCiphers.length; ++i) {
|
||||
SUPPORTED_CIPHERS.add(supportedCiphers[i]);
|
||||
}
|
||||
List<String> ciphers = new ArrayList<String>();
|
||||
addIfSupported(
|
||||
supportedCiphers, ciphers,
|
||||
SUPPORTED_CIPHERS, ciphers,
|
||||
// XXX: Make sure to sync this list with OpenSslEngineFactory.
|
||||
// GCM (Galois/Counter Mode) requires JDK 8.
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
@ -97,22 +140,28 @@ public abstract class JdkSslContext extends SslContext {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addIfSupported(String[] supported, List<String> enabled, String... names) {
|
||||
for (String n: names) {
|
||||
for (String s: supported) {
|
||||
if (n.equals(s)) {
|
||||
enabled.add(s);
|
||||
break;
|
||||
}
|
||||
private static void addIfSupported(Set<String> supported, List<String> enabled, String... names) {
|
||||
for (int i = 0; i < names.length; ++i) {
|
||||
String n = names[i];
|
||||
if (supported.contains(n)) {
|
||||
enabled.add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final String[] cipherSuites;
|
||||
private final List<String> unmodifiableCipherSuites;
|
||||
private final JdkApplicationProtocolNegotiator apn;
|
||||
|
||||
JdkSslContext(Iterable<String> ciphers) {
|
||||
cipherSuites = toCipherSuiteArray(ciphers);
|
||||
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config,
|
||||
boolean isServer) {
|
||||
this(ciphers, cipherFilter, toNegotiator(config, isServer));
|
||||
}
|
||||
|
||||
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn) {
|
||||
this.apn = checkNotNull(apn, "apn");
|
||||
cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
|
||||
ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS);
|
||||
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
|
||||
}
|
||||
|
||||
@ -166,25 +215,227 @@ public abstract class JdkSslContext extends SslContext {
|
||||
}
|
||||
|
||||
private SSLEngine wrapEngine(SSLEngine engine) {
|
||||
if (nextProtocols().isEmpty()) {
|
||||
return engine;
|
||||
} else {
|
||||
return new JettyNpnSslEngine(engine, nextProtocols(), isServer());
|
||||
return apn.wrapperFactory().wrapSslEngine(engine, apn, isServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdkApplicationProtocolNegotiator applicationProtocolNegotiator() {
|
||||
return apn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a {@link ApplicationProtocolConfiguration} object to a {@link JdkApplicationProtocolNegotiator} object.
|
||||
* @param config The configuration which defines the translation
|
||||
* @param isServer {@code true} if a server {@code false} otherwise.
|
||||
* @return The results of the translation
|
||||
*/
|
||||
static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) {
|
||||
if (config == null) {
|
||||
return JdkDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] toCipherSuiteArray(Iterable<String> ciphers) {
|
||||
if (ciphers == null) {
|
||||
return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
|
||||
} else {
|
||||
List<String> newCiphers = new ArrayList<String>();
|
||||
for (String c: ciphers) {
|
||||
if (c == null) {
|
||||
break;
|
||||
}
|
||||
newCiphers.add(c);
|
||||
}
|
||||
return newCiphers.toArray(new String[newCiphers.size()]);
|
||||
/**
|
||||
* Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
|
||||
* @param certChainFile a 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 kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
|
||||
* @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
|
||||
*/
|
||||
protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
|
||||
KeyManagerFactory kmf)
|
||||
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
|
||||
NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
|
||||
CertificateException, KeyException, IOException {
|
||||
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
|
||||
if (algorithm == null) {
|
||||
algorithm = "SunX509";
|
||||
}
|
||||
return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
|
||||
* and a certificate chain.
|
||||
* @param certChainFile a X.509 certificate chain file in PEM format
|
||||
* @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
|
||||
* Reference Guide for information about standard algorithm names.
|
||||
* @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 kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
|
||||
* @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
|
||||
* and a certificate chain.
|
||||
*/
|
||||
protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
|
||||
String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
|
||||
InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
|
||||
CertificateException, KeyException, UnrecoverableKeyException {
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(null, null);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
KeyFactory rsaKF = KeyFactory.getInstance("RSA");
|
||||
KeyFactory dsaKF = KeyFactory.getInstance("DSA");
|
||||
|
||||
ByteBuf encodedKeyBuf = PemReader.readPrivateKey(keyFile);
|
||||
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
|
||||
encodedKeyBuf.readBytes(encodedKey).release();
|
||||
|
||||
char[] keyPasswordChars = keyPassword == null ? new char[0] : keyPassword.toCharArray();
|
||||
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey);
|
||||
|
||||
PrivateKey key;
|
||||
try {
|
||||
key = rsaKF.generatePrivate(encodedKeySpec);
|
||||
} catch (InvalidKeySpecException ignore) {
|
||||
key = dsaKF.generatePrivate(encodedKeySpec);
|
||||
}
|
||||
|
||||
List<Certificate> certChain = new ArrayList<Certificate>();
|
||||
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
||||
try {
|
||||
for (ByteBuf buf: certs) {
|
||||
certChain.add(cf.generateCertificate(new ByteBufInputStream(buf)));
|
||||
}
|
||||
} finally {
|
||||
for (ByteBuf buf: certs) {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[certChain.size()]));
|
||||
|
||||
// Set up key manager factory to use our key store
|
||||
if (kmf == null) {
|
||||
kmf = KeyManagerFactory.getInstance(keyAlgorithm);
|
||||
}
|
||||
kmf.init(ks, keyPasswordChars);
|
||||
|
||||
return kmf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link TrustManagerFactory} from a certificate chain file.
|
||||
* @param certChainFile The certificate file to build from.
|
||||
* @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}.
|
||||
* @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
|
||||
*/
|
||||
protected static TrustManagerFactory buildTrustManagerFactory(File certChainFile,
|
||||
TrustManagerFactory trustManagerFactory)
|
||||
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(null, null);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
|
||||
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
||||
try {
|
||||
for (ByteBuf buf: certs) {
|
||||
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(buf));
|
||||
X500Principal principal = cert.getSubjectX500Principal();
|
||||
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
|
||||
}
|
||||
} finally {
|
||||
for (ByteBuf buf: certs) {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Set up trust manager factory to use our key store.
|
||||
if (trustManagerFactory == null) {
|
||||
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
}
|
||||
trustManagerFactory.init(ks);
|
||||
|
||||
return trustManagerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key specification for an (encrypted) private key.
|
||||
*
|
||||
* @param password characters, if {@code null} or empty an unencrypted key is assumed
|
||||
* @param key bytes of the DER encoded private key
|
||||
*
|
||||
* @return a key specification
|
||||
*
|
||||
* @throws IOException if parsing {@code key} fails
|
||||
* @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unkown
|
||||
* @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unkown
|
||||
* @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
|
||||
* @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
|
||||
* {@code key}
|
||||
* @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
|
||||
*/
|
||||
private static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
|
||||
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
|
||||
if (password == null || password.length == 0) {
|
||||
return new PKCS8EncodedKeySpec(key);
|
||||
}
|
||||
|
||||
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
|
||||
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
|
||||
|
||||
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
|
||||
|
||||
return encryptedPrivateKeyInfo.getKeySpec(cipher);
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,9 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import org.eclipse.jetty.npn.NextProtoNego;
|
||||
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
||||
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
@ -26,105 +23,32 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
final class JettyNpnSslEngine extends SSLEngine {
|
||||
|
||||
private static boolean available;
|
||||
|
||||
static boolean isAvailable() {
|
||||
updateAvailability();
|
||||
return available;
|
||||
}
|
||||
|
||||
private static void updateAvailability() {
|
||||
if (available) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Try to get the bootstrap class loader.
|
||||
ClassLoader bootloader = ClassLoader.getSystemClassLoader().getParent();
|
||||
if (bootloader == null) {
|
||||
// If failed, use the system class loader,
|
||||
// although it's not perfect to tell if NPN extension has been loaded.
|
||||
bootloader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
Class.forName("sun.security.ssl.NextProtoNegoExtension", true, bootloader);
|
||||
available = true;
|
||||
} catch (Exception ignore) {
|
||||
// npn-boot was not loaded.
|
||||
}
|
||||
}
|
||||
|
||||
class JdkSslEngine extends SSLEngine {
|
||||
private final SSLEngine engine;
|
||||
private final JettyNpnSslSession session;
|
||||
|
||||
JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
||||
assert !nextProtocols.isEmpty();
|
||||
private final JdkSslSession session;
|
||||
|
||||
JdkSslEngine(SSLEngine engine) {
|
||||
this.engine = engine;
|
||||
session = new JettyNpnSslSession(engine);
|
||||
|
||||
if (server) {
|
||||
NextProtoNego.put(engine, new ServerProvider() {
|
||||
@Override
|
||||
public void unsupported() {
|
||||
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return nextProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void protocolSelected(String protocol) {
|
||||
getSession().setApplicationProtocol(protocol);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final String[] list = nextProtocols.toArray(new String[nextProtocols.size()]);
|
||||
final String fallback = list[list.length - 1];
|
||||
|
||||
NextProtoNego.put(engine, new ClientProvider() {
|
||||
@Override
|
||||
public boolean supports() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsupported() {
|
||||
session.setApplicationProtocol(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String selectProtocol(List<String> protocols) {
|
||||
for (String p: list) {
|
||||
if (protocols.contains(p)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
session = new JdkSslSession(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JettyNpnSslSession getSession() {
|
||||
public JdkSslSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public SSLEngine getWrappedEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInbound() throws SSLException {
|
||||
NextProtoNego.remove(engine);
|
||||
engine.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOutbound() {
|
||||
NextProtoNego.remove(engine);
|
||||
engine.closeOutbound();
|
||||
}
|
||||
|
@ -16,35 +16,15 @@
|
||||
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import java.io.File;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Security;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
/**
|
||||
* A server-side {@link SslContext} which uses JDK's SSL/TLS implementation.
|
||||
@ -52,7 +32,6 @@ import java.util.List;
|
||||
public final class JdkSslServerContext extends JdkSslContext {
|
||||
|
||||
private final SSLContext ctx;
|
||||
private final List<String> nextProtocols;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -73,7 +52,8 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
* {@code null} if it's not password-protected.
|
||||
*/
|
||||
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,8 +65,8 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
* {@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 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.
|
||||
@ -94,87 +74,118 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
*/
|
||||
public JdkSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, ciphers, cipherFilter,
|
||||
toNegotiator(apn, true), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
super(ciphers);
|
||||
/**
|
||||
* 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 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 JdkSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(null, null, certChainFile, keyFile, keyPassword, null,
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
if (certChainFile == null) {
|
||||
throw new NullPointerException("certChainFile");
|
||||
}
|
||||
if (keyFile == null) {
|
||||
throw new NullPointerException("keyFile");
|
||||
}
|
||||
/**
|
||||
* 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 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 JdkSslServerContext(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 {
|
||||
this(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory,
|
||||
ciphers, cipherFilter, toNegotiator(apn, true), sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
if (keyPassword == null) {
|
||||
keyPassword = "";
|
||||
}
|
||||
|
||||
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
||||
if (!JettyNpnSslEngine.isAvailable()) {
|
||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String p: nextProtocols) {
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
list.add(p);
|
||||
}
|
||||
|
||||
this.nextProtocols = Collections.unmodifiableList(list);
|
||||
} else {
|
||||
this.nextProtocols = Collections.emptyList();
|
||||
}
|
||||
|
||||
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
|
||||
if (algorithm == null) {
|
||||
algorithm = "SunX509";
|
||||
/**
|
||||
* 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 JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
|
||||
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
super(ciphers, cipherFilter, apn);
|
||||
if (keyFile == null && keyManagerFactory == null) {
|
||||
throw new NullPointerException("keyFile, keyManagerFactory");
|
||||
}
|
||||
|
||||
try {
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(null, null);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
KeyFactory rsaKF = KeyFactory.getInstance("RSA");
|
||||
KeyFactory dsaKF = KeyFactory.getInstance("DSA");
|
||||
|
||||
ByteBuf encodedKeyBuf = PemReader.readPrivateKey(keyFile);
|
||||
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
|
||||
encodedKeyBuf.readBytes(encodedKey).release();
|
||||
|
||||
char[] keyPasswordChars = keyPassword.toCharArray();
|
||||
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey);
|
||||
|
||||
PrivateKey key;
|
||||
try {
|
||||
key = rsaKF.generatePrivate(encodedKeySpec);
|
||||
} catch (InvalidKeySpecException ignore) {
|
||||
key = dsaKF.generatePrivate(encodedKeySpec);
|
||||
if (trustCertChainFile != null) {
|
||||
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
|
||||
}
|
||||
|
||||
List<Certificate> certChain = new ArrayList<Certificate>();
|
||||
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
||||
try {
|
||||
for (ByteBuf buf: certs) {
|
||||
certChain.add(cf.generateCertificate(new ByteBufInputStream(buf)));
|
||||
}
|
||||
} finally {
|
||||
for (ByteBuf buf: certs) {
|
||||
buf.release();
|
||||
}
|
||||
if (keyFile != null) {
|
||||
keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory);
|
||||
}
|
||||
|
||||
ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[certChain.size()]));
|
||||
|
||||
// Set up key manager factory to use our key store
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
|
||||
kmf.init(ks, keyPasswordChars);
|
||||
|
||||
// Initialize the SSLContext to work with our key managers.
|
||||
ctx = SSLContext.getInstance(PROTOCOL);
|
||||
ctx.init(kmf.getKeyManagers(), null, null);
|
||||
ctx.init(keyManagerFactory.getKeyManagers(),
|
||||
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
|
||||
null);
|
||||
|
||||
SSLSessionContext sessCtx = ctx.getServerSessionContext();
|
||||
if (sessionCacheSize > 0) {
|
||||
@ -193,48 +204,8 @@ public final class JdkSslServerContext extends JdkSslContext {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return nextProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLContext context() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a key specification for an (encrypted) private key.
|
||||
*
|
||||
* @param password characters, if {@code null} or empty an unencrypted key is assumed
|
||||
* @param key bytes of the DER encoded private key
|
||||
*
|
||||
* @return a key specification
|
||||
*
|
||||
* @throws IOException if parsing {@code key} fails
|
||||
* @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unkown
|
||||
* @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unkown
|
||||
* @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
|
||||
* @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
|
||||
* {@code key}
|
||||
* @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
|
||||
*/
|
||||
private static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
|
||||
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
|
||||
if (password == null || password.length == 0) {
|
||||
return new PKCS8EncodedKeySpec(key);
|
||||
}
|
||||
|
||||
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
|
||||
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
|
||||
|
||||
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
|
||||
|
||||
return encryptedPrivateKeyInfo.getKeySpec(cipher);
|
||||
}
|
||||
}
|
||||
|
169
handler/src/main/java/io/netty/handler/ssl/JdkSslSession.java
Normal file
169
handler/src/main/java/io/netty/handler/ssl/JdkSslSession.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
import javax.security.cert.X509Certificate;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
final class JdkSslSession implements SSLSession {
|
||||
private final SSLEngine engine;
|
||||
private volatile String applicationProtocol;
|
||||
|
||||
JdkSslSession(SSLEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
void setApplicationProtocol(String applicationProtocol) {
|
||||
if (applicationProtocol != null) {
|
||||
applicationProtocol = applicationProtocol.replace(':', '_');
|
||||
}
|
||||
this.applicationProtocol = applicationProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
final String protocol = unwrap().getProtocol();
|
||||
final String applicationProtocol = this.applicationProtocol;
|
||||
|
||||
if (applicationProtocol == null) {
|
||||
if (protocol != null) {
|
||||
return protocol.replace(':', '_');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final StringBuilder buf = new StringBuilder(32);
|
||||
if (protocol != null) {
|
||||
buf.append(protocol.replace(':', '_'));
|
||||
buf.append(':');
|
||||
} else {
|
||||
buf.append("null:");
|
||||
}
|
||||
buf.append(applicationProtocol);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private SSLSession unwrap() {
|
||||
return engine.getSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getId() {
|
||||
return unwrap().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLSessionContext getSessionContext() {
|
||||
return unwrap().getSessionContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreationTime() {
|
||||
return unwrap().getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastAccessedTime() {
|
||||
return unwrap().getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
unwrap().invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return unwrap().isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putValue(String s, Object o) {
|
||||
unwrap().putValue(s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(String s) {
|
||||
return unwrap().getValue(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeValue(String s) {
|
||||
unwrap().removeValue(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getValueNames() {
|
||||
return unwrap().getValueNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
|
||||
return unwrap().getPeerCertificates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate[] getLocalCertificates() {
|
||||
return unwrap().getLocalCertificates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
|
||||
return unwrap().getPeerCertificateChain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
|
||||
return unwrap().getPeerPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getLocalPrincipal() {
|
||||
return unwrap().getLocalPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCipherSuite() {
|
||||
return unwrap().getCipherSuite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPeerHost() {
|
||||
return unwrap().getPeerHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeerPort() {
|
||||
return unwrap().getPeerPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPacketBufferSize() {
|
||||
return unwrap().getPacketBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getApplicationBufferSize() {
|
||||
return unwrap().getApplicationBufferSize();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* OpenSSL version of {@link ApplicationProtocolNegotiator}.
|
||||
*/
|
||||
public interface OpenSslApplicationProtocolNegotiator extends ApplicationProtocolNegotiator {
|
||||
// The current need for this interface is primarily for consistency with the JDK provider
|
||||
// How the OpenSsl provider will provide extensibility to control the application selection
|
||||
// and notification algorithms is not yet known (JNI, pure java, tcnative hooks, etc...).
|
||||
// OpenSSL itself is currently not in compliance with the specification for the 2 supported
|
||||
// protocols (ALPN, NPN) with respect to allowing the handshakes to fail during the application
|
||||
// protocol negotiation process. Issue https://github.com/openssl/openssl/issues/188 has been created for this.
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides no {@link ApplicationProtocolNegotiator} functionality for OpenSSL.
|
||||
*/
|
||||
final class OpenSslDefaultApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
|
||||
static final OpenSslDefaultApplicationProtocolNegotiator INSTANCE =
|
||||
new OpenSslDefaultApplicationProtocolNegotiator();
|
||||
|
||||
private OpenSslDefaultApplicationProtocolNegotiator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.handler.ssl.ApplicationProtocolUtil.toList;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OpenSSL {@link ApplicationProtocolNegotiator} for NPN.
|
||||
*/
|
||||
public final class OpenSslNpnApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator {
|
||||
private final List<String> protocols;
|
||||
|
||||
public OpenSslNpnApplicationProtocolNegotiator(Iterable<String> protocols) {
|
||||
this.protocols = checkNotNull(toList(protocols), "protocols");
|
||||
}
|
||||
|
||||
public OpenSslNpnApplicationProtocolNegotiator(String... protocols) {
|
||||
this.protocols = checkNotNull(toList(protocols), "protocols");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> protocols() {
|
||||
return protocols;
|
||||
}
|
||||
}
|
@ -15,20 +15,23 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
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 java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.apache.tomcat.jni.Pool;
|
||||
import org.apache.tomcat.jni.SSL;
|
||||
import org.apache.tomcat.jni.SSLContext;
|
||||
|
||||
/**
|
||||
* A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
||||
*/
|
||||
@ -65,7 +68,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
private final List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers);
|
||||
private final long sessionCacheSize;
|
||||
private final long sessionTimeout;
|
||||
private final List<String> nextProtocols;
|
||||
private final OpenSslApplicationProtocolNegotiator apn;
|
||||
|
||||
/** The OpenSSL SSL_CTX object */
|
||||
private final long ctx;
|
||||
@ -90,7 +93,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
* {@code null} if it's not password-protected.
|
||||
*/
|
||||
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
this(certChainFile, keyFile, keyPassword, null, OpenSslDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,8 +105,7 @@ public final class OpenSslServerContext extends SslContext {
|
||||
* {@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 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.
|
||||
@ -111,20 +113,39 @@ public final class OpenSslServerContext extends SslContext {
|
||||
*/
|
||||
public OpenSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
this(certChainFile, keyFile, keyPassword, ciphers, toNegotiator(apn, false), 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.
|
||||
*/
|
||||
public OpenSslServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, OpenSslApplicationProtocolNegotiator apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
|
||||
OpenSsl.ensureAvailability();
|
||||
|
||||
if (certChainFile == null) {
|
||||
throw new NullPointerException("certChainFile");
|
||||
}
|
||||
checkNotNull(certChainFile, "certChainFile");
|
||||
if (!certChainFile.isFile()) {
|
||||
throw new IllegalArgumentException("certChainFile is not a file: " + certChainFile);
|
||||
}
|
||||
if (keyFile == null) {
|
||||
throw new NullPointerException("keyPath");
|
||||
}
|
||||
checkNotNull(keyFile, "keyFile");
|
||||
this.apn = checkNotNull(apn, "apn");
|
||||
if (!keyFile.isFile()) {
|
||||
throw new IllegalArgumentException("keyPath is not a file: " + keyFile);
|
||||
}
|
||||
@ -135,9 +156,6 @@ public final class OpenSslServerContext extends SslContext {
|
||||
if (keyPassword == null) {
|
||||
keyPassword = "";
|
||||
}
|
||||
if (nextProtocols == null) {
|
||||
nextProtocols = Collections.emptyList();
|
||||
}
|
||||
|
||||
for (String c: ciphers) {
|
||||
if (c == null) {
|
||||
@ -146,15 +164,6 @@ public final class OpenSslServerContext extends SslContext {
|
||||
this.ciphers.add(c);
|
||||
}
|
||||
|
||||
List<String> nextProtoList = new ArrayList<String>();
|
||||
for (String p: nextProtocols) {
|
||||
if (p == null) {
|
||||
break;
|
||||
}
|
||||
nextProtoList.add(p);
|
||||
}
|
||||
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
|
||||
|
||||
// Allocate a new APR pool.
|
||||
aprPool = Pool.create(0);
|
||||
|
||||
@ -219,11 +228,12 @@ public final class OpenSslServerContext extends SslContext {
|
||||
}
|
||||
|
||||
/* Set next protocols for next protocol negotiation extension, if specified */
|
||||
if (!nextProtoList.isEmpty()) {
|
||||
List<String> protocols = apn.protocols();
|
||||
if (!protocols.isEmpty()) {
|
||||
// Convert the protocol list into a comma-separated string.
|
||||
StringBuilder nextProtocolBuf = new StringBuilder();
|
||||
for (String p: nextProtoList) {
|
||||
nextProtocolBuf.append(p);
|
||||
for (int i = 0; i < protocols.size(); ++i) {
|
||||
nextProtocolBuf.append(protocols.get(i));
|
||||
nextProtocolBuf.append(',');
|
||||
}
|
||||
nextProtocolBuf.setLength(nextProtocolBuf.length() - 1);
|
||||
@ -283,9 +293,8 @@ public final class OpenSslServerContext extends SslContext {
|
||||
return sessionTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> nextProtocols() {
|
||||
return nextProtocols;
|
||||
public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
|
||||
return apn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,10 +316,11 @@ public final class OpenSslServerContext extends SslContext {
|
||||
*/
|
||||
@Override
|
||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||
if (nextProtocols.isEmpty()) {
|
||||
List<String> protocols = apn.protocols();
|
||||
if (protocols.isEmpty()) {
|
||||
return new OpenSslEngine(ctx, alloc, null);
|
||||
} else {
|
||||
return new OpenSslEngine(ctx, alloc, nextProtocols.get(nextProtocols.size() - 1));
|
||||
return new OpenSslEngine(ctx, alloc, protocols.get(protocols.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,4 +357,38 @@ public final class OpenSslServerContext extends SslContext {
|
||||
Pool.destroy(aprPool);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a {@link ApplicationProtocolConfiguration} object to a
|
||||
* {@link OpenSslApplicationProtocolNegotiator} object.
|
||||
* @param config The configuration which defines the translation
|
||||
* @param isServer {@code true} if a server {@code false} otherwise.
|
||||
* @return The results of the translation
|
||||
*/
|
||||
private static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config,
|
||||
boolean isServer) {
|
||||
if (config == null) {
|
||||
return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
}
|
||||
|
||||
switch(config.protocol()) {
|
||||
case NONE:
|
||||
return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||
case NPN:
|
||||
if (isServer) {
|
||||
switch(config.selectedListenerFailureBehavior()) {
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
return new OpenSslNpnApplicationProtocolNegotiator(config.supportedProtocols());
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.selectedListenerFailureBehavior()).append(" behavior").toString());
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException("OpenSSL provider does not support client mode");
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException(new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.protocol()).append(" protocol").toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,16 @@ import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
|
||||
@ -51,7 +54,6 @@ import java.util.List;
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class SslContext {
|
||||
|
||||
/**
|
||||
* Returns the default server-side implementation provider currently in use.
|
||||
*
|
||||
@ -82,7 +84,7 @@ public abstract class SslContext {
|
||||
* @return a new server-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
|
||||
return newServerContext(null, certChainFile, keyFile, null, null, null, 0, 0);
|
||||
return newServerContext(certChainFile, keyFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +98,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
return newServerContext(null, certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
return newServerContext(null, certChainFile, keyFile, keyPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,8 +110,8 @@ public abstract class SslContext {
|
||||
* {@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 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.
|
||||
@ -118,11 +120,11 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newServerContext(
|
||||
null, certChainFile, keyFile, keyPassword,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,7 +138,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
SslProvider provider, File certChainFile, File keyFile) throws SSLException {
|
||||
return newServerContext(provider, certChainFile, keyFile, null, null, null, 0, 0);
|
||||
return newServerContext(provider, certChainFile, keyFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,7 +154,8 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
||||
return newServerContext(provider, certChainFile, keyFile, keyPassword, null, null, 0, 0);
|
||||
return newServerContext(provider, certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,18 +169,58 @@ public abstract class SslContext {
|
||||
* {@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 cipherFilter a filter to apply over the supplied list of ciphers
|
||||
* Only required if {@code provider} is {@link SslProvider#JDK}
|
||||
* @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.
|
||||
* @return a new server-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newServerContext(
|
||||
SslProvider provider,
|
||||
public static SslContext newServerContext(SslProvider provider,
|
||||
File certChainFile, File keyFile, String keyPassword,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newServerContext(provider, null, null, certChainFile, keyFile, keyPassword, null,
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server-side {@link SslContext}.
|
||||
* @param provider the {@link SslContext} implementation to use.
|
||||
* {@code null} to use the current default one.
|
||||
* @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}.
|
||||
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
|
||||
* @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}.
|
||||
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
|
||||
* @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 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.
|
||||
* @return a new server-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newServerContext(SslProvider provider,
|
||||
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 {
|
||||
|
||||
if (provider == null) {
|
||||
@ -187,12 +230,15 @@ public abstract class SslContext {
|
||||
switch (provider) {
|
||||
case JDK:
|
||||
return new JdkSslServerContext(
|
||||
certChainFile, keyFile, keyPassword,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
||||
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
case OPENSSL:
|
||||
if (trustCertChainFile != null) {
|
||||
throw new UnsupportedOperationException("OpenSSL provider does not support mutual authentication");
|
||||
}
|
||||
return new OpenSslServerContext(
|
||||
certChainFile, keyFile, keyPassword,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
keyCertChainFile, keyFile, keyPassword,
|
||||
ciphers, apn, sessionCacheSize, sessionTimeout);
|
||||
default:
|
||||
throw new Error(provider.toString());
|
||||
}
|
||||
@ -204,7 +250,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext() throws SSLException {
|
||||
return newClientContext(null, null, null, null, null, 0, 0);
|
||||
return newClientContext(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,7 +261,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(File certChainFile) throws SSLException {
|
||||
return newClientContext(null, certChainFile, null, null, null, 0, 0);
|
||||
return newClientContext(null, certChainFile);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +274,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(null, null, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(null, null, trustManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,7 +290,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(null, certChainFile, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(null, certChainFile, trustManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -257,8 +303,8 @@ public abstract class SslContext {
|
||||
* {@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 nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @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.
|
||||
@ -268,11 +314,11 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newClientContext(
|
||||
null, certChainFile, trustManagerFactory,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -284,7 +330,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(SslProvider provider) throws SSLException {
|
||||
return newClientContext(provider, null, null, null, null, 0, 0);
|
||||
return newClientContext(provider, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -298,7 +344,7 @@ public abstract class SslContext {
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
|
||||
return newClientContext(provider, certChainFile, null, null, null, 0, 0);
|
||||
return newClientContext(provider, certChainFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,7 +360,7 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(provider, null, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(provider, trustManagerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,7 +378,8 @@ public abstract class SslContext {
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
||||
return newClientContext(provider, certChainFile, trustManagerFactory, null, null, 0, 0);
|
||||
return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||
null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -347,8 +394,8 @@ public abstract class SslContext {
|
||||
* {@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 nextProtocols the application layer protocols to accept, in the order of preference.
|
||||
* {@code null} to disable TLS NPN/ALPN extension.
|
||||
* @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.
|
||||
@ -356,19 +403,61 @@ public abstract class SslContext {
|
||||
*
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(
|
||||
SslProvider provider,
|
||||
public static SslContext newClientContext(SslProvider provider,
|
||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
||||
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||
return newClientContext(provider, certChainFile, trustManagerFactory, null, null, null, null,
|
||||
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client-side {@link SslContext}.
|
||||
* @param provider the {@link SslContext} implementation to use.
|
||||
* {@code null} to use the current default one.
|
||||
* @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}.
|
||||
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
|
||||
* @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 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}.
|
||||
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
|
||||
* @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.
|
||||
*
|
||||
* @return a new client-side {@link SslContext}
|
||||
*/
|
||||
public static SslContext newClientContext(SslProvider provider,
|
||||
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 {
|
||||
|
||||
if (provider != null && provider != SslProvider.JDK) {
|
||||
throw new SSLException("client context unsupported for: " + provider);
|
||||
}
|
||||
|
||||
return new JdkSslClientContext(
|
||||
certChainFile, trustManagerFactory,
|
||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
||||
return new JdkSslClientContext(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
||||
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||
}
|
||||
|
||||
SslContext() { }
|
||||
@ -401,12 +490,9 @@ public abstract class SslContext {
|
||||
public abstract long sessionTimeout();
|
||||
|
||||
/**
|
||||
* Returns the list of application layer protocols for the TLS NPN/ALPN extension, in the order of preference.
|
||||
*
|
||||
* @return the list of application layer protocols.
|
||||
* {@code null} if NPN/ALPN extension has been disabled.
|
||||
* Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
|
||||
*/
|
||||
public abstract List<String> nextProtocols();
|
||||
public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
|
||||
|
||||
/**
|
||||
* Creates a new {@link SSLEngine}.
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class will filter all requested ciphers out that are not supported by the current {@link SSLEngine}.
|
||||
*/
|
||||
public final class SupportedCipherSuiteFilter implements CipherSuiteFilter {
|
||||
public static final SupportedCipherSuiteFilter INSTANCE = new SupportedCipherSuiteFilter();
|
||||
|
||||
private SupportedCipherSuiteFilter() { }
|
||||
|
||||
@Override
|
||||
public String[] filterCipherSuites(Iterable<String> ciphers, List<String> defaultCiphers,
|
||||
Set<String> supportedCiphers) {
|
||||
if (defaultCiphers == null) {
|
||||
throw new NullPointerException("defaultCiphers");
|
||||
}
|
||||
if (supportedCiphers == null) {
|
||||
throw new NullPointerException("supportedCiphers");
|
||||
}
|
||||
|
||||
final List<String> newCiphers;
|
||||
if (ciphers == null) {
|
||||
newCiphers = new ArrayList<String>(defaultCiphers.size());
|
||||
ciphers = defaultCiphers;
|
||||
} else {
|
||||
newCiphers = new ArrayList<String>(supportedCiphers.size());
|
||||
}
|
||||
for (String c : ciphers) {
|
||||
if (c == null) {
|
||||
break;
|
||||
}
|
||||
if (supportedCiphers.contains(c)) {
|
||||
newCiphers.add(c);
|
||||
}
|
||||
}
|
||||
return newCiphers.toArray(new String[newCiphers.size()]);
|
||||
}
|
||||
|
||||
}
|
568
handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java
Normal file
568
handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java
Normal file
@ -0,0 +1,568 @@
|
||||
/*
|
||||
* 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.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
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.SimpleChannelInboundHandler;
|
||||
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 io.netty.util.concurrent.Future;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class JdkSslEngineTest {
|
||||
private static final String APPLICATION_LEVEL_PROTOCOL = "my-protocol";
|
||||
private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO";
|
||||
|
||||
@Mock
|
||||
private MessageReciever serverReceiver;
|
||||
@Mock
|
||||
private MessageReciever clientReceiver;
|
||||
|
||||
private Throwable serverException;
|
||||
private Throwable clientException;
|
||||
private SslContext serverSslCtx;
|
||||
private SslContext clientSslCtx;
|
||||
private ServerBootstrap sb;
|
||||
private Bootstrap cb;
|
||||
private Channel serverChannel;
|
||||
private Channel serverConnectedChannel;
|
||||
private Channel clientChannel;
|
||||
private CountDownLatch serverLatch;
|
||||
private CountDownLatch clientLatch;
|
||||
|
||||
private interface MessageReciever {
|
||||
void messageReceived(ByteBuf msg);
|
||||
}
|
||||
|
||||
private final class MessageDelegatorChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
private final MessageReciever receiver;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
public MessageDelegatorChannelHandler(MessageReciever receiver, CountDownLatch latch) {
|
||||
super(false);
|
||||
this.receiver = receiver;
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
|
||||
receiver.messageReceived(msg);
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
serverLatch = new CountDownLatch(1);
|
||||
clientLatch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws InterruptedException {
|
||||
if (serverChannel != null) {
|
||||
serverChannel.close().sync();
|
||||
Future<?> serverGroup = sb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
Future<?> serverChildGroup = sb.childGroup().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
Future<?> clientGroup = cb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS);
|
||||
serverGroup.sync();
|
||||
serverChildGroup.sync();
|
||||
clientGroup.sync();
|
||||
}
|
||||
clientChannel = null;
|
||||
serverChannel = null;
|
||||
serverConnectedChannel = null;
|
||||
}
|
||||
|
||||
@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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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 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,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutualAuthSameCerts() throws Exception {
|
||||
mySetupMutualAuth(new File(getClass().getResource("test_unencrypted.pem").getFile()),
|
||||
new File(getClass().getResource("test.crt").getFile()),
|
||||
null);
|
||||
runTest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutualAuthDiffCerts() throws Exception {
|
||||
File serverKeyFile = new File(getClass().getResource("test_encrypted.pem").getFile());
|
||||
File serverCrtFile = new File(getClass().getResource("test.crt").getFile());
|
||||
String serverKeyPassword = "12345";
|
||||
File clientKeyFile = new File(getClass().getResource("test2_encrypted.pem").getFile());
|
||||
File clientCrtFile = new File(getClass().getResource("test2.crt").getFile());
|
||||
String clientKeyPassword = "12345";
|
||||
mySetupMutualAuth(clientCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword,
|
||||
serverCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword);
|
||||
runTest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutualAuthDiffCertsServerFailure() throws Exception {
|
||||
File serverKeyFile = new File(getClass().getResource("test_encrypted.pem").getFile());
|
||||
File serverCrtFile = new File(getClass().getResource("test.crt").getFile());
|
||||
String serverKeyPassword = "12345";
|
||||
File clientKeyFile = new File(getClass().getResource("test2_encrypted.pem").getFile());
|
||||
File clientCrtFile = new File(getClass().getResource("test2.crt").getFile());
|
||||
String clientKeyPassword = "12345";
|
||||
// Client trusts server but server only trusts itself
|
||||
mySetupMutualAuth(serverCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword,
|
||||
serverCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword);
|
||||
assertTrue(serverLatch.await(2, TimeUnit.SECONDS));
|
||||
assertTrue(serverException instanceof SSLHandshakeException);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutualAuthDiffCertsClientFailure() throws Exception {
|
||||
File serverKeyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
|
||||
File serverCrtFile = new File(getClass().getResource("test.crt").getFile());
|
||||
String serverKeyPassword = null;
|
||||
File clientKeyFile = new File(getClass().getResource("test2_unencrypted.pem").getFile());
|
||||
File clientCrtFile = new File(getClass().getResource("test2.crt").getFile());
|
||||
String clientKeyPassword = null;
|
||||
// Server trusts client but client only trusts itself
|
||||
mySetupMutualAuth(clientCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword,
|
||||
clientCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword);
|
||||
assertTrue(clientLatch.await(2, TimeUnit.SECONDS));
|
||||
assertTrue(clientException instanceof SSLHandshakeException);
|
||||
}
|
||||
|
||||
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 mySetupMutualAuth(File keyFile, File crtFile, String keyPassword)
|
||||
throws SSLException, CertificateException, InterruptedException {
|
||||
mySetupMutualAuth(crtFile, keyFile, crtFile, keyPassword, crtFile, keyFile, crtFile, keyPassword);
|
||||
}
|
||||
|
||||
private void mySetupMutualAuth(
|
||||
File servertTrustCrtFile, File serverKeyFile, File serverCrtFile, String serverKeyPassword,
|
||||
File clientTrustCrtFile, File clientKeyFile, File clientCrtFile, String clientKeyPassword)
|
||||
throws InterruptedException, SSLException, CertificateException {
|
||||
serverSslCtx = new JdkSslServerContext(servertTrustCrtFile, null,
|
||||
serverCrtFile, serverKeyFile, serverKeyPassword, null,
|
||||
null, IdentityCipherSuiteFilter.INSTANCE, (ApplicationProtocolConfig) null, 0, 0);
|
||||
clientSslCtx = new JdkSslClientContext(clientTrustCrtFile, null,
|
||||
clientCrtFile, clientKeyFile, clientKeyPassword, null,
|
||||
null, IdentityCipherSuiteFilter.INSTANCE, (ApplicationProtocolConfig) null, 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();
|
||||
SSLEngine engine = serverSslCtx.newEngine(ch.alloc());
|
||||
engine.setUseClientMode(false);
|
||||
engine.setNeedClientAuth(true);
|
||||
p.addLast(new SslHandler(engine));
|
||||
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(APPLICATION_LEVEL_PROTOCOL);
|
||||
}
|
||||
|
||||
private void runTest(String expectedApplicationProtocol) throws Exception {
|
||||
final ByteBuf clientMessage = Unpooled.copiedBuffer("I am a client".getBytes());
|
||||
final ByteBuf serverMessage = Unpooled.copiedBuffer("I am a server".getBytes());
|
||||
try {
|
||||
writeAndVerifyReceived(clientMessage.retain(), clientChannel, serverLatch, serverReceiver);
|
||||
writeAndVerifyReceived(serverMessage.retain(), serverConnectedChannel, clientLatch, clientReceiver);
|
||||
if (expectedApplicationProtocol != null) {
|
||||
verifyApplicationLevelProtocol(clientChannel, expectedApplicationProtocol);
|
||||
verifyApplicationLevelProtocol(serverConnectedChannel, expectedApplicationProtocol);
|
||||
}
|
||||
} finally {
|
||||
clientMessage.release();
|
||||
serverMessage.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyApplicationLevelProtocol(Channel channel, String expectedApplicationProtocol) {
|
||||
SslHandler handler = channel.pipeline().get(SslHandler.class);
|
||||
assertNotNull(handler);
|
||||
String[] protocol = handler.engine().getSession().getProtocol().split(":");
|
||||
assertNotNull(protocol);
|
||||
if (expectedApplicationProtocol != null && !expectedApplicationProtocol.isEmpty()) {
|
||||
assertTrue("protocol.length must be greater than 1 but is " + protocol.length, protocol.length > 1);
|
||||
assertEquals(expectedApplicationProtocol, protocol[1]);
|
||||
} else {
|
||||
assertEquals(1, protocol.length);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, CountDownLatch receiverLatch,
|
||||
MessageReciever receiver) throws Exception {
|
||||
List<ByteBuf> dataCapture = null;
|
||||
try {
|
||||
sendChannel.writeAndFlush(message);
|
||||
receiverLatch.await(5, TimeUnit.SECONDS);
|
||||
message.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(receiver).messageReceived(captor.capture());
|
||||
dataCapture = captor.getAllValues();
|
||||
assertEquals(message, dataCapture.get(0));
|
||||
} finally {
|
||||
if (dataCapture != null) {
|
||||
for (ByteBuf data : dataCapture) {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,14 @@ public class JdkSslServerContextTest {
|
||||
new JdkSslServerContext(crtFile, keyFile, "12345");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdkSslServerWithEncryptedPrivateKey2() throws SSLException {
|
||||
File keyFile = new File(getClass().getResource("test2_encrypted.pem").getFile());
|
||||
File crtFile = new File(getClass().getResource("test2.crt").getFile());
|
||||
|
||||
new JdkSslServerContext(crtFile, keyFile, "12345");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdkSslServerWithUnencryptedPrivateKey() throws SSLException {
|
||||
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
|
||||
|
18
handler/src/test/resources/io/netty/handler/ssl/test2.crt
Normal file
18
handler/src/test/resources/io/netty/handler/ssl/test2.crt
Normal file
@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC6DCCAdACCQCp0Mn/2UCl2TANBgkqhkiG9w0BAQsFADA2MTQwMgYDVQQDDCtj
|
||||
ZmYyNGEwY2I4NGFmNjExZDdhODFjMGI4MDY4OTA2OC5uZXR0eS50ZXN0MB4XDTE0
|
||||
MTAxNzE4NDczM1oXDTE0MTExNjE4NDczM1owNjE0MDIGA1UEAwwrY2ZmMjRhMGNi
|
||||
ODRhZjYxMWQ3YTgxYzBiODA2ODkwNjgubmV0dHkudGVzdDCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBALgddI5XJcUK45ONr4QTfZZxbJJeOYKPEWVIWK/P
|
||||
Wz6EJXt3hDdpmnaRUKAv4mMIFlxWVkxTqa/dB3hjcm5hPvNgPAUaEWzMtGd32p95
|
||||
sJzbxiWvxhf5rqF0n1Zk5KX+EcasiCupNg3TL7gfTSSZfaGSWf460oaCS6WCU4X9
|
||||
XTUhys7N5BFM+uQLE048CnkBCO1An980Fau/0+BLXgW+iJC6XWTJbpZ+r7rDpBKl
|
||||
+HmQQ5tgGlCZcnhmS9bzYT3hoag6JkDoIwbFsVOkwemxZGb8GsGE74/rrzUJ9MdR
|
||||
/ETCA2km1na6ESst0/wm0qD3clJahP8xEoaJ+W1TFGizRWkCAwEAATANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEAmeGPRWXzu7+f20ZJA/u6WjcmsUhSTtt0YcBNtii4Pm0snIE9
|
||||
UyRBGlvS2uFHTilD7MOYOHX6ATlHZAsfegpiPE5jCvE4CzFPpQaVAT/sKNtsWH43
|
||||
ZQHn4NK1DAFIVDysO3AGGhL0mub8iWEYHs81+6tSSFlbDFqwYtw7ueerhVLUIaIa
|
||||
S0SvtXUVitX2LzMlYCEto2s50fcqOnj8uve/dG8BmiwR1DqqVKkAWAXf8uGhwwD+
|
||||
659E3g9vNz6QUchd8K/TIv52i8EDuWu3FElohmfFUXu43A+Z+lbuDrEW3suqTC3y
|
||||
0JIa2DfHWA7WTyF4UD32aAC+U6BLIOA6WoPi1Q==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,29 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIE6jAcBgoqhkiG9w0BDAEDMA4ECCqT2dycwPtCAgIIAASCBMg/Z60Q85kVL5kv
|
||||
q8WIIY9tbXo/2Q+6rspxdit9SRd86MV9QRdfZ5Vjwt0JTa+Rd1gMaNK4PySW23bq
|
||||
F2+dD0sjVBcE24Qg0h4BcmL+YBdTftBfk7NDH/rHhsew7DZru9fdDvkO9bV3jXIz
|
||||
fARW9U7JIfgAi6CfJ8Q1PS7sg6dVtrcjMRIie32x0TSbZrn+h9AaXpLHsC8oXiyY
|
||||
BhWe4i9B7PobyJ0r/CTBFhbfUCGwRyHac0+bZXvlcwX9wy3W7jagc6RDlznOpowU
|
||||
FP35CQGeKsJ9WD+yy5MU8X8M8v+eeaJk4oX+PSWJX669CxbYocVP/+LUtOXpe+4h
|
||||
7yMmVNLUtsgBlY6tNsU0XBQkrqqb+voSxVBEVZ1WTKgLWsE/EiQ2P2GU8Gnr+J6c
|
||||
/yHxw0D4q9J3jV40SiuXQlgFwlf8u9FuVjOcGxTidfKXyvNqPKqgkf9QD+7E09q3
|
||||
JQoNbI/A8BXrpdx9h87Gt0TblPwVJP2nf5whig9W62R4y9SWybUUNr2MFNkvEfKe
|
||||
1QK8isf+HlvIO+VBYi4jof9HkWLwnAszlkpC+k1cOiSjNRn8QyLzsqX7A/VuS6W8
|
||||
6kKeND4yRNA4b7rfQqhyGg7gBwiwN+22UF6SKiikX4TB1ZyLdzlbPe0L+X/Gq0Jz
|
||||
Kf+8/slgzB5K9WpDtKsARH/lRPAx1rcascvFxMuCJL5O9MO9l4xWDJor71WgPC2N
|
||||
KwXxvEW3Kyvs3pSgWc8MC0BKcD9WIAahAlAVmSQBxDNWvJlGTgUVhzPqan7h03Fd
|
||||
nWAxSn315ObfK9rjbqUBO9x/nkSZFS9nApmeiWkOIwVzgNfAfb9md07TYyC/rpK3
|
||||
nGIsThekqqQULMQaAPmEFqUj6A/0KlpBj1gZwddYvVvEL/MuQO0QBdz4n/OncxYP
|
||||
TVoQEqXsndmNQnkuk2Kr4FACV2M9rbr84HJUIZVGGVSM5h80GrRqK03qpTzM8Nkc
|
||||
e04R4KDpLDKHm+G4xYZbbraIGXNTkhxTqdNA2FyjJWFurmpQyFay55vC6WBFBVNA
|
||||
BGVIqD1/9K3dJJGlpiHyymRCK9YGvflZlSr7dm7PW7PPEthwTijbAHkABOKsFSiu
|
||||
xaUj027WIVuDb5FFIAaF3Wmn4GFXvsSH+8L95CQuXGB8J/5Buo+/Hg6S7PeDwrf+
|
||||
qNRAfg9vxo+AZOWpWfGEYGHQeX6BxVjdffar9RwL99cele4h2FgBLtIuAXvgLPyx
|
||||
b+MIjDliCe1Nqx0PCCuaB1xRnaKiwbl7itDidzI8BUAaFcKxbBH2lpr44+vYPVHb
|
||||
70Xrw55RLvrVYKAcaZgryTNOvbRatifJIMg3kf8V++2rwUMoZ+DQfXin/C4S/2/b
|
||||
c6I1OvYaGxmI1YiI6qSpOryDSzTNlDEWcdh5feuixiP5RbyaQFswq2fH0hsWWHS4
|
||||
OsCeqT0nm5vd1CdUFQJ4Nuh/TTdgCAVKk5yJZJvH2BX77I2d4T0ZRGHLDKUm8P0E
|
||||
n6ntrMqLFR+QooONAZg0DTaxvbsCvaupRJCn9NgiwtXyYJKbvf5F8NEOe57NoGwd
|
||||
LqQ332mVTuJ1DiqnChLoe7Mz7OY21RsTa/AK5Q/onClvBATrLD0ynK4WiLn4+hGs
|
||||
HK5t3audgdnrLxs4UoA=
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4HXSOVyXFCuOT
|
||||
ja+EE32WcWySXjmCjxFlSFivz1s+hCV7d4Q3aZp2kVCgL+JjCBZcVlZMU6mv3Qd4
|
||||
Y3JuYT7zYDwFGhFszLRnd9qfebCc28Ylr8YX+a6hdJ9WZOSl/hHGrIgrqTYN0y+4
|
||||
H00kmX2hkln+OtKGgkulglOF/V01IcrOzeQRTPrkCxNOPAp5AQjtQJ/fNBWrv9Pg
|
||||
S14FvoiQul1kyW6Wfq+6w6QSpfh5kEObYBpQmXJ4ZkvW82E94aGoOiZA6CMGxbFT
|
||||
pMHpsWRm/BrBhO+P6681CfTHUfxEwgNpJtZ2uhErLdP8JtKg93JSWoT/MRKGiflt
|
||||
UxRos0VpAgMBAAECggEAYiTZd/L+oD3AuGwjrp0RKjwGKzPtJiqLlFjvZbB8LCQX
|
||||
Muyv3zX8781glDNSU4YBHXGsiP1kC+ofzE3+ttZBz0xyUinmNgAc/rbGJJKi0crZ
|
||||
okdDqo4fR9O6CDy6Ib4Azc40vEl0FgSIgHa3EZZ8gL9aF4pVpPwZxP1m9prrr6EP
|
||||
SOlJP7rJNA/sTpuy0gz+UAu2Xf53pdkREUW7E2uzIGwrHxQVserN7Xxtft/zT79/
|
||||
oIHF09pHfiqE8a2TuVvVavjwV6787PSewFs7j8iKId9bpo1O7iqvj0UKOE+/63Lf
|
||||
1pWRn7lRGS9ACw8EoyTY/M0njUbDEfaObJUzt08pjQKBgQDevZLRQjbGDtKOfQe6
|
||||
PKb/6PeFEE466NPFKH1bEz26VmC5vzF8U7lk71S11Dma51+vbOENzS5VlqOWqO+N
|
||||
CyXTzb8a0rHXXUEP4+V6CazesTOEoBKViDswt2ffJfQYoCOFfKrcKq0j1Ps8Svhq
|
||||
yzcMjAfX8eKIDWxK3qk+09SBtwKBgQDTm2Te4ENYwV5be+Z5L5See8gHNU5w3RtU
|
||||
koO54TYBeJOTsTTtGDqEg60MoWIcx69OAJlHwTp5nPV5fhrjB8I9WUmI+2sPK7sU
|
||||
OmhV/QzPjr6HW7fpbvbZ6fT+/Ay3aREa+qsJMypXsoqML1/fAeBno3hvHQt5Neog
|
||||
leu3m0/x3wKBgQCCc8b8FeqcfuvkleejtIgeU2Q8I3ud1uTIkNkyMQezDYni385s
|
||||
wWBQdDdJsvz181LAHGWGvsfHSs2OnGyIT6Ic9WBaplGQD8beNpwcqHP9jQzePR4F
|
||||
Q99evdvw/nqCva9wK76p6bizxrZJ7qKlcVVRXOXvHHSPOEVXaCb5a/kG6wKBgGN6
|
||||
2G8XC1I8hfmIRA+Q2NOw6ZbJ7riMmf6mapsGT3ddkjOKyZD1JP2LUd1wOUnCbp3D
|
||||
FkxvgOgPbC/Toxw8V4qz4Sgu2mPlcSvPUaGrN0yUlOnZqpppek9z96OwJuJK2KnQ
|
||||
Unweu7dCznOdCfszTKYsacAC7ZPsTsdG8+v7bhgNAoGBAL8wlTp3tfQ2iuGDnQaf
|
||||
268BBUtqp2qPlGPXCdkc5XXbnHXLFY/UYGw27Vh+UNW8UORTFYEb8XPvUxB4q2Mx
|
||||
8ZZdcjFB1J4dM2+KGr51CEuzzpFuhFU8Nn4D/hcfYNKg733gTeSoI0Gs2Y9R+bDo
|
||||
+cA9UxmyFSgS+Dq/7BOmPCDI
|
||||
-----END PRIVATE KEY-----
|
148
pom.xml
148
pom.xml
@ -109,7 +109,20 @@
|
||||
<!-- Our Javadoc has poor enough quality to fail the build thanks to JDK8 javadoc which got more strict. -->
|
||||
<maven.javadoc.failOnError>false</maven.javadoc.failOnError>
|
||||
<!-- npn-boot does not work with JDK 8 -->
|
||||
<argLine.bootcp>-D_</argLine.bootcp>
|
||||
<jetty.alpn.version>8.1.0.v20141016</jetty.alpn.version>
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.alpn.path}</argLine.bootcp>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>alpn-8u25</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.8.0_25</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.alpn.version>8.1.1.v20141016</jetty.alpn.version>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
@ -281,6 +294,8 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
@ -293,6 +308,8 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
@ -305,6 +322,109 @@
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u55</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_55</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u60</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_60</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u65</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_65</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u67</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_67</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.8.v20141013</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u71</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_71</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.9.v20141016</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>npn-alpn-7u72</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>java.version</name>
|
||||
<value>1.7.0_72</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<jetty.npn.version>1.1.9.v20141016</jetty.npn.version>
|
||||
<jetty.alpn.version>7.1.0.v20141016</jetty.alpn.version>
|
||||
<!-- Defer definition of Xbootclasspath to default or forcenpn profile -->
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<!--
|
||||
This profile exists because either ALPN or NPN can exits on the class path at once, but not both.
|
||||
The JDK version is typically used to distinguish which should be used but there is some overlap
|
||||
where both could be used. ALPN is the default and this profile is enabled with a -Dforcenpn=true arugument
|
||||
-->
|
||||
<id>forcenpn</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>forcenpn</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<argLine.bootcp>-Xbootclasspath/p:${jetty.npn.path}</argLine.bootcp>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
@ -313,8 +433,10 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<jboss.marshalling.version>1.3.18.GA</jboss.marshalling.version>
|
||||
<jetty.npn.version>1.1.7.v20140316</jetty.npn.version>
|
||||
<jetty.npn.version>1.1.9.v20141016</jetty.npn.version>
|
||||
<jetty.npn.path>${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${jetty.npn.version}/npn-boot-${jetty.npn.version}.jar</jetty.npn.path>
|
||||
<jetty.alpn.version>8.1.0.v20141016</jetty.alpn.version>
|
||||
<jetty.alpn.path>${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${jetty.alpn.version}/alpn-boot-${jetty.alpn.version}.jar</jetty.alpn.path>
|
||||
<argLine.common>
|
||||
-server
|
||||
-dsa -da -ea:io.netty...
|
||||
@ -380,13 +502,23 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.npn</groupId>
|
||||
<artifactId>npn-api</artifactId>
|
||||
<version>1.1.0.v20120525</version>
|
||||
<version>1.1.1.v20141010</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<version>${jetty.npn.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||
<artifactId>alpn-api</artifactId>
|
||||
<version>1.1.0.v20141014</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<version>${jetty.alpn.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Protocol Buffers - completely optional -->
|
||||
<dependency>
|
||||
@ -672,6 +804,14 @@
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.9</version>
|
||||
<dependencies>
|
||||
<!-- Upgrade ASM and support Java 8 bytecode -->
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-all</artifactId>
|
||||
<version>5.0.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>org.codehaus.mojo.signature</groupId>
|
||||
@ -798,7 +938,7 @@
|
||||
<instructions>
|
||||
<Export-Package>${project.groupId}.*</Export-Package>
|
||||
<!-- enforce JVM vendor package as optional -->
|
||||
<Import-Package>sun.misc.*;resolution:=optional,sun.nio.ch;resolution:=optional,sun.security.*;resolution:=optional,*</Import-Package>
|
||||
<Import-Package>sun.misc.*;resolution:=optional,sun.nio.ch;resolution:=optional,sun.security.*;resolution:=optional,org.eclipse.jetty.npn;version="[1,)";resolution=optional,org.eclipse.jetty.alpn;version="[1,)";resolution=optional,*</Import-Package>
|
||||
<!-- override "internal" private package convention -->
|
||||
<Private-Package>!*</Private-Package>
|
||||
</instructions>
|
||||
|
Loading…
x
Reference in New Issue
Block a user