/* 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.srm.client;
import eu.emi.security.authn.x509.X509Credential;
import eu.emi.security.authn.x509.proxy.ProxyGenerator;
import eu.emi.security.authn.x509.proxy.ProxyRequestOptions;
import org.apache.http.HttpHost;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.pkcs.CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
/**
* Wraps a TLS establishing ConnectionSocketFactory and adds optional GSI delegation on top of it.
*/
public class GsiConnectionSocketFactory implements LayeredConnectionSocketFactory
{
private final LayeredConnectionSocketFactory socketFactory;
public GsiConnectionSocketFactory(LayeredConnectionSocketFactory socketFactory)
{
this.socketFactory = socketFactory;
}
@Override
public Socket createLayeredSocket(Socket sock, String target, int port, HttpContext context) throws IOException
{
Socket socket = socketFactory.createLayeredSocket(sock, target, port, context);
delegate(socket, (HttpClientTransport.Delegation) context.getAttribute(HttpClientTransport.TRANSPORT_HTTP_DELEGATION),
(X509Credential) context.getAttribute(HttpClientTransport.TRANSPORT_HTTP_CREDENTIALS));
return socket;
}
@Override
public Socket createSocket(HttpContext context) throws IOException
{
return socketFactory.createSocket(context);
}
@Override
public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException
{
Socket socket = socketFactory.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
delegate(socket,
(HttpClientTransport.Delegation) context.getAttribute(HttpClientTransport.TRANSPORT_HTTP_DELEGATION),
(X509Credential) context.getAttribute(HttpClientTransport.TRANSPORT_HTTP_CREDENTIALS));
return socket;
}
private void delegate(Socket socket, HttpClientTransport.Delegation delegation, X509Credential credential) throws IOException
{
if (delegation != null) {
switch (delegation) {
case SKIP:
break;
case NONE:
socket.getOutputStream().write('0');
socket.getOutputStream().flush();
break;
case LIMITED:
case FULL:
socket.getOutputStream().write('D');
socket.getOutputStream().flush();
try {
// read csr
ASN1InputStream dIn = new ASN1InputStream(socket.getInputStream());
PKCS10CertificationRequest csr =
new PKCS10CertificationRequest(CertificationRequest.getInstance(dIn.readObject()));
// generate proxy
ProxyRequestOptions options = new ProxyRequestOptions(credential.getCertificateChain(), csr);
options.setLimited(delegation == HttpClientTransport.Delegation.LIMITED);
X509Certificate[] chain = ProxyGenerator.generate(options, credential.getKey());
// send to server
socket.getOutputStream().write(chain[0].getEncoded());
socket.getOutputStream().flush();
} catch (SignatureException | NoSuchProviderException | CertificateEncodingException |
InvalidKeyException | NoSuchAlgorithmException | CertificateParsingException e) {
throw new IOException("Failed to signed CSR during delegation: " + e.getMessage(), e);
}
break;
}
}
}
}