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 -f8af84d599
-e74c8edba3
Result: Support for ALPN and mutual authentication.
This commit is contained in:
parent
e73f32b52d
commit
56b8bb30b2
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;
|
||||||
|
}
|
||||||
|
}
|
@ -27,11 +27,14 @@ import io.netty.handler.codec.http.HttpMethod;
|
|||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import io.netty.handler.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
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.SslContext;
|
||||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
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.
|
* An SPDY client that allows you to send HTTP GET to a SPDY server.
|
||||||
* <p>
|
* <p>
|
||||||
@ -54,8 +57,13 @@ public final class SpdyClient {
|
|||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
// Configure SSL.
|
// Configure SSL.
|
||||||
final SslContext sslCtx = SslContext.newClientContext(
|
final SslContext sslCtx = SslContext.newClientContext(
|
||||||
null, InsecureTrustManagerFactory.INSTANCE, null,
|
null, InsecureTrustManagerFactory.INSTANCE, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
new ApplicationProtocolConfig(
|
||||||
|
Protocol.NPN,
|
||||||
|
SelectorFailureBehavior.FATAL_ALERT,
|
||||||
|
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||||
|
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||||
|
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||||
0, 0);
|
0, 0);
|
||||||
|
|
||||||
HttpResponseClientHandler httpResponseHandler = new HttpResponseClientHandler();
|
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.codec.spdy.SpdyOrHttpChooser.SelectedProtocol;
|
||||||
import io.netty.handler.logging.LogLevel;
|
import io.netty.handler.logging.LogLevel;
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
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.SslContext;
|
||||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SPDY Server that responds to a GET request with a Hello World.
|
* A SPDY Server that responds to a GET request with a Hello World.
|
||||||
* <p>
|
* <p>
|
||||||
@ -55,8 +58,13 @@ public final class SpdyServer {
|
|||||||
// Configure SSL.
|
// Configure SSL.
|
||||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||||
SslContext sslCtx = SslContext.newServerContext(
|
SslContext sslCtx = SslContext.newServerContext(
|
||||||
ssc.certificate(), ssc.privateKey(), null, null,
|
ssc.certificate(), ssc.privateKey(), null, null, IdentityCipherSuiteFilter.INSTANCE,
|
||||||
Arrays.asList(SelectedProtocol.SPDY_3_1.protocolName(), SelectedProtocol.HTTP_1_1.protocolName()),
|
new ApplicationProtocolConfig(
|
||||||
|
Protocol.NPN,
|
||||||
|
SelectorFailureBehavior.FATAL_ALERT,
|
||||||
|
SelectedListenerFailureBehavior.FATAL_ALERT,
|
||||||
|
SelectedProtocol.SPDY_3_1.protocolName(),
|
||||||
|
SelectedProtocol.HTTP_1_1.protocolName()),
|
||||||
0, 0);
|
0, 0);
|
||||||
|
|
||||||
// Configure the server.
|
// Configure the server.
|
||||||
|
@ -66,6 +66,22 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
</project>
|
</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;
|
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.SSLContext;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
import javax.net.ssl.SSLSessionContext;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
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.
|
* 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 {
|
public final class JdkSslClientContext extends JdkSslContext {
|
||||||
|
|
||||||
private final SSLContext ctx;
|
private final SSLContext ctx;
|
||||||
private final List<String> nextProtocols;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*/
|
*/
|
||||||
public JdkSslClientContext() throws SSLException {
|
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.
|
* {@code null} to use the default.
|
||||||
*/
|
*/
|
||||||
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
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.
|
* {@code null} to use the default.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||||
* {@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.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||||
@ -101,65 +95,120 @@ public final class JdkSslClientContext extends JdkSslContext {
|
|||||||
*/
|
*/
|
||||||
public JdkSslClientContext(
|
public JdkSslClientContext(
|
||||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
|
this(certChainFile, trustManagerFactory, ciphers, cipherFilter,
|
||||||
super(ciphers);
|
toNegotiator(apn, false), sessionCacheSize, sessionTimeout);
|
||||||
|
|
||||||
if (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
|
||||||
if (!JettyNpnSslEngine.isAvailable()) {
|
|
||||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> nextProtoList = new ArrayList<String>();
|
/**
|
||||||
for (String p: nextProtocols) {
|
* Creates a new instance.
|
||||||
if (p == null) {
|
*
|
||||||
break;
|
* @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);
|
||||||
}
|
}
|
||||||
nextProtoList.add(p);
|
|
||||||
}
|
/**
|
||||||
this.nextProtocols = Collections.unmodifiableList(nextProtoList);
|
* Creates a new instance.
|
||||||
} else {
|
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
|
||||||
this.nextProtocols = Collections.emptyList();
|
* {@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
try {
|
||||||
if (certChainFile == null) {
|
if (trustCertChainFile != null) {
|
||||||
|
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
|
||||||
|
}
|
||||||
|
if (keyFile != null) {
|
||||||
|
keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory);
|
||||||
|
}
|
||||||
ctx = SSLContext.getInstance(PROTOCOL);
|
ctx = SSLContext.getInstance(PROTOCOL);
|
||||||
if (trustManagerFactory == null) {
|
ctx.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(),
|
||||||
ctx.init(null, null, null);
|
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
|
||||||
} else {
|
null);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
SSLSessionContext sessCtx = ctx.getClientSessionContext();
|
SSLSessionContext sessCtx = ctx.getClientSessionContext();
|
||||||
if (sessionCacheSize > 0) {
|
if (sessionCacheSize > 0) {
|
||||||
@ -169,7 +218,7 @@ public final class JdkSslClientContext extends JdkSslContext {
|
|||||||
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
|
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> nextProtocols() {
|
|
||||||
return nextProtocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLContext context() {
|
public SSLContext context() {
|
||||||
return ctx;
|
return ctx;
|
||||||
|
@ -16,17 +16,50 @@
|
|||||||
|
|
||||||
package io.netty.handler.ssl;
|
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.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import java.io.File;
|
||||||
import javax.net.ssl.SSLEngine;
|
import java.io.IOException;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
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.
|
* 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 PROTOCOL = "TLS";
|
||||||
static final String[] PROTOCOLS;
|
static final String[] PROTOCOLS;
|
||||||
static final List<String> DEFAULT_CIPHERS;
|
static final List<String> DEFAULT_CIPHERS;
|
||||||
|
static final Set<String> SUPPORTED_CIPHERS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SSLContext context;
|
SSLContext context;
|
||||||
|
int i;
|
||||||
try {
|
try {
|
||||||
context = SSLContext.getInstance(PROTOCOL);
|
context = SSLContext.getInstance(PROTOCOL);
|
||||||
context.init(null, null, null);
|
context.init(null, null, null);
|
||||||
@ -51,10 +86,14 @@ public abstract class JdkSslContext extends SslContext {
|
|||||||
SSLEngine engine = context.createSSLEngine();
|
SSLEngine engine = context.createSSLEngine();
|
||||||
|
|
||||||
// Choose the sensible default list of protocols.
|
// 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>();
|
List<String> protocols = new ArrayList<String>();
|
||||||
addIfSupported(
|
addIfSupported(
|
||||||
supportedProtocols, protocols,
|
supportedProtocolsSet, protocols,
|
||||||
"TLSv1.2", "TLSv1.1", "TLSv1");
|
"TLSv1.2", "TLSv1.1", "TLSv1");
|
||||||
|
|
||||||
if (!protocols.isEmpty()) {
|
if (!protocols.isEmpty()) {
|
||||||
@ -64,10 +103,14 @@ public abstract class JdkSslContext extends SslContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Choose the sensible default list of cipher suites.
|
// 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>();
|
List<String> ciphers = new ArrayList<String>();
|
||||||
addIfSupported(
|
addIfSupported(
|
||||||
supportedCiphers, ciphers,
|
SUPPORTED_CIPHERS, ciphers,
|
||||||
// XXX: Make sure to sync this list with OpenSslEngineFactory.
|
// XXX: Make sure to sync this list with OpenSslEngineFactory.
|
||||||
// GCM (Galois/Counter Mode) requires JDK 8.
|
// GCM (Galois/Counter Mode) requires JDK 8.
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
"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) {
|
private static void addIfSupported(Set<String> supported, List<String> enabled, String... names) {
|
||||||
for (String n: names) {
|
for (int i = 0; i < names.length; ++i) {
|
||||||
for (String s: supported) {
|
String n = names[i];
|
||||||
if (n.equals(s)) {
|
if (supported.contains(n)) {
|
||||||
enabled.add(s);
|
enabled.add(n);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String[] cipherSuites;
|
private final String[] cipherSuites;
|
||||||
private final List<String> unmodifiableCipherSuites;
|
private final List<String> unmodifiableCipherSuites;
|
||||||
|
private final JdkApplicationProtocolNegotiator apn;
|
||||||
|
|
||||||
JdkSslContext(Iterable<String> ciphers) {
|
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config,
|
||||||
cipherSuites = toCipherSuiteArray(ciphers);
|
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));
|
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,25 +215,227 @@ public abstract class JdkSslContext extends SslContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SSLEngine wrapEngine(SSLEngine engine) {
|
private SSLEngine wrapEngine(SSLEngine engine) {
|
||||||
if (nextProtocols().isEmpty()) {
|
return apn.wrapperFactory().wrapSslEngine(engine, apn, isServer());
|
||||||
return engine;
|
}
|
||||||
|
|
||||||
|
@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 {
|
} else {
|
||||||
return new JettyNpnSslEngine(engine, nextProtocols(), isServer());
|
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) {
|
* Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
|
||||||
return DEFAULT_CIPHERS.toArray(new String[DEFAULT_CIPHERS.size()]);
|
* @param certChainFile a X.509 certificate chain file in PEM format
|
||||||
} else {
|
* @param keyFile a PKCS#8 private key file in PEM format
|
||||||
List<String> newCiphers = new ArrayList<String>();
|
* @param keyPassword the password of the {@code keyFile}.
|
||||||
for (String c: ciphers) {
|
* {@code null} if it's not password-protected.
|
||||||
if (c == null) {
|
* @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
|
||||||
break;
|
* @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";
|
||||||
}
|
}
|
||||||
newCiphers.add(c);
|
return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf);
|
||||||
}
|
}
|
||||||
return newCiphers.toArray(new String[newCiphers.size()]);
|
|
||||||
|
/**
|
||||||
|
* 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
|
* License for the specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
import java.nio.ByteBuffer;
|
||||||
import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLEngineResult;
|
import javax.net.ssl.SSLEngineResult;
|
||||||
@ -26,105 +23,32 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
|||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLParameters;
|
import javax.net.ssl.SSLParameters;
|
||||||
import javax.net.ssl.SSLSession;
|
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 SSLEngine engine;
|
||||||
private final JettyNpnSslSession session;
|
private final JdkSslSession session;
|
||||||
|
|
||||||
JettyNpnSslEngine(SSLEngine engine, final List<String> nextProtocols, boolean server) {
|
|
||||||
assert !nextProtocols.isEmpty();
|
|
||||||
|
|
||||||
|
JdkSslEngine(SSLEngine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
session = new JettyNpnSslSession(engine);
|
session = new JdkSslSession(engine);
|
||||||
|
|
||||||
if (server) {
|
|
||||||
NextProtoNego.put(engine, new ServerProvider() {
|
|
||||||
@Override
|
|
||||||
public void unsupported() {
|
|
||||||
getSession().setApplicationProtocol(nextProtocols.get(nextProtocols.size() - 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> protocols() {
|
public JdkSslSession getSession() {
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JettyNpnSslSession getSession() {
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SSLEngine getWrappedEngine() {
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeInbound() throws SSLException {
|
public void closeInbound() throws SSLException {
|
||||||
NextProtoNego.remove(engine);
|
|
||||||
engine.closeInbound();
|
engine.closeInbound();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeOutbound() {
|
public void closeOutbound() {
|
||||||
NextProtoNego.remove(engine);
|
|
||||||
engine.closeOutbound();
|
engine.closeOutbound();
|
||||||
}
|
}
|
||||||
|
|
@ -16,35 +16,15 @@
|
|||||||
|
|
||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import java.io.File;
|
||||||
import io.netty.buffer.ByteBufInputStream;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.net.ssl.KeyManager;
|
||||||
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.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
import javax.net.ssl.SSLSessionContext;
|
||||||
import java.io.File;
|
import javax.net.ssl.TrustManager;
|
||||||
import java.io.IOException;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A server-side {@link SslContext} which uses JDK's SSL/TLS implementation.
|
* 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 {
|
public final class JdkSslServerContext extends JdkSslContext {
|
||||||
|
|
||||||
private final SSLContext ctx;
|
private final SSLContext ctx;
|
||||||
private final List<String> nextProtocols;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -73,7 +52,8 @@ public final class JdkSslServerContext extends JdkSslContext {
|
|||||||
* {@code null} if it's not password-protected.
|
* {@code null} if it's not password-protected.
|
||||||
*/
|
*/
|
||||||
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
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.
|
* {@code null} if it's not password-protected.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||||
* {@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.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||||
@ -94,87 +74,118 @@ public final class JdkSslServerContext extends JdkSslContext {
|
|||||||
*/
|
*/
|
||||||
public JdkSslServerContext(
|
public JdkSslServerContext(
|
||||||
File certChainFile, File keyFile, String keyPassword,
|
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 {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
|
this(certChainFile, keyFile, keyPassword, ciphers, cipherFilter,
|
||||||
super(ciphers);
|
toNegotiator(apn, true), sessionCacheSize, sessionTimeout);
|
||||||
|
|
||||||
if (certChainFile == null) {
|
|
||||||
throw new NullPointerException("certChainFile");
|
|
||||||
}
|
|
||||||
if (keyFile == null) {
|
|
||||||
throw new NullPointerException("keyFile");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyPassword == null) {
|
/**
|
||||||
keyPassword = "";
|
* 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 (nextProtocols != null && nextProtocols.iterator().hasNext()) {
|
/**
|
||||||
if (!JettyNpnSslEngine.isAvailable()) {
|
* Creates a new instance.
|
||||||
throw new SSLException("NPN/ALPN unsupported: " + nextProtocols);
|
* @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);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> list = new ArrayList<String>();
|
/**
|
||||||
for (String p: nextProtocols) {
|
* Creates a new instance.
|
||||||
if (p == null) {
|
* @param trustCertChainFile an X.509 certificate chain file in PEM format.
|
||||||
break;
|
* This provides the certificate chains used for mutual authentication.
|
||||||
}
|
* {@code null} to use the system default
|
||||||
list.add(p);
|
* @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.nextProtocols = Collections.unmodifiableList(list);
|
* @param keyCertChainFile an X.509 certificate chain file in PEM format
|
||||||
} else {
|
* @param keyFile a PKCS#8 private key file in PEM format
|
||||||
this.nextProtocols = Collections.emptyList();
|
* @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
|
||||||
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
|
* that is used to encrypt data being sent to clients.
|
||||||
if (algorithm == null) {
|
* {@code null} to use the default or the results of parsing
|
||||||
algorithm = "SunX509";
|
* {@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 {
|
try {
|
||||||
KeyStore ks = KeyStore.getInstance("JKS");
|
if (trustCertChainFile != null) {
|
||||||
ks.load(null, null);
|
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
|
||||||
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 (keyFile != null) {
|
||||||
List<Certificate> certChain = new ArrayList<Certificate>();
|
keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory);
|
||||||
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
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
|
|
||||||
kmf.init(ks, keyPasswordChars);
|
|
||||||
|
|
||||||
// Initialize the SSLContext to work with our key managers.
|
// Initialize the SSLContext to work with our key managers.
|
||||||
ctx = SSLContext.getInstance(PROTOCOL);
|
ctx = SSLContext.getInstance(PROTOCOL);
|
||||||
ctx.init(kmf.getKeyManagers(), null, null);
|
ctx.init(keyManagerFactory.getKeyManagers(),
|
||||||
|
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
|
||||||
|
null);
|
||||||
|
|
||||||
SSLSessionContext sessCtx = ctx.getServerSessionContext();
|
SSLSessionContext sessCtx = ctx.getServerSessionContext();
|
||||||
if (sessionCacheSize > 0) {
|
if (sessionCacheSize > 0) {
|
||||||
@ -193,48 +204,8 @@ public final class JdkSslServerContext extends JdkSslContext {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> nextProtocols() {
|
|
||||||
return nextProtocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLContext context() {
|
public SSLContext context() {
|
||||||
return ctx;
|
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;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
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.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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.
|
* 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 List<String> unmodifiableCiphers = Collections.unmodifiableList(ciphers);
|
||||||
private final long sessionCacheSize;
|
private final long sessionCacheSize;
|
||||||
private final long sessionTimeout;
|
private final long sessionTimeout;
|
||||||
private final List<String> nextProtocols;
|
private final OpenSslApplicationProtocolNegotiator apn;
|
||||||
|
|
||||||
/** The OpenSSL SSL_CTX object */
|
/** The OpenSSL SSL_CTX object */
|
||||||
private final long ctx;
|
private final long ctx;
|
||||||
@ -90,7 +93,7 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
* {@code null} if it's not password-protected.
|
* {@code null} if it's not password-protected.
|
||||||
*/
|
*/
|
||||||
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
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.
|
* {@code null} if it's not password-protected.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param apn Provides a means to configure parameters related to application protocol negotiation.
|
||||||
* {@code null} to disable TLS NPN/ALPN extension.
|
|
||||||
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||||
@ -111,20 +113,39 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
*/
|
*/
|
||||||
public OpenSslServerContext(
|
public OpenSslServerContext(
|
||||||
File certChainFile, File keyFile, String keyPassword,
|
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 {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
|
|
||||||
OpenSsl.ensureAvailability();
|
OpenSsl.ensureAvailability();
|
||||||
|
|
||||||
if (certChainFile == null) {
|
checkNotNull(certChainFile, "certChainFile");
|
||||||
throw new NullPointerException("certChainFile");
|
|
||||||
}
|
|
||||||
if (!certChainFile.isFile()) {
|
if (!certChainFile.isFile()) {
|
||||||
throw new IllegalArgumentException("certChainFile is not a file: " + certChainFile);
|
throw new IllegalArgumentException("certChainFile is not a file: " + certChainFile);
|
||||||
}
|
}
|
||||||
if (keyFile == null) {
|
checkNotNull(keyFile, "keyFile");
|
||||||
throw new NullPointerException("keyPath");
|
this.apn = checkNotNull(apn, "apn");
|
||||||
}
|
|
||||||
if (!keyFile.isFile()) {
|
if (!keyFile.isFile()) {
|
||||||
throw new IllegalArgumentException("keyPath is not a file: " + keyFile);
|
throw new IllegalArgumentException("keyPath is not a file: " + keyFile);
|
||||||
}
|
}
|
||||||
@ -135,9 +156,6 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
if (keyPassword == null) {
|
if (keyPassword == null) {
|
||||||
keyPassword = "";
|
keyPassword = "";
|
||||||
}
|
}
|
||||||
if (nextProtocols == null) {
|
|
||||||
nextProtocols = Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String c: ciphers) {
|
for (String c: ciphers) {
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
@ -146,15 +164,6 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
this.ciphers.add(c);
|
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.
|
// Allocate a new APR pool.
|
||||||
aprPool = Pool.create(0);
|
aprPool = Pool.create(0);
|
||||||
|
|
||||||
@ -219,11 +228,12 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Set next protocols for next protocol negotiation extension, if specified */
|
/* 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.
|
// Convert the protocol list into a comma-separated string.
|
||||||
StringBuilder nextProtocolBuf = new StringBuilder();
|
StringBuilder nextProtocolBuf = new StringBuilder();
|
||||||
for (String p: nextProtoList) {
|
for (int i = 0; i < protocols.size(); ++i) {
|
||||||
nextProtocolBuf.append(p);
|
nextProtocolBuf.append(protocols.get(i));
|
||||||
nextProtocolBuf.append(',');
|
nextProtocolBuf.append(',');
|
||||||
}
|
}
|
||||||
nextProtocolBuf.setLength(nextProtocolBuf.length() - 1);
|
nextProtocolBuf.setLength(nextProtocolBuf.length() - 1);
|
||||||
@ -283,9 +293,8 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
return sessionTimeout;
|
return sessionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
|
||||||
public List<String> nextProtocols() {
|
return apn;
|
||||||
return nextProtocols;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -307,10 +316,11 @@ public final class OpenSslServerContext extends SslContext {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||||
if (nextProtocols.isEmpty()) {
|
List<String> protocols = apn.protocols();
|
||||||
|
if (protocols.isEmpty()) {
|
||||||
return new OpenSslEngine(ctx, alloc, null);
|
return new OpenSslEngine(ctx, alloc, null);
|
||||||
} else {
|
} 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);
|
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.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
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.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
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}.
|
* 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>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public abstract class SslContext {
|
public abstract class SslContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default server-side implementation provider currently in use.
|
* 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}
|
* @return a new server-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
|
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(
|
public static SslContext newServerContext(
|
||||||
File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
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.
|
* {@code null} if it's not password-protected.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||||
* {@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.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||||
@ -118,11 +120,11 @@ public abstract class SslContext {
|
|||||||
*/
|
*/
|
||||||
public static SslContext newServerContext(
|
public static SslContext newServerContext(
|
||||||
File certChainFile, File keyFile, String keyPassword,
|
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 {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
return newServerContext(
|
return newServerContext(
|
||||||
null, certChainFile, keyFile, keyPassword,
|
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(
|
public static SslContext newServerContext(
|
||||||
SslProvider provider, File certChainFile, File keyFile) throws SSLException {
|
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(
|
public static SslContext newServerContext(
|
||||||
SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
|
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.
|
* {@code null} if it's not password-protected.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||||
* {@code null} to disable TLS NPN/ALPN extension.
|
* 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.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @return a new server-side {@link SslContext}
|
* @return a new server-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newServerContext(
|
public static SslContext newServerContext(SslProvider provider,
|
||||||
SslProvider provider,
|
|
||||||
File certChainFile, File keyFile, String keyPassword,
|
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 {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
|
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
@ -187,12 +230,15 @@ public abstract class SslContext {
|
|||||||
switch (provider) {
|
switch (provider) {
|
||||||
case JDK:
|
case JDK:
|
||||||
return new JdkSslServerContext(
|
return new JdkSslServerContext(
|
||||||
certChainFile, keyFile, keyPassword,
|
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
||||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||||
case OPENSSL:
|
case OPENSSL:
|
||||||
|
if (trustCertChainFile != null) {
|
||||||
|
throw new UnsupportedOperationException("OpenSSL provider does not support mutual authentication");
|
||||||
|
}
|
||||||
return new OpenSslServerContext(
|
return new OpenSslServerContext(
|
||||||
certChainFile, keyFile, keyPassword,
|
keyCertChainFile, keyFile, keyPassword,
|
||||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
ciphers, apn, sessionCacheSize, sessionTimeout);
|
||||||
default:
|
default:
|
||||||
throw new Error(provider.toString());
|
throw new Error(provider.toString());
|
||||||
}
|
}
|
||||||
@ -204,7 +250,7 @@ public abstract class SslContext {
|
|||||||
* @return a new client-side {@link SslContext}
|
* @return a new client-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newClientContext() throws SSLException {
|
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}
|
* @return a new client-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newClientContext(File certChainFile) throws SSLException {
|
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}
|
* @return a new client-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
|
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(
|
public static SslContext newClientContext(
|
||||||
File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
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.
|
* {@code null} to use the default.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||||
* {@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.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
||||||
@ -268,11 +314,11 @@ public abstract class SslContext {
|
|||||||
*/
|
*/
|
||||||
public static SslContext newClientContext(
|
public static SslContext newClientContext(
|
||||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
File certChainFile, TrustManagerFactory trustManagerFactory,
|
||||||
Iterable<String> ciphers, Iterable<String> nextProtocols,
|
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
|
||||||
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
return newClientContext(
|
return newClientContext(
|
||||||
null, certChainFile, trustManagerFactory,
|
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}
|
* @return a new client-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newClientContext(SslProvider provider) throws SSLException {
|
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}
|
* @return a new client-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
|
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(
|
public static SslContext newClientContext(
|
||||||
SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
|
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(
|
public static SslContext newClientContext(
|
||||||
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
|
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.
|
* {@code null} to use the default.
|
||||||
* @param ciphers the cipher suites to enable, in the order of preference.
|
* @param ciphers the cipher suites to enable, in the order of preference.
|
||||||
* {@code null} to use the default cipher suites.
|
* {@code null} to use the default cipher suites.
|
||||||
* @param nextProtocols the application layer protocols to accept, in the order of preference.
|
* @param cipherFilter a filter to apply over the supplied list of ciphers
|
||||||
* {@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.
|
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
|
||||||
* {@code 0} to use the default value.
|
* {@code 0} to use the default value.
|
||||||
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
|
* @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}
|
* @return a new client-side {@link SslContext}
|
||||||
*/
|
*/
|
||||||
public static SslContext newClientContext(
|
public static SslContext newClientContext(SslProvider provider,
|
||||||
SslProvider provider,
|
|
||||||
File certChainFile, TrustManagerFactory trustManagerFactory,
|
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 {
|
long sessionCacheSize, long sessionTimeout) throws SSLException {
|
||||||
|
|
||||||
if (provider != null && provider != SslProvider.JDK) {
|
if (provider != null && provider != SslProvider.JDK) {
|
||||||
throw new SSLException("client context unsupported for: " + provider);
|
throw new SSLException("client context unsupported for: " + provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JdkSslClientContext(
|
return new JdkSslClientContext(trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword,
|
||||||
certChainFile, trustManagerFactory,
|
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
|
||||||
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SslContext() { }
|
SslContext() { }
|
||||||
@ -401,12 +490,9 @@ public abstract class SslContext {
|
|||||||
public abstract long sessionTimeout();
|
public abstract long sessionTimeout();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of application layer protocols for the TLS NPN/ALPN extension, in the order of preference.
|
* Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
|
||||||
*
|
|
||||||
* @return the list of application layer protocols.
|
|
||||||
* {@code null} if NPN/ALPN extension has been disabled.
|
|
||||||
*/
|
*/
|
||||||
public abstract List<String> nextProtocols();
|
public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link SSLEngine}.
|
* 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");
|
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
|
@Test
|
||||||
public void testJdkSslServerWithUnencryptedPrivateKey() throws SSLException {
|
public void testJdkSslServerWithUnencryptedPrivateKey() throws SSLException {
|
||||||
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
|
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. -->
|
<!-- 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>
|
<maven.javadoc.failOnError>false</maven.javadoc.failOnError>
|
||||||
<!-- npn-boot does not work with JDK 8 -->
|
<!-- 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>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
@ -281,6 +294,8 @@
|
|||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
<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>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
@ -293,6 +308,8 @@
|
|||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
<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>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
@ -305,6 +322,109 @@
|
|||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<jetty.npn.version>1.1.6.v20130911</jetty.npn.version>
|
<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>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
@ -313,8 +433,10 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<jboss.marshalling.version>1.3.18.GA</jboss.marshalling.version>
|
<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.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>
|
<argLine.common>
|
||||||
-server
|
-server
|
||||||
-dsa -da -ea:io.netty...
|
-dsa -da -ea:io.netty...
|
||||||
@ -372,13 +494,23 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.npn</groupId>
|
<groupId>org.eclipse.jetty.npn</groupId>
|
||||||
<artifactId>npn-api</artifactId>
|
<artifactId>npn-api</artifactId>
|
||||||
<version>1.1.0.v20120525</version>
|
<version>1.1.1.v20141010</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mortbay.jetty.npn</groupId>
|
<groupId>org.mortbay.jetty.npn</groupId>
|
||||||
<artifactId>npn-boot</artifactId>
|
<artifactId>npn-boot</artifactId>
|
||||||
<version>${jetty.npn.version}</version>
|
<version>${jetty.npn.version}</version>
|
||||||
</dependency>
|
</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 -->
|
<!-- Google Protocol Buffers - completely optional -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -635,6 +767,14 @@
|
|||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||||
<version>1.9</version>
|
<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>
|
<configuration>
|
||||||
<signature>
|
<signature>
|
||||||
<groupId>org.codehaus.mojo.signature</groupId>
|
<groupId>org.codehaus.mojo.signature</groupId>
|
||||||
@ -761,7 +901,7 @@
|
|||||||
<instructions>
|
<instructions>
|
||||||
<Export-Package>${project.groupId}.*</Export-Package>
|
<Export-Package>${project.groupId}.*</Export-Package>
|
||||||
<!-- enforce JVM vendor package as optional -->
|
<!-- 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 -->
|
<!-- override "internal" private package convention -->
|
||||||
<Private-Package>!*</Private-Package>
|
<Private-Package>!*</Private-Package>
|
||||||
</instructions>
|
</instructions>
|
||||||
|
Loading…
Reference in New Issue
Block a user