From a19e8b9b0f88b80e77592b33cd2e510aca2e9706 Mon Sep 17 00:00:00 2001 From: Christoffer Soop Date: Wed, 4 Apr 2018 22:46:38 +0200 Subject: [PATCH] Support EC key spec algorithm fixes #887 --- .../docker/client/DockerCertificates.java | 43 +++++++++++++++---- .../docker/client/DockerCertificatesTest.java | 27 ++++++++++-- .../dockerSslDirectoryWithEcKey/ca.pem | 21 +++++++++ .../dockerSslDirectoryWithEcKey/cert.pem | 13 ++++++ .../dockerSslDirectoryWithEcKey/cert.pub | 4 ++ .../dockerSslDirectoryWithEcKey/key.pem | 5 +++ 6 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/dockerSslDirectoryWithEcKey/ca.pem create mode 100644 src/test/resources/dockerSslDirectoryWithEcKey/cert.pem create mode 100644 src/test/resources/dockerSslDirectoryWithEcKey/cert.pub create mode 100644 src/test/resources/dockerSslDirectoryWithEcKey/key.pem diff --git a/src/main/java/com/spotify/docker/client/DockerCertificates.java b/src/main/java/com/spotify/docker/client/DockerCertificates.java index 6d5f6adc2..eeca4c071 100644 --- a/src/main/java/com/spotify/docker/client/DockerCertificates.java +++ b/src/main/java/com/spotify/docker/client/DockerCertificates.java @@ -20,7 +20,9 @@ package com.spotify.docker.client; +import com.google.common.base.Joiner; import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; import com.spotify.docker.client.exceptions.DockerCertificateException; import java.io.BufferedReader; @@ -44,6 +46,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.List; +import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; @@ -67,6 +70,7 @@ public class DockerCertificates implements DockerCertificatesStore { public static final String DEFAULT_CLIENT_KEY_NAME = "key.pem"; private static final char[] KEY_STORE_PASSWORD = "docker!!11!!one!".toCharArray(); + private static final Set PRIVATE_KEY_ALGS = ImmutableSet.of("RSA", "EC"); private static final Logger log = LoggerFactory.getLogger(DockerCertificates.class); private final SSLContext sslContext; @@ -123,15 +127,15 @@ private KeyStore newKeyStore() throws CertificateException, NoSuchAlgorithmExcep return keyStore; } - private PrivateKey readPrivateKey(Path file) throws IOException, InvalidKeySpecException, - NoSuchAlgorithmException, DockerCertificateException { - try (BufferedReader reader = Files.newBufferedReader(file, Charset.defaultCharset()); - PEMParser pemParser = new PEMParser(reader)) { + private PrivateKey readPrivateKey(final Path file) + throws IOException, InvalidKeySpecException, DockerCertificateException { + try (final BufferedReader reader = Files.newBufferedReader(file, Charset.defaultCharset()); + final PEMParser pemParser = new PEMParser(reader)) { final Object readObject = pemParser.readObject(); if (readObject instanceof PEMKeyPair) { - PEMKeyPair clientKeyPair = (PEMKeyPair) readObject; + final PEMKeyPair clientKeyPair = (PEMKeyPair) readObject; return generatePrivateKey(clientKeyPair.getPrivateKeyInfo()); } else if (readObject instanceof PrivateKeyInfo) { return generatePrivateKey((PrivateKeyInfo) readObject); @@ -142,11 +146,32 @@ private PrivateKey readPrivateKey(Path file) throws IOException, InvalidKeySpecE } } - private static PrivateKey generatePrivateKey(PrivateKeyInfo privateKeyInfo) throws IOException, - InvalidKeySpecException, NoSuchAlgorithmException { + private static PrivateKey generatePrivateKey(final PrivateKeyInfo privateKeyInfo) + throws IOException, InvalidKeySpecException { final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded()); - final KeyFactory kf = KeyFactory.getInstance("RSA"); - return kf.generatePrivate(spec); + return tryGeneratePrivateKey(spec, PRIVATE_KEY_ALGS); + } + + private static PrivateKey tryGeneratePrivateKey(final PKCS8EncodedKeySpec spec, + final Set algorithms) + throws InvalidKeySpecException { + + KeyFactory kf; + PrivateKey key; + for (final String algorithm : algorithms) { + try { + kf = KeyFactory.getInstance(algorithm); + key = kf.generatePrivate(spec); + log.debug("Generated private key from spec using the '{}' algorithm", algorithm); + return key; + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + log.debug("Tried generating private key from spec using the '{}' algorithm", algorithm, e); + } + } + + final String error = String.format("Could not generate private key from spec. Tried using %s", + Joiner.on(", ").join(algorithms)); + throw new InvalidKeySpecException(error); } private List readCertificates(Path file) throws CertificateException, IOException { diff --git a/src/test/java/com/spotify/docker/client/DockerCertificatesTest.java b/src/test/java/com/spotify/docker/client/DockerCertificatesTest.java index 53c16af4f..1c2fed741 100644 --- a/src/test/java/com/spotify/docker/client/DockerCertificatesTest.java +++ b/src/test/java/com/spotify/docker/client/DockerCertificatesTest.java @@ -20,9 +20,11 @@ package com.spotify.docker.client; +import static java.lang.System.getenv; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -30,17 +32,17 @@ import com.google.common.io.Resources; import com.spotify.docker.client.DockerCertificates.SslContextFactory; import com.spotify.docker.client.exceptions.DockerCertificateException; - import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; - import org.junit.Test; import org.mockito.ArgumentCaptor; public class DockerCertificatesTest { + private static final boolean TRAVIS = "true".equals(getenv("TRAVIS")); + private SslContextFactory factory = mock(SslContextFactory.class); private ArgumentCaptor keyStore = ArgumentCaptor.forClass(KeyStore.class); private ArgumentCaptor trustStore = ArgumentCaptor.forClass(KeyStore.class); @@ -137,7 +139,24 @@ public void testReadPrivateKeyPkcs8() throws Exception { assertNotNull(pkEntry.getPrivateKey()); } - private Path getResourceFile(String path) throws URISyntaxException { + @Test + public void testReadEllipticCurvePrivateKey() throws Exception { + assumeFalse("Travis' openjdk7 doesn't support the elliptic curve algorithm", TRAVIS); + + DockerCertificates.builder() + .dockerCertPath(getResourceFile("dockerSslDirectoryWithEcKey")) + .sslFactory(factory) + .build(); + + verify(factory).newSslContext(keyStore.capture(), password.capture(), trustStore.capture()); + + final KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getValue() + .getEntry("key", new KeyStore.PasswordProtection(password.getValue())); + + assertNotNull(pkEntry.getPrivateKey()); + } + + private Path getResourceFile(final String path) throws URISyntaxException { return Paths.get(Resources.getResource(path).toURI()); } @@ -145,7 +164,7 @@ private Path getCertPath() throws URISyntaxException { return getResourceFile("dockerSslDirectory"); } - private Path getVariant(String filename) throws URISyntaxException { + private Path getVariant(final String filename) throws URISyntaxException { return getResourceFile("dockerSslVariants").resolve(filename); } } diff --git a/src/test/resources/dockerSslDirectoryWithEcKey/ca.pem b/src/test/resources/dockerSslDirectoryWithEcKey/ca.pem new file mode 100644 index 000000000..0b0ff0124 --- /dev/null +++ b/src/test/resources/dockerSslDirectoryWithEcKey/ca.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIBgTCCASegAwIBAgIUSDpwT90Xy4XatwFHXuyazJQNEf4wCgYIKoZIzj0EAwIw +HTEbMBkGA1UEAxMSVUNQIENsaWVudCBSb290IENBMB4XDTE4MDIyMjEwMjIwMFoX +DTIzMDIyMTEwMjIwMFowHTEbMBkGA1UEAxMSVUNQIENsaWVudCBSb290IENBMFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7/PN6GE2k0y3927Y2cZpV29o4vwvRqVp +hbjRBDqDrbEDNQ2iObGTI0h2N3AedH8ID9YtzrkIlIyQrRqBiYeDUqNFMEMwDgYD +VR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFLW4zQYH +RlnC2+K+yJUUmm8HK6oNMAoGCCqGSM49BAMCA0gAMEUCIQDv+a4ZccY7eAKZ6Uv9 +8iYt/It0oi8/yKjwvcj+xq4cWAIgOoG5nUw+kJgdeKWh2ykeKkr8W35YKPVUgLLT +Dly4Dgg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBajCCARCgAwIBAgIUM9oWZbj9B/Zn+nMRW/ZSby3kCY4wCgYIKoZIzj0EAwIw +EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTgwMjIyMTAyMjAwWhcNMzgwMjE3MTAy +MjAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABHB576n29gJNGxzciJBM8bpltIaCJGR91AlKVcQOJiXcqzS1l69Ypy2bkBpG +Q+OGFFxLMm4LwQ9gEXWn8pWD4LOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBSrjMUwJK/LbbGGvtw9lSdU2ee1fTAKBggqhkjO +PQQDAgNIADBFAiEAuZITvKIFBc0qgxDmXglz+NSqBTqhLum6RMAjGbX7EZYCICXq +T4LPHVlg7FCJlK5jdGvHXKCK0iD9SJDFY6nac74S +-----END CERTIFICATE----- diff --git a/src/test/resources/dockerSslDirectoryWithEcKey/cert.pem b/src/test/resources/dockerSslDirectoryWithEcKey/cert.pem new file mode 100644 index 000000000..3966df789 --- /dev/null +++ b/src/test/resources/dockerSslDirectoryWithEcKey/cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICBjCCAaugAwIBAgIUNKAK+kxeV1WQ2f14p2XZ9lDLToYwCgYIKoZIzj0EAwIw +EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTgwNDAxMTE1MjAwWhcNMjgwMzI5MTE1 +MjAwWjBsMQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMSgwJgYDVQQK +Ex9PcmNhOiAzenhyMmVkNWdncm1qN2c0a3Vzd29pbm9iMQ8wDQYDVQQLEwZDbGll +bnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4E38 +k2IT/7YDQ1xJz7AKqtFej9kiNZGktrYdXRKaDHnrwwKHrZPIn2B1QWsJkbEc2GPN +E4PQvjT6z9m0n3ibX6OBgzCBgDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYI +KwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUYUZw4IfDSxvF7RMMUNNz +oZGNGg8wHwYDVR0jBBgwFoAUq4zFMCSvy22xhr7cPZUnVNnntX0wCwYDVR0RBAQw +AoEAMAoGCCqGSM49BAMCA0kAMEYCIQDx+3BtBxIFtz/l2JNbbp/UmSH3ki70lJbB +ot8exwXWXwIhAOiiZ2hlAPcPE1x3tNUpTmSemvGog5M8hyePWtviq+qy +-----END CERTIFICATE----- diff --git a/src/test/resources/dockerSslDirectoryWithEcKey/cert.pub b/src/test/resources/dockerSslDirectoryWithEcKey/cert.pub new file mode 100644 index 000000000..e10e395f6 --- /dev/null +++ b/src/test/resources/dockerSslDirectoryWithEcKey/cert.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4E38k2IT/7YDQ1xJz7AKqtFej9ki +NZGktrYdXRKaDHnrwwKHrZPIn2B1QWsJkbEc2GPNE4PQvjT6z9m0n3ibXw== +-----END PUBLIC KEY----- diff --git a/src/test/resources/dockerSslDirectoryWithEcKey/key.pem b/src/test/resources/dockerSslDirectoryWithEcKey/key.pem new file mode 100644 index 000000000..65951b875 --- /dev/null +++ b/src/test/resources/dockerSslDirectoryWithEcKey/key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPiEL1Rb11CWgyr+e5KF2Gu4jh7QR2FywdHMGXnE5bvRoAoGCCqGSM49 +AwEHoUQDQgAE4E38k2IT/7YDQ1xJz7AKqtFej9kiNZGktrYdXRKaDHnrwwKHrZPI +n2B1QWsJkbEc2GPNE4PQvjT6z9m0n3ibXw== +-----END EC PRIVATE KEY-----