/*
* Commons eID Project.
* Copyright (C) 2008-2013 FedICT.
* Copyright (C) 2014-2015 e-Contract.be BVBA.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/
package test.integ.be.fedict.commons.eid.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Locale;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.misc.NetscapeCertType;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import org.joda.time.DateTime;
import org.junit.Test;
import be.fedict.commons.eid.jca.BeIDManagerFactoryParameters;
import be.fedict.commons.eid.jca.BeIDProvider;
import org.bouncycastle.asn1.x509.Extension;
public class SSLTest {
private static final Log LOG = LogFactory.getLog(SSLTest.class);
@Test
public void testTestEIDBelgiumBe() throws Exception {
Security.addProvider(new BeIDProvider());
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance("BeID");
keyManagerFactory.init(null);
SecureRandom secureRandom = new SecureRandom();
sslContext.init(keyManagerFactory.getKeyManagers(),
new TrustManager[]{new ClientTestX509TrustManager()},
secureRandom);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
"test.eid.belgium.be", 443);
LOG.debug("socket created");
SSLSession sslSession = sslSocket.getSession();
Certificate[] peerCertificates = sslSession.getPeerCertificates();
for (Certificate peerCertificate : peerCertificates) {
LOG.debug("peer certificate: "
+ ((X509Certificate) peerCertificate)
.getSubjectX500Principal());
}
}
@Test
public void testMutualSSL() throws Exception {
Security.addProvider(new BeIDProvider());
final KeyPair serverKeyPair = generateKeyPair();
final PrivateKey serverPrivateKey = serverKeyPair.getPrivate();
final DateTime notBefore = new DateTime();
final DateTime notAfter = notBefore.plusDays(1);
final X509Certificate serverCertificate = generateCACertificate(
serverKeyPair, "CN=Test", notBefore, notAfter);
final KeyManager keyManager = new ServerTestX509KeyManager(
serverPrivateKey, serverCertificate);
final TrustManager trustManager = new ServerTestX509TrustManager();
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[]{keyManager},
new TrustManager[]{trustManager}, new SecureRandom());
final SSLServerSocketFactory sslServerSocketFactory = sslContext
.getServerSocketFactory();
final int serverPort = 8443;
final SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory
.createServerSocket(serverPort);
sslServerSocket.setNeedClientAuth(true);
final TestRunnable testRunnable = new TestRunnable(serverPort);
final Thread thread = new Thread(testRunnable);
thread.start();
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
LOG.debug("server accepted");
InputStream inputStream = sslSocket.getInputStream();
int result = inputStream.read();
LOG.debug("result: " + result);
assertEquals(12, result);
SSLSession sslSession = sslSocket.getSession();
sslSession.invalidate();
sslSocket = (SSLSocket) sslServerSocket.accept();
inputStream = sslSocket.getInputStream();
result = inputStream.read();
LOG.debug("result: " + result);
assertEquals(34, result);
}
private static final class TestRunnable implements Runnable {
private static final Log LOG = LogFactory.getLog(TestRunnable.class);
private final int serverPort;
public TestRunnable(final int serverPort) {
this.serverPort = serverPort;
}
@Override
public void run() {
try {
mutualSSLConnection();
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private void mutualSSLConnection() throws Exception {
Thread.sleep(1000);
final JFrame frame = new JFrame("Mutual SSL test");
frame.setSize(200, 200);
frame.setLocation(300, 300);
frame.setVisible(true);
final SSLContext sslContext = SSLContext.getInstance("TLS");
final KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance("BeID");
final BeIDManagerFactoryParameters spec = new BeIDManagerFactoryParameters();
spec.setLocale(Locale.FRENCH);
spec.setParentComponent(frame);
spec.setAutoRecovery(true);
spec.setCardReaderStickiness(true);
keyManagerFactory.init(spec);
// SecureRandom secureRandom = SecureRandom.getInstance("BeID");
SecureRandom secureRandom = new SecureRandom();
sslContext.init(keyManagerFactory.getKeyManagers(),
new TrustManager[]{new ClientTestX509TrustManager()},
secureRandom);
final SSLSocketFactory sslSocketFactory = sslContext
.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
"localhost", this.serverPort);
LOG.debug("socket created");
OutputStream outputStream = sslSocket.getOutputStream();
outputStream.write(12);
SSLSession sslSession = sslSocket.getSession();
sslSession.invalidate();
JOptionPane.showMessageDialog(null, "Please remove eID card...");
sslSocket.close();
sslSocket = (SSLSocket) sslSocketFactory.createSocket("localhost",
this.serverPort);
outputStream = sslSocket.getOutputStream();
outputStream.write(34);
}
}
private static final class ClientTestX509TrustManager
implements
X509TrustManager {
private static final Log LOG = LogFactory
.getLog(ClientTestX509TrustManager.class);
@Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
LOG.debug("checkClientTrusted");
}
@Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
LOG.debug("checkServerTrusted: " + authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
LOG.debug("getAcceptedIssuers");
return null;
}
}
private static final class ServerTestX509TrustManager
implements
X509TrustManager {
private static final Log LOG = LogFactory
.getLog(ServerTestX509TrustManager.class);
@Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
LOG.debug("checkClientTrusted");
LOG.debug("subject: " + chain[0].getSubjectX500Principal());
}
@Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
LOG.debug("checkServerTrusted");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
LOG.debug("getAcceptedIssuers");
return new X509Certificate[]{};
}
}
private static final class ServerTestX509KeyManager
implements
X509KeyManager {
private static final Log LOG = LogFactory
.getLog(ServerTestX509KeyManager.class);
private final PrivateKey serverPrivateKey;
private final X509Certificate serverCertificate;
public ServerTestX509KeyManager(final PrivateKey serverPrivateKey,
final X509Certificate serverCertificate) {
this.serverPrivateKey = serverPrivateKey;
this.serverCertificate = serverCertificate;
}
@Override
public String chooseClientAlias(final String[] keyType,
final Principal[] issuers, final Socket socket) {
LOG.debug("chooseClientAlias");
return null;
}
@Override
public String chooseServerAlias(final String keyType,
final Principal[] issuers, final Socket socket) {
LOG.debug("chooseServerAlias: " + keyType);
if (keyType.equals("RSA")) {
return "test-server";
}
return null;
}
@Override
public X509Certificate[] getCertificateChain(final String alias) {
LOG.debug("getCertificateChain: " + alias);
if (false == alias.equals("test-server")) {
return null;
}
return new X509Certificate[]{this.serverCertificate};
}
@Override
public String[] getClientAliases(final String keyType,
final Principal[] issuers) {
LOG.debug("getClientAliases");
return null;
}
@Override
public PrivateKey getPrivateKey(final String alias) {
LOG.debug("getPrivateKey: " + alias);
if (false == alias.equals("test-server")) {
return null;
}
return this.serverPrivateKey;
}
@Override
public String[] getServerAliases(final String keyType,
final Principal[] issuers) {
LOG.debug("getServerAliases");
return null;
}
}
private static KeyPair generateKeyPair() throws Exception {
final KeyPairGenerator keyPairGenerator = KeyPairGenerator
.getInstance("RSA");
final SecureRandom random = new SecureRandom();
keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024,
RSAKeyGenParameterSpec.F4), random);
final KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
}
private X509Certificate generateCACertificate(final KeyPair keyPair,
final String subject, final DateTime notBefore,
final DateTime notAfter) throws Exception {
LOG.debug("generate CA certificate: " + subject);
final X500Name issuer = new X500Name(subject);
final X500Name subjectX500Name = new X500Name(subject);
final SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo
.getInstance(keyPair.getPublic().getEncoded());
final SecureRandom secureRandom = new SecureRandom();
final byte[] serialValue = new byte[8];
secureRandom.nextBytes(serialValue);
final BigInteger serial = new BigInteger(serialValue);
final X509v3CertificateBuilder x509v3CertificateBuilder = new X509v3CertificateBuilder(
issuer, serial, notBefore.toDate(), notAfter.toDate(),
subjectX500Name, publicKeyInfo);
try {
final JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
x509v3CertificateBuilder.addExtension(
Extension.subjectKeyIdentifier, false, extensionUtils
.createSubjectKeyIdentifier(keyPair.getPublic()));
x509v3CertificateBuilder.addExtension(
Extension.authorityKeyIdentifier, false, extensionUtils
.createAuthorityKeyIdentifier(keyPair.getPublic()));
x509v3CertificateBuilder.addExtension(
MiscObjectIdentifiers.netscapeCertType, false,
new NetscapeCertType(NetscapeCertType.sslCA
| NetscapeCertType.smimeCA
| NetscapeCertType.objectSigningCA));
x509v3CertificateBuilder.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign));
x509v3CertificateBuilder.addExtension(Extension.basicConstraints,
true, new BasicConstraints(2147483647));
} catch (final Exception e) {
throw new RuntimeException(e);
}
final AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1withRSA");
final AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
.find(sigAlgId);
AsymmetricKeyParameter asymmetricKeyParameter;
try {
asymmetricKeyParameter = PrivateKeyFactory.createKey(keyPair
.getPrivate().getEncoded());
} catch (final IOException e) {
throw new RuntimeException(e);
}
ContentSigner contentSigner;
try {
contentSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(asymmetricKeyParameter);
} catch (final OperatorCreationException e) {
throw new RuntimeException(e);
}
final X509CertificateHolder x509CertificateHolder = x509v3CertificateBuilder
.build(contentSigner);
byte[] encodedCertificate;
try {
encodedCertificate = x509CertificateHolder.getEncoded();
} catch (final IOException e) {
throw new RuntimeException(e);
}
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
} catch (final CertificateException e) {
throw new RuntimeException(e);
}
X509Certificate certificate;
try {
certificate = (X509Certificate) certificateFactory
.generateCertificate(new ByteArrayInputStream(
encodedCertificate));
} catch (final CertificateException e) {
throw new RuntimeException(e);
}
return certificate;
}
@Test
public void testKeyManagerFactory() throws Exception {
Security.addProvider(new BeIDProvider());
final KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance("BeID");
assertNotNull(keyManagerFactory);
final String algo = keyManagerFactory.getAlgorithm();
LOG.debug("key manager factory algo: " + algo);
assertEquals("BeID", algo);
final KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
assertNotNull(keyManagers);
}
}