/*
* Copyright 2013, The Sporting Exchange Limited
*
* Licensed 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 com.betfair.cougar.netutil.nio;
import com.betfair.cougar.netutil.SslContextFactory;
import com.betfair.cougar.netutil.nio.monitoring.SessionWriteQueueMonitoring;
import com.betfair.cougar.util.KeyStoreManagement;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.ThreadModel;
import org.apache.mina.common.support.BaseIoServiceConfig;
import org.apache.mina.filter.SSLFilter;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.io.Resource;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.IOException;
@ManagedResource
public class TlsNioConfig extends NioConfig implements BeanNameAware {
private static Logger logger = LoggerFactory.getLogger(TlsNioConfig.class);
private boolean supportsTls;
private boolean requiresTls;
private boolean needClientAuth;
private boolean wantClientAuth;
private Resource keystore;
private String keystoreType;
private String keystorePassword;
private Resource truststore;
private String truststoreType;
private String truststorePassword;
private MBeanServer mbeanServer;
private KeyStoreManagement keystoreChains;
private KeyStoreManagement truststoreChains;
private String allowedCipherSuites;
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
}
private KeyStoreManagement getKeystoreCertificateChains() throws Exception {
if (keystoreChains == null) {
keystoreChains = KeyStoreManagement.getKeyStoreManagement(keystoreType, keystore, keystorePassword);
if (keystoreChains != null) {
try {
mbeanServer.registerMBean(keystoreChains, new ObjectName("CoUGAR.socket.ssl:name=keyStore,nioConfig="+beanName));
}
catch (InstanceAlreadyExistsException iaee) {
logger.warn("You appear to have loaded the same TlsNioConfig more than once in the same JVM", iaee);
}
}
}
return keystoreChains;
}
private KeyStoreManagement getTruststoreCertificateChains() throws Exception {
if (truststoreChains == null) {
truststoreChains = KeyStoreManagement.getKeyStoreManagement(truststoreType, truststore, truststorePassword);
if (truststoreChains != null) {
try {
mbeanServer.registerMBean(truststoreChains, new ObjectName("CoUGAR.socket.ssl:name=trustStore,nioConfig="+beanName));
}
catch (InstanceAlreadyExistsException iaee) {
logger.warn("You appear to have loaded the same TlsNioConfig more than once in the same JVM", iaee);
}
}
}
return truststoreChains;
}
protected void configureProtocol(BaseIoServiceConfig config, boolean isServer) throws IOException {
try {
ByteBuffer.setUseDirectBuffers(isUseDirectBuffersInMina());
SslContextFactory factory = new SslContextFactory();
if (isServer) {
if (supportsTls) {
KeyStoreManagement keystoreMgmt = getKeystoreCertificateChains();
if (keystoreMgmt == null) {
throw new IllegalStateException("This configuration ostensibly supports TLS, yet doesn't provide valid keystore configuration");
}
factory.setKeyManagerFactoryKeyStore(keystoreMgmt.getKeyStore());
factory.setKeyManagerFactoryKeyStorePassword(keystorePassword);
if (wantClientAuth) {
KeyStoreManagement truststoreMgmt = getTruststoreCertificateChains();
if (truststoreMgmt == null) {
throw new IllegalStateException("This configuration ostensibly supports client auth, yet doesn't provide valid truststore configuration");
}
factory.setTrustManagerFactoryKeyStore(truststoreMgmt.getKeyStore());
}
}
}
else {
if (supportsTls) {
KeyStoreManagement truststoreMgmt = getTruststoreCertificateChains();
if (truststoreMgmt == null) {
throw new IllegalStateException("This configuration ostensibly supports TLS, yet doesn't provide valid truststore configuration");
}
factory.setTrustManagerFactoryKeyStore(truststoreMgmt.getKeyStore());
if (wantClientAuth) {
KeyStoreManagement keystoreMgmt = getKeystoreCertificateChains();
if (keystoreMgmt == null) {
throw new IllegalStateException("This configuration ostensibly supports client auth, yet doesn't provide valid keystore configuration");
}
factory.setKeyManagerFactoryKeyStore(keystoreMgmt.getKeyStore());
factory.setKeyManagerFactoryKeyStorePassword(keystorePassword);
}
}
}
SSLFilter sslFilter = null;
if (supportsTls) {
sslFilter = new SSLFilter(factory.newInstance());
sslFilter.setWantClientAuth(wantClientAuth);
sslFilter.setNeedClientAuth(needClientAuth);
sslFilter.setUseClientMode(!isServer);
String[] cipherSuites = allowedCipherSuites == null || "".equals(allowedCipherSuites.trim()) ? null : allowedCipherSuites.split(",");
if (cipherSuites != null) {
sslFilter.setEnabledCipherSuites(cipherSuites);
}
}
CougarProtocol protocol;
if (isServer) {
protocol = CougarProtocol.getServerInstance(getNioLogger(), getKeepAliveInterval(), getKeepAliveTimeout(), sslFilter, supportsTls, requiresTls);
}
else {
protocol = CougarProtocol.getClientInstance(getNioLogger(), getKeepAliveInterval(), getKeepAliveTimeout(), sslFilter, supportsTls, requiresTls, getRpcTimeoutMillis());
}
config.getFilterChain().addLast("slowHandling", new SessionWriteQueueMonitoring(getNioLogger(), getMaxWriteQueueSize()));
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new CougarProtocolEncoder(getNioLogger()), new CougarProtocolDecoder(getNioLogger())));
config.getFilterChain().addLast("protocol", protocol);
config.setThreadModel(ThreadModel.MANUAL);
}
catch (Exception e) {
throw new IOException("Unable to initialise MINA", e);
}
}
public void setNeedClientAuth(boolean needClientAuth) {
this.needClientAuth = needClientAuth;
}
@ManagedAttribute
public boolean isNeedClientAuth() {
return needClientAuth;
}
public void setWantClientAuth(boolean wantClientAuth) {
this.wantClientAuth = wantClientAuth;
}
@ManagedAttribute
public boolean isWantClientAuth() {
return wantClientAuth;
}
public void setKeystore(Resource keystore) {
this.keystore = keystore;
}
@ManagedAttribute
public Resource getKeystore() {
return keystore;
}
public void setKeystoreType(String keystoreType) {
this.keystoreType = keystoreType;
}
@ManagedAttribute
public String getKeystoreType() {
return keystoreType;
}
public void setKeystorePassword(String keystorePassword) {
this.keystorePassword = keystorePassword;
}
@ManagedAttribute
public String getKeystorePassword() {
return keystorePassword;
}
public void setTruststore(Resource truststore) {
this.truststore = truststore;
}
@ManagedAttribute
public Resource getTruststore() {
return truststore;
}
public void setTruststoreType(String truststoreType) {
this.truststoreType = truststoreType;
}
@ManagedAttribute
public String getTruststoreType() {
return truststoreType;
}
public void setTruststorePassword(String truststorePassword) {
this.truststorePassword = truststorePassword;
}
@ManagedAttribute
public String getTruststorePassword() {
return truststorePassword;
}
@ManagedAttribute
public boolean isSupportsTls() {
return supportsTls;
}
public void setSupportsTls(boolean supportsTls) {
this.supportsTls = supportsTls;
}
@ManagedAttribute
public boolean isRequiresTls() {
return requiresTls;
}
public void setRequiresTls(boolean requiresTls) {
this.requiresTls = requiresTls;
}
public void setMbeanServer(MBeanServer mbeanServer) {
this.mbeanServer = mbeanServer;
}
@ManagedAttribute
public String getAllowedCipherSuites() {
return allowedCipherSuites;
}
public void setAllowedCipherSuites(String allowedCipherSuites) {
this.allowedCipherSuites = allowedCipherSuites;
}
}