diff --git a/java/src/main/java/org/wildfly/openssl/OpenSSLContextSPI.java b/java/src/main/java/org/wildfly/openssl/OpenSSLContextSPI.java
index d956e04c..fbfdcfb1 100644
--- a/java/src/main/java/org/wildfly/openssl/OpenSSLContextSPI.java
+++ b/java/src/main/java/org/wildfly/openssl/OpenSSLContextSPI.java
@@ -58,15 +58,33 @@ public abstract class OpenSSLContextSPI extends SSLContextSpi {
public static final int DEFAULT_SESSION_CACHE_SIZE = 1000;
- private static final String BEGIN_RSA_CERT = "-----BEGIN RSA PRIVATE KEY-----\n";
-
- private static final String END_RSA_CERT = "\n-----END RSA PRIVATE KEY-----";
+ private static enum KeyAlgorithm {
+ RSA(SSL.SSL_AIDX_RSA),
+ EC(SSL.SSL_AIDX_ECC),
+ DSA(SSL.SSL_AIDX_DSA);
+
+ private final int idx;
+ private final String beginStanza;
+ private final String endStanza;
+
+ KeyAlgorithm(int idx) {
+ this.idx = idx;
+ this.beginStanza = String.format("-----BEGIN %s PRIVATE KEY-----\n", name());
+ this.endStanza = String.format("\n-----END %s PRIVATE KEY-----", name());
+ }
- private static final String BEGIN_DSA_CERT = "-----BEGIN DSA PRIVATE KEY-----\n";
+ public String getBeginStanza() {
+ return beginStanza;
+ }
- private static final String END_DSA_CERT = "\n-----END DSA PRIVATE KEY-----";
+ public String getEndStanza() {
+ return endStanza;
+ }
- private static final String[] ALGORITHMS = {"RSA", "DSA"};
+ public int getAlgorithmIndex() {
+ return idx;
+ }
+ }
private OpenSSLServerSessionContext serverSessionContext;
private OpenSSLClientSessionContext clientSessionContext;
@@ -179,10 +197,9 @@ private synchronized void init(KeyManager[] kms, TrustManager[] tms) throws KeyM
// Load Server key and certificate
X509KeyManager keyManager = chooseKeyManager(kms);
if (keyManager != null) {
- for (String algorithm : ALGORITHMS) {
+ for (KeyAlgorithm algorithm : KeyAlgorithm.values()) {
- boolean rsa = algorithm.equals("RSA");
- final String[] aliases = keyManager.getServerAliases(algorithm, null);
+ final String[] aliases = keyManager.getServerAliases(algorithm.name(), null);
if (aliases != null && aliases.length != 0) {
for(String alias: aliases) {
@@ -192,22 +209,23 @@ private synchronized void init(KeyManager[] kms, TrustManager[] tms) throws KeyM
continue;
}
if (LOG.isLoggable(Level.FINE)) {
- LOG.fine("Using alias " + alias + " for " + algorithm);
+ LOG.log(Level.FINE, "Using alias {0} for {1}", new Object[]{alias, algorithm});
}
- StringBuilder sb = new StringBuilder(rsa ? BEGIN_RSA_CERT : BEGIN_DSA_CERT);
byte[] encodedPrivateKey = key.getEncoded();
if (encodedPrivateKey == null) {
throw new KeyManagementException(Messages.MESSAGES.unableToObtainPrivateKey());
}
- sb.append(Base64.getMimeEncoder(64, new byte[]{'\n'}).encodeToString(encodedPrivateKey));
- sb.append(rsa ? END_RSA_CERT : END_DSA_CERT);
+ String keyString = algorithm.getBeginStanza()
+ + Base64.getMimeEncoder(64, new byte[]{'\n'}).encodeToString(encodedPrivateKey)
+ + algorithm.getEndStanza();
byte[][] encodedIntermediaries = new byte[certificateChain.length - 1][];
for(int i = 1; i < certificateChain.length; ++i) {
encodedIntermediaries[i - 1] = certificateChain[i].getEncoded();
}
X509Certificate certificate = certificateChain[0];
- SSL.getInstance().setCertificate(ctx, certificate.getEncoded(), encodedIntermediaries, sb.toString().getBytes(StandardCharsets.US_ASCII), rsa ? SSL.SSL_AIDX_RSA : SSL.SSL_AIDX_DSA);
+ SSL.getInstance().setCertificate(ctx, certificate.getEncoded(), encodedIntermediaries,
+ keyString.getBytes(StandardCharsets.US_ASCII), algorithm.getAlgorithmIndex());
break;
}
}
diff --git a/java/src/test/java/org/wildfly/openssl/BasicOpenSSLSocketECTest.java b/java/src/test/java/org/wildfly/openssl/BasicOpenSSLSocketECTest.java
new file mode 100644
index 00000000..9203f9aa
--- /dev/null
+++ b/java/src/test/java/org/wildfly/openssl/BasicOpenSSLSocketECTest.java
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2022 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * 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 org.wildfly.openssl;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import static org.wildfly.openssl.OpenSSLEngine.isTLS13Supported;
+import static org.wildfly.openssl.SSL.SSL_PROTO_TLSv1_2;
+import static org.wildfly.openssl.SSL.SSL_PROTO_TLSv1_3;
+
+/**
+ *
Test class that uses TLSv1.2 and TLSv1.3 to connect a client and server
+ * using openssl engine and the EC certificates.
+ *
+ * @author rmartinc
+ */
+public class BasicOpenSSLSocketECTest extends AbstractOpenSSLTest {
+
+ public void testECCertificates(String protocol, boolean opensslClient, boolean opensslServer) throws IOException, NoSuchAlgorithmException, InterruptedException {
+
+ try (ServerSocket serverSocket = SSLTestUtils.createServerSocket()) {
+ final AtomicReference sessionID = new AtomicReference<>();
+ final AtomicReference engineRef = new AtomicReference<>();
+
+ Thread acceptThread = new Thread(new EchoRunnable(serverSocket,
+ SSLTestUtils.createECSSLContext(opensslServer? "openssl." + protocol : protocol), sessionID,
+ engine -> {
+ engine.setNeedClientAuth(true);
+ engineRef.set(engine);
+ return engine;
+ }));
+ acceptThread.start();
+ final SSLContext sslContext = SSLTestUtils.createClientECSSLContext(opensslClient? "openssl." + protocol : protocol);
+ try (SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket()) {
+ socket.setReuseAddress(true);
+ socket.connect(SSLTestUtils.createSocketAddress());
+ socket.getOutputStream().write("hello world".getBytes(StandardCharsets.US_ASCII));
+ socket.getOutputStream().flush();
+ byte[] data = new byte[100];
+ int read = socket.getInputStream().read(data);
+
+ Assert.assertEquals("hello world", new String(data, 0, read));
+ if (!SSL_PROTO_TLSv1_3.equals(protocol)) {
+ Assert.assertArrayEquals(socket.getSession().getId(), sessionID.get());
+ }
+ Assert.assertEquals(protocol, socket.getSession().getProtocol());
+ Assert.assertNotNull(socket.getSession().getCipherSuite());
+
+ Assert.assertNotNull(socket.getSession().getPeerCertificates());
+ Assert.assertTrue(socket.getSession().getPeerCertificates().length > 0);
+ Assert.assertTrue(socket.getSession().getPeerCertificates()[0] instanceof X509Certificate);
+ Assert.assertEquals("CN=localhost", ((X509Certificate) socket.getSession().getPeerCertificates()[0]).getSubjectDN().getName());
+ Assert.assertEquals("EC", ((X509Certificate) socket.getSession().getPeerCertificates()[0]).getPublicKey().getAlgorithm());
+
+ Assert.assertNotNull(engineRef.get().getSession().getPeerCertificates());
+ Assert.assertTrue(engineRef.get().getSession().getPeerCertificates().length > 0);
+ Assert.assertTrue(engineRef.get().getSession().getPeerCertificates()[0] instanceof X509Certificate);
+ Assert.assertEquals("CN=Test Client", ((X509Certificate) engineRef.get().getSession().getPeerCertificates()[0]).getSubjectDN().getName());
+ Assert.assertEquals("EC", ((X509Certificate) engineRef.get().getSession().getPeerCertificates()[0]).getPublicKey().getAlgorithm());
+ socket.getSession().invalidate();
+ }
+ serverSocket.close();
+ acceptThread.join();
+ }
+ }
+
+ @Test
+ public void testTLSv12() throws IOException, NoSuchAlgorithmException, InterruptedException {
+ testECCertificates(SSL_PROTO_TLSv1_2, true, true);
+ testECCertificates(SSL_PROTO_TLSv1_2, true, false);
+ testECCertificates(SSL_PROTO_TLSv1_2, false, true);
+ }
+
+ @Test
+ public void testTLSv13() throws IOException, NoSuchAlgorithmException, InterruptedException {
+ Assume.assumeTrue(isTLS13Supported());
+ testECCertificates(SSL_PROTO_TLSv1_3, true, true);
+ testECCertificates(SSL_PROTO_TLSv1_3, true, false);
+ testECCertificates(SSL_PROTO_TLSv1_3, false, true);
+ }
+}
diff --git a/java/src/test/java/org/wildfly/openssl/SSLTestUtils.java b/java/src/test/java/org/wildfly/openssl/SSLTestUtils.java
index e1635eb4..f779dabe 100644
--- a/java/src/test/java/org/wildfly/openssl/SSLTestUtils.java
+++ b/java/src/test/java/org/wildfly/openssl/SSLTestUtils.java
@@ -58,23 +58,27 @@ private static KeyStore loadKeyStore(final String name) throws IOException {
}
}
- static SSLContext createSSLContext(String provider) throws IOException {
- KeyManager[] keyManagers;
- try {
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- keyManagerFactory.init(loadKeyStore("server.keystore"), "password".toCharArray());
- keyManagers = keyManagerFactory.getKeyManagers();
- } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
- throw new RuntimeException("Unable to initialise KeyManager[]", e);
+ private static SSLContext createSSLContext(String provider, String keystore, String truststore) throws IOException {
+ KeyManager[] keyManagers = null;
+ if (keystore != null) {
+ try {
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ keyManagerFactory.init(loadKeyStore(keystore), "password".toCharArray());
+ keyManagers = keyManagerFactory.getKeyManagers();
+ } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
+ throw new RuntimeException("Unable to initialise KeyManager[]", e);
+ }
}
TrustManager[] trustManagers = null;
- try {
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init(loadKeyStore("server.truststore"));
- trustManagers = trustManagerFactory.getTrustManagers();
- } catch (NoSuchAlgorithmException | KeyStoreException e) {
- throw new RuntimeException("Unable to initialise TrustManager[]", e);
+ if (truststore != null) {
+ try {
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(loadKeyStore(truststore));
+ trustManagers = trustManagerFactory.getTrustManagers();
+ } catch (NoSuchAlgorithmException | KeyStoreException e) {
+ throw new RuntimeException("Unable to initialise TrustManager[]", e);
+ }
}
try {
@@ -87,72 +91,28 @@ static SSLContext createSSLContext(String provider) throws IOException {
}
}
- static SSLContext createClientSSLContext(String provider) throws IOException {
- KeyManager[] keyManagers;
- try {
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- keyManagerFactory.init(loadKeyStore("client.keystore"), "password".toCharArray());
- keyManagers = keyManagerFactory.getKeyManagers();
- } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
- throw new RuntimeException("Unable to initialise KeyManager[]", e);
- }
-
- TrustManager[] trustManagers = null;
- try {
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init(loadKeyStore("client.truststore"));
- trustManagers = trustManagerFactory.getTrustManagers();
- } catch (NoSuchAlgorithmException | KeyStoreException e) {
- throw new RuntimeException("Unable to initialise TrustManager[]", e);
- }
+ static SSLContext createSSLContext(String provider) throws IOException {
+ return createSSLContext(provider, "server.keystore", "server.truststore");
+ }
- try {
- final SSLContext context = SSLContext.getInstance(provider);
- context.init(keyManagers, trustManagers, new SecureRandom());
- return context;
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Unable to create and initialise the SSLContext", e);
- }
+ static SSLContext createClientSSLContext(String provider) throws IOException {
+ return createSSLContext(provider, "client.keystore", "client.truststore");
}
static SSLContext createDSASSLContext(String provider) throws IOException {
- KeyManager[] keyManagers;
- try {
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- keyManagerFactory.init(loadKeyStore("server-dsa.keystore"), "password".toCharArray());
- keyManagers = keyManagerFactory.getKeyManagers();
- } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
- throw new RuntimeException("Unable to initialise KeyManager[]", e);
- }
-
- try {
- final SSLContext context = SSLContext.getInstance(provider);
- context.init(keyManagers, null, new SecureRandom());
- return context;
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Unable to create and initialise the SSLContext", e);
- }
+ return createSSLContext(provider, "server-dsa.keystore", null);
}
+
static SSLContext createClientDSASSLContext(String provider) throws IOException {
- TrustManager[] trustManagers = null;
- try {
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init(loadKeyStore("client-dsa.truststore"));
- trustManagers = trustManagerFactory.getTrustManagers();
- } catch (NoSuchAlgorithmException | KeyStoreException e) {
- throw new RuntimeException("Unable to initialise TrustManager[]", e);
- }
+ return createSSLContext(provider, null, "client-dsa.truststore");
+ }
- try {
- final SSLContext context = SSLContext.getInstance(provider);
- context.init(null, trustManagers, new SecureRandom());
- return context;
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Unable to create and initialise the SSLContext", e);
- }
+ static SSLContext createECSSLContext(String provider) throws IOException {
+ return createSSLContext(provider, "server-ec.keystore", "server-ec.truststore");
+ }
+
+ static SSLContext createClientECSSLContext(String provider) throws IOException {
+ return createSSLContext(provider, "client-ec.keystore", "client-ec.truststore");
}
public static byte[] readData(InputStream in) throws IOException {
diff --git a/java/src/test/resources/client-ec.keystore b/java/src/test/resources/client-ec.keystore
new file mode 100644
index 00000000..ec4a27fb
Binary files /dev/null and b/java/src/test/resources/client-ec.keystore differ
diff --git a/java/src/test/resources/client-ec.truststore b/java/src/test/resources/client-ec.truststore
new file mode 100644
index 00000000..cf043739
Binary files /dev/null and b/java/src/test/resources/client-ec.truststore differ
diff --git a/java/src/test/resources/server-ec.keystore b/java/src/test/resources/server-ec.keystore
new file mode 100644
index 00000000..e56f698a
Binary files /dev/null and b/java/src/test/resources/server-ec.keystore differ
diff --git a/java/src/test/resources/server-ec.truststore b/java/src/test/resources/server-ec.truststore
new file mode 100644
index 00000000..6380858e
Binary files /dev/null and b/java/src/test/resources/server-ec.truststore differ