/* dCache - http://www.dcache.org/
*
* Copyright (C) 2015 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.dss;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import eu.emi.security.authn.x509.CrlCheckingMode;
import eu.emi.security.authn.x509.NamespaceCheckingMode;
import eu.emi.security.authn.x509.OCSPCheckingMode;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.cert.CertificateFactory;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.dcache.gsi.KeyPairCache;
import org.dcache.gsi.ServerGsiEngine;
import org.dcache.ssl.CanlContextFactory;
import org.dcache.util.Args;
import org.dcache.util.CertificateFactories;
import org.dcache.util.Crypto;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.toArray;
import static java.util.Arrays.asList;
public class ServerGsiEngineDssContextFactory implements DssContextFactory
{
private static final String SERVICE_KEY = "service_key";
private static final String SERVICE_CERT = "service_cert";
private static final String SERVICE_TRUSTED_CERTS = "service_trusted_certs";
private static final String CIPHER_FLAGS = "ciphers";
private static final String NAMESPACE_MODE = "namespace-mode";
private static final String CRL_MODE = "crl-mode";
private static final String OCSP_MODE = "ocsp-mode";
private static final String KEY_CACHE_LIFETIME = "key-cache-lifetime";
private static final String KEY_CACHE_LIFETIME_UNIT = "key-cache-lifetime-unit";
private final CertificateFactory cf;
private final Set<String> bannedCiphers;
private final Set<String> bannedProtocols;
private final Callable<SSLContext> factory;
private final KeyPairCache keyPairCache;
public ServerGsiEngineDssContextFactory(String args) throws Exception
{
this(new Args(args));
}
public ServerGsiEngineDssContextFactory(Args arguments) throws Exception
{
this(new File(arguments.getOption(SERVICE_KEY)),
new File(arguments.getOption(SERVICE_CERT)),
new File(arguments.getOption(SERVICE_TRUSTED_CERTS)),
Crypto.getBannedCipherSuitesFromConfigurationValue(arguments.getOption(CIPHER_FLAGS)),
NamespaceCheckingMode.valueOf(arguments.getOption(NAMESPACE_MODE)),
CrlCheckingMode.valueOf(arguments.getOption(CRL_MODE)),
OCSPCheckingMode.valueOf(arguments.getOption(OCSP_MODE)),
arguments.getLongOption(KEY_CACHE_LIFETIME),
TimeUnit.valueOf(arguments.getOption(KEY_CACHE_LIFETIME_UNIT)));
}
public ServerGsiEngineDssContextFactory(File serverKeyPath, File serverCertificatePath,
File certificateAuthorityPath, String[] bannedCiphers,
NamespaceCheckingMode namespaceMode, CrlCheckingMode crlMode,
OCSPCheckingMode ocspMode, long keyCacheLifetime,
TimeUnit keyCacheLifetimeUnit) throws Exception
{
cf = CertificateFactories.newX509CertificateFactory();
this.bannedCiphers = ImmutableSet.copyOf(bannedCiphers);
this.bannedProtocols = ImmutableSet.of("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
keyPairCache = new KeyPairCache(keyCacheLifetime, keyCacheLifetimeUnit);
factory = CanlContextFactory.custom()
.withCertificateAuthorityPath(certificateAuthorityPath.toPath())
.withCrlCheckingMode(crlMode)
.withOcspCheckingMode(ocspMode)
.withNamespaceMode(namespaceMode)
.withLazy(false)
.withKeyPath(serverKeyPath.toPath())
.withCertificatePath(serverCertificatePath.toPath())
.buildWithCaching();
factory.call(); // Fail fast in case of config errors
}
@Override
public DssContext create(InetSocketAddress remoteSocketAddress, InetSocketAddress localSocketAddress)
throws IOException
{
try {
SSLEngine delegate = factory.call().createSSLEngine(remoteSocketAddress.getHostString(),
remoteSocketAddress.getPort());
SSLParameters sslParameters = delegate.getSSLParameters();
String[] cipherSuites = toArray(filter(asList(sslParameters.getCipherSuites()), not(in(bannedCiphers))), String.class);
String[] protocols = toArray(filter(asList(sslParameters.getProtocols()), not(in(bannedProtocols))), String.class);
sslParameters.setCipherSuites(cipherSuites);
sslParameters.setProtocols(protocols);
sslParameters.setWantClientAuth(true);
sslParameters.setNeedClientAuth(true);
delegate.setSSLParameters(sslParameters);
ServerGsiEngine engine = new ServerGsiEngine(delegate, cf);
engine.setKeyPairCache(keyPairCache);
engine.setUsingLegacyClose(true);
return new SslEngineDssContext(engine, cf);
} catch (Exception e) {
Throwables.propagateIfPossible(e, IOException.class);
throw new IOException("Failed to create SSL engine: " + e.getMessage(), e);
}
}
}