Skip to content
This repository has been archived by the owner on Mar 21, 2022. It is now read-only.

Commit

Permalink
Support EC key spec algorithm
Browse files Browse the repository at this point in the history
fixes #887
  • Loading branch information
Christoffer Soop authored and davidxia committed Jun 2, 2018
1 parent dbae73f commit a19e8b9
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 13 deletions.
43 changes: 34 additions & 9 deletions src/main/java/com/spotify/docker/client/DockerCertificates.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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<String> PRIVATE_KEY_ALGS = ImmutableSet.of("RSA", "EC");
private static final Logger log = LoggerFactory.getLogger(DockerCertificates.class);

private final SSLContext sslContext;
Expand Down Expand Up @@ -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);
Expand All @@ -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<String> 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<Certificate> readCertificates(Path file) throws CertificateException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,29 @@

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;

import com.google.common.base.Optional;
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> keyStore = ArgumentCaptor.forClass(KeyStore.class);
private ArgumentCaptor<KeyStore> trustStore = ArgumentCaptor.forClass(KeyStore.class);
Expand Down Expand Up @@ -137,15 +139,32 @@ 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());
}

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);
}
}
21 changes: 21 additions & 0 deletions src/test/resources/dockerSslDirectoryWithEcKey/ca.pem
Original file line number Diff line number Diff line change
@@ -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-----
13 changes: 13 additions & 0 deletions src/test/resources/dockerSslDirectoryWithEcKey/cert.pem
Original file line number Diff line number Diff line change
@@ -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-----
4 changes: 4 additions & 0 deletions src/test/resources/dockerSslDirectoryWithEcKey/cert.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4E38k2IT/7YDQ1xJz7AKqtFej9ki
NZGktrYdXRKaDHnrwwKHrZPIn2B1QWsJkbEc2GPNE4PQvjT6z9m0n3ibXw==
-----END PUBLIC KEY-----
5 changes: 5 additions & 0 deletions src/test/resources/dockerSslDirectoryWithEcKey/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPiEL1Rb11CWgyr+e5KF2Gu4jh7QR2FywdHMGXnE5bvRoAoGCCqGSM49
AwEHoUQDQgAE4E38k2IT/7YDQ1xJz7AKqtFej9kiNZGktrYdXRKaDHnrwwKHrZPI
n2B1QWsJkbEc2GPNE4PQvjT6z9m0n3ibXw==
-----END EC PRIVATE KEY-----

0 comments on commit a19e8b9

Please sign in to comment.