/****************************************************************************
* Copyright (C) 2013 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.control.module.tctoken;
import generated.TCTokenType;
import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import org.openecard.bouncycastle.crypto.tls.ProtocolVersion;
import org.openecard.bouncycastle.crypto.tls.TlsClient;
import org.openecard.bouncycastle.crypto.tls.TlsClientProtocol;
import org.openecard.bouncycastle.crypto.tls.TlsPSKIdentity;
import org.openecard.common.interfaces.Dispatcher;
import org.openecard.crypto.common.sal.GenericCryptoSignerFinder;
import org.openecard.crypto.tls.ClientCertDefaultTlsClient;
import org.openecard.crypto.tls.ClientCertPSKTlsClient;
import org.openecard.crypto.tls.ClientCertTlsClient;
import org.openecard.crypto.tls.TlsPSKIdentityImpl;
import org.openecard.crypto.tls.auth.CredentialFactory;
import org.openecard.crypto.tls.auth.DynamicAuthentication;
import org.openecard.crypto.tls.auth.SmartCardCredentialFactory;
import org.openecard.crypto.tls.proxy.ProxySettings;
/**
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class TlsConnectionHandler {
private final Dispatcher dispatcher;
private final TCTokenRequest tokenRequest;
private final ConnectionHandleType handle;
private URL serverAddress;
private String hostname;
private int port;
private String resource;
private String sessionId;
private ClientCertTlsClient tlsClient;
private boolean usesTls = false;
public TlsConnectionHandler(Dispatcher dispatcher, TCTokenRequest tokenRequest, ConnectionHandleType handle)
throws ConnectionError {
this.dispatcher = dispatcher;
this.tokenRequest = tokenRequest;
this.handle = handle;
}
public void setUpClient() throws ConnectionError {
try {
TCTokenType token = tokenRequest.getTCToken();
String cardType = null;
if (handle.getRecognitionInfo() != null) {
cardType = handle.getRecognitionInfo().getCardType();
}
if (cardType == null) {
cardType = tokenRequest.getCardType();
}
// eID servers usually have problems with sni, so disable it for them
boolean noSni = "http://bsi.bund.de/cif/npa.xml".equals(cardType);
sessionId = token.getSessionIdentifier();
serverAddress = new URL(token.getServerAddress());
String serverHost = serverAddress.getHost();
String secProto = token.getPathSecurityProtocol();
serverAddress = fixServerAddress(serverAddress, sessionId);
// extract connection parameters from endpoint
hostname = serverAddress.getHost();
port = serverAddress.getPort();
if (port == -1) {
port = serverAddress.getDefaultPort();
}
resource = serverAddress.getFile();
// TODO: remove this workaround as soon as eGK server uses HTTPS
if (serverAddress.getProtocol().equals("http")) {
usesTls = false;
return;
} else {
usesTls = true;
}
// Set up TLS connection
if (secProto.equals("urn:ietf:rfc:4279") || secProto.equals("urn:ietf:rfc:5487")) {
DynamicAuthentication tlsAuth = new DynamicAuthentication();
tlsAuth.setHostname(serverHost);
// FIXME: verify certificate chain as soon as a usable solution exists fpr the trust problem
//tlsAuth.setCertificateVerifier(new JavaSecVerifier());
byte[] psk = token.getPathSecurityParameters().getPSK();
TlsPSKIdentity pskId = new TlsPSKIdentityImpl(sessionId.getBytes(), psk);
tlsClient = new ClientCertPSKTlsClient(pskId, noSni ? null : serverHost);
tlsClient.setAuthentication(tlsAuth);
tlsClient.setClientVersion(ProtocolVersion.TLSv12);
} else if (secProto.equals("urn:ietf:rfc:4346")) {
DynamicAuthentication tlsAuth = new DynamicAuthentication();
tlsAuth.setHostname(serverHost);
// use a smartcard for client authentication if needed
tlsAuth.setCredentialFactory(makeSmartCardCredential());
// FIXME: verify certificate chain as soon as a usable solution exists fpr the trust problem
//tlsAuth.setCertificateVerifier(new JavaSecVerifier());
tlsClient = new ClientCertDefaultTlsClient(noSni ? null : serverHost);
tlsClient.setAuthentication(tlsAuth);
tlsClient.setClientVersion(ProtocolVersion.TLSv11);
} else {
throw new ConnectionError("Unknow security protocol '" + secProto + "' requested.");
}
} catch (MalformedURLException ex) {
throw new ConnectionError(ex);
}
}
public boolean usesTls() {
return usesTls;
}
public URL getServerAddress() {
return serverAddress;
}
public String getHostname() {
return hostname;
}
public int getPort() {
return port;
}
public String getResource() {
return resource;
}
public String getSessionId() {
return sessionId;
}
public TlsClient getTlsClient() {
return tlsClient;
}
public TlsClientProtocol createTlsConnection() throws IOException, URISyntaxException {
return createTlsConnection(tlsClient.getClientVersion());
}
public TlsClientProtocol createTlsConnection(ProtocolVersion tlsVersion)
throws IOException, URISyntaxException {
Socket socket = ProxySettings.getDefault().getSocket(hostname, port);
tlsClient.setClientVersion(tlsVersion);
// TLS
InputStream sockIn = socket.getInputStream();
OutputStream sockOut = socket.getOutputStream();
TlsClientProtocol handler = new TlsClientProtocol(sockIn, sockOut);
handler.connect(tlsClient);
return handler;
}
private static URL fixServerAddress(URL serverAddress, String sessionIdentifier) throws MalformedURLException {
URL realServerAddress = serverAddress;
// FIXME: remove this hilariously stupid bull*#@%&/ code which satisfies a mistake introduced by the AA
String queryPart = serverAddress.getQuery();
if (queryPart == null || ! (queryPart.contains("?sessionid=") || queryPart.contains("&sessionid="))) {
String sAddr = serverAddress.toString();
// fix path of url
if (serverAddress.getPath().isEmpty()) {
sAddr += "/";
}
// add parameter
if (sAddr.endsWith("?")) {
sAddr += "sessionid=" + sessionIdentifier;
} else if (sAddr.contains("?")) {
sAddr += "&sessionid=" + sessionIdentifier;
} else {
sAddr += "?sessionid=" + sessionIdentifier;
}
realServerAddress = new URL(sAddr);
}
// END: ugly fix
return realServerAddress;
}
private CredentialFactory makeSmartCardCredential() {
GenericCryptoSignerFinder finder = new GenericCryptoSignerFinder(dispatcher, handle, false);
SmartCardCredentialFactory scFac = new SmartCardCredentialFactory(finder);
return scFac;
}
}