Skip to content

Commit

Permalink
Add SSL support to WebSocket client
Browse files Browse the repository at this point in the history
  • Loading branch information
irunika authored and irunika committed Jun 20, 2018
1 parent d447f6c commit 17acfa8
Show file tree
Hide file tree
Showing 27 changed files with 720 additions and 404 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ public final class Constants {
public static final String LOCALHOST = "localhost";

public static final String HTTP_OBJECT_AGGREGATOR = "HTTP_OBJECT_AGGREGATOR";
public static final String WEBSOCKET_PROTOCOL = "ws";
public static final String WEBSOCKET_PROTOCOL_SECURED = "wss";
public static final String WS_SCHEME = "ws";
public static final String WSS_SCHEME = "wss";
public static final String WEBSOCKET_UPGRADE = "websocket";
public static final String WEBSOCKET_FRAME_HANDLER = "WEBSOCKET_FRAME_HANDLER";
public static final String WEBSOCKET_FRAME_BLOCKING_HANDLER = "WEBSOCKET_FRAME_BLOCKING_HANDLER";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.DefaultHttpResponse;
Expand All @@ -35,15 +36,22 @@
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.HttpConversionUtil;
import io.netty.handler.ssl.ReferenceCountedOpenSslContext;
import io.netty.handler.ssl.ReferenceCountedOpenSslEngine;
import io.netty.handler.ssl.SslHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.transport.http.netty.common.ssl.SSLConfig;
import org.wso2.transport.http.netty.common.ssl.SSLHandlerFactory;
import org.wso2.transport.http.netty.config.ChunkConfig;
import org.wso2.transport.http.netty.config.Parameter;
import org.wso2.transport.http.netty.config.SslConfiguration;
import org.wso2.transport.http.netty.contract.HttpResponseFuture;
import org.wso2.transport.http.netty.message.DefaultListener;
import org.wso2.transport.http.netty.message.HTTPCarbonMessage;
import org.wso2.transport.http.netty.message.Listener;
import org.wso2.transport.http.netty.sender.CertificateValidationHandler;
import org.wso2.transport.http.netty.sender.OCSPStaplingHandler;

import java.io.File;
import java.io.IOException;
Expand All @@ -54,6 +62,8 @@
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;

import static org.wso2.transport.http.netty.common.Constants.COLON;
import static org.wso2.transport.http.netty.common.Constants.HTTP_HOST;
Expand Down Expand Up @@ -341,7 +351,7 @@ public static SSLConfig getSSLConfigForSender(String certPass, String keyStorePa
certPass = keyStorePass;
}
if (trustStoreFilePath == null || trustStorePass == null) {
throw new IllegalArgumentException("TrusStoreFile or trustStorePassword not defined for HTTPS scheme");
throw new IllegalArgumentException("TrustStoreFile or trustStorePassword not defined for HTTPS/WSS scheme");
}
SSLConfig sslConfig = new SSLConfig(null, null).setCertPass(null);

Expand Down Expand Up @@ -374,6 +384,64 @@ public static SSLConfig getSSLConfigForSender(String certPass, String keyStorePa
return sslConfig;
}

/**
* Configure outbound HTTP pipeline for SSL configuration.
*
* @param socketChannel Socket channel of outbound connection
* @param sslConfiguration {@link SslConfiguration}
* @param host host of the connection
* @param port port of the connection
* @throws SSLException if any error occurs in the SSL connection
*/
public static void configureHttpPipelineForSSL(SocketChannel socketChannel, String host, int port,
SslConfiguration sslConfiguration) throws SSLException {
log.debug("adding ssl handler");
SSLConfig sslConfig = sslConfiguration.generateSSLConfig();
ChannelPipeline pipeline = socketChannel.pipeline();
if (sslConfiguration.isOcspStaplingEnabled()) {
SSLHandlerFactory sslHandlerFactory = new SSLHandlerFactory(sslConfig);
ReferenceCountedOpenSslContext referenceCountedOpenSslContext = sslHandlerFactory
.buildClientReferenceCountedOpenSslContext();

if (referenceCountedOpenSslContext != null) {
SslHandler sslHandler = referenceCountedOpenSslContext.newHandler(socketChannel.alloc());
ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine();
socketChannel.pipeline().addLast(sslHandler);
socketChannel.pipeline().addLast(new OCSPStaplingHandler(engine));
}
} else {
SSLEngine sslEngine = instantiateAndConfigSSL(sslConfig, host, port,
sslConfiguration.hostNameVerificationEnabled());
pipeline.addLast(Constants.SSL_HANDLER, new SslHandler(sslEngine));
if (sslConfiguration.validateCertEnabled()) {
pipeline.addLast(Constants.HTTP_CERT_VALIDATION_HANDLER, new CertificateValidationHandler(
sslEngine, sslConfiguration.getCacheValidityPeriod(), sslConfiguration.getCacheSize()));
}
}
}

/**
* Set configurations to create ssl engine.
*
* @param sslConfig ssl related configurations
* @return ssl engine
*/
public static SSLEngine instantiateAndConfigSSL(SSLConfig sslConfig, String host, int port,
boolean hostNameVerificationEnabled) {
// set the pipeline factory, which creates the pipeline for each newly created channels
SSLEngine sslEngine = null;
if (sslConfig != null) {
SSLHandlerFactory sslHandlerFactory = new SSLHandlerFactory(sslConfig);
sslEngine = sslHandlerFactory.buildClientSSLEngine(host, port);
sslEngine.setUseClientMode(true);
sslHandlerFactory.setSNIServerNames(sslEngine, host);
if (hostNameVerificationEnabled) {
sslHandlerFactory.setHostNameVerfication(sslEngine);
}
}
return sslEngine;
}

/**
* Get integer type property value from a property map.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,12 @@
package org.wso2.transport.http.netty.config;

import org.wso2.transport.http.netty.common.ProxyServerConfiguration;
import org.wso2.transport.http.netty.common.Util;
import org.wso2.transport.http.netty.common.ssl.SSLConfig;
import org.wso2.transport.http.netty.sender.channel.pool.PoolConfiguration;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;


/**
* JAXB representation of the Netty transport sender configuration.
*/
@SuppressWarnings("unused")
@XmlAccessorType(XmlAccessType.FIELD)
public class SenderConfiguration {
public class SenderConfiguration extends SslConfiguration {

private static final String DEFAULT_KEY = "netty";

Expand All @@ -48,57 +35,17 @@ public static SenderConfiguration getDefault() {
return defaultConfig;
}

@XmlAttribute(required = true)
private String id = DEFAULT_KEY;

@XmlAttribute
private String scheme = "http";

@XmlAttribute
private String keyStoreFile;

@XmlAttribute
private String keyStorePassword;

@XmlAttribute
private String trustStoreFile;

@XmlAttribute
private String trustStorePass;

@XmlAttribute
private String certPass;

@XmlAttribute
private int socketIdleTimeout = 60000;

@XmlAttribute
private boolean httpTraceLogEnabled;

private ChunkConfig chunkingConfig = ChunkConfig.AUTO;

@XmlAttribute
private String sslProtocol;

@XmlElementWrapper(name = "parameters")
@XmlElement(name = "parameter")
private List<Parameter> parameters = new ArrayList<>();

private KeepAliveConfig keepAliveConfig = KeepAliveConfig.AUTO;

@XmlAttribute
private boolean forceHttp2 = false;

private String tlsStoreType;
private String httpVersion = "1.1";
private ProxyServerConfiguration proxyServerConfiguration;
private PoolConfiguration poolConfiguration;
private boolean validateCertEnabled;
private int cacheSize = 50;
private int cacheValidityPeriod = 15;
private boolean hostNameVerificationEnabled = true;

private ForwardedExtensionConfig forwardedExtensionConfig;
private boolean ocspStaplingEnabled = false;

public SenderConfiguration() {
this.poolConfiguration = new PoolConfiguration();
Expand All @@ -109,30 +56,6 @@ public SenderConfiguration(String id) {
this.poolConfiguration = new PoolConfiguration();
}

public void setSSLProtocol(String sslProtocol) {
this.sslProtocol = sslProtocol;
}

public String getSSLProtocol() {
return sslProtocol;
}

public String getCertPass() {
return certPass;
}

public String getTLSStoreType() {
return tlsStoreType;
}

public void setTLSStoreType(String storeType) {
this.tlsStoreType = storeType;
}

public void setCertPass(String certPass) {
this.certPass = certPass;
}

public String getId() {
return id;
}
Expand All @@ -141,62 +64,6 @@ public void setId(String id) {
this.id = id;
}

public String getKeyStoreFile() {
return keyStoreFile;
}

public void setKeyStoreFile(String keyStoreFile) {
this.keyStoreFile = keyStoreFile;
}

public String getKeyStorePassword() {
return keyStorePassword;
}

public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}

public String getScheme() {
return scheme;
}

public void setScheme(String scheme) {
this.scheme = scheme;
}

public List<Parameter> getParameters() {
return parameters;
}

public void setParameters(List<Parameter> parameters) {
this.parameters = parameters;
}

public String getTrustStoreFile() {
return trustStoreFile;
}

public void setTrustStoreFile(String trustStoreFile) {
this.trustStoreFile = trustStoreFile;
}

public String getTrustStorePass() {
return trustStorePass;
}

public void setTrustStorePass(String trustStorePass) {
this.trustStorePass = trustStorePass;
}

public SSLConfig getSSLConfig() {
if (scheme == null || !scheme.equalsIgnoreCase("https")) {
return null;
}
return Util.getSSLConfigForSender(certPass, keyStorePassword, keyStoreFile, trustStoreFile, trustStorePass,
parameters, sslProtocol, tlsStoreType);
}

public int getSocketIdleTimeout(int defaultValue) {
if (socketIdleTimeout == 0) {
return defaultValue;
Expand Down Expand Up @@ -258,46 +125,6 @@ public void setForceHttp2(boolean forceHttp2) {
this.forceHttp2 = forceHttp2;
}

public void setValidateCertEnabled(boolean validateCertEnabled) {
this.validateCertEnabled = validateCertEnabled;
}

public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
}

public void setCacheValidityPeriod(int cacheValidityPeriod) {
this.cacheValidityPeriod = cacheValidityPeriod;
}

public boolean validateCertEnabled() {
return validateCertEnabled;
}

public int getCacheSize() {
return cacheSize;
}

public void setHostNameVerificationEnabled(boolean hostNameVerificationEnabled) {
this.hostNameVerificationEnabled = hostNameVerificationEnabled;
}

public boolean hostNameVerificationEnabled() {
return hostNameVerificationEnabled;
}

public int getCacheValidityPeriod() {
return cacheValidityPeriod;
}

public void setOcspStaplingEnabled(boolean ocspStaplingEnabled) {
this.ocspStaplingEnabled = ocspStaplingEnabled;
}

public boolean isOcspStaplingEnabled() {
return ocspStaplingEnabled;
}

public PoolConfiguration getPoolConfiguration() {
return poolConfiguration;
}
Expand Down
Loading

0 comments on commit 17acfa8

Please sign in to comment.