Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 128 additions & 78 deletions src/main/java/com/github/dockerjava/core/util/CertificateUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.CheckForNull;

import static java.util.Objects.requireNonNull;

public class CertificateUtils {
private static final Logger LOG = LoggerFactory.getLogger(CertificateUtils.class);

private CertificateUtils() {
// utility class
}
Expand All @@ -41,115 +49,157 @@ public static boolean verifyCertificatesExist(String dockerCertPath) {
return result;
}

/**
* @param dockerCertPath with standard named files.
*/
public static KeyStore createKeyStore(final String dockerCertPath) throws NoSuchAlgorithmException,
InvalidKeySpecException, IOException, CertificateException, KeyStoreException {
KeyPair keyPair = loadPrivateKey(dockerCertPath);
List<Certificate> privateCertificates = loadCertificates(dockerCertPath);
return createKeyStore("key.pem", "cert.pem");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dockerCertPath is ignored completely here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to fix locally

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smells like i missed this wrapper when refactored methods to be reusable for non-file based usage.
Could you fix or should i?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess it should just load files into string, or pass streams and everything would work

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to load into strings but I ended with a javax.ws.rs.ProcessingException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: signature check failed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe ca.cert is missing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call to createTrustStore...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, had a typo there. Now it seems to work.

}


@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public static KeyStore createKeyStore(final String keypem, final String certpem) throws NoSuchAlgorithmException,
InvalidKeySpecException, IOException, CertificateException, KeyStoreException {
PrivateKey privateKey = loadPrivateKey(keypem);
requireNonNull(privateKey);
List<Certificate> privateCertificates = loadCertificates(certpem);

KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null);

keyStore.setKeyEntry("docker", keyPair.getPrivate(), "docker".toCharArray(),
privateCertificates.toArray(new Certificate[privateCertificates.size()]));
keyStore.setKeyEntry("docker",
privateKey,
"docker".toCharArray(),
privateCertificates.toArray(new Certificate[privateCertificates.size()])
);

return keyStore;
}

public static KeyStore createTrustStore(final String dockerCertPath) throws IOException, CertificateException,
KeyStoreException, NoSuchAlgorithmException {
File caPath = new File(dockerCertPath, "ca.pem");
BufferedReader reader = new BufferedReader(new FileReader(caPath));
PEMParser pemParser = null;

try {
pemParser = new PEMParser(reader);
X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject();
Certificate caCertificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(
certificateHolder);

KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null);
trustStore.setCertificateEntry("ca", caCertificate);
return trustStore;

} finally {
if (pemParser != null) {
IOUtils.closeQuietly(pemParser);
}

if (reader != null) {
IOUtils.closeQuietly(reader);
}
/**
* from "cert.pem" String
*/
private static List<Certificate> loadCertificates(final String certpem) throws IOException,
CertificateException {
final StringReader certReader = new StringReader(certpem);
try (BufferedReader reader = new BufferedReader(certReader)) {
return loadCertificates(reader);
}

}

private static List<Certificate> loadCertificates(final String dockerCertPath) throws IOException,
/**
* "cert.pem" from reader
*/
private static List<Certificate> loadCertificates(final Reader reader) throws IOException,
CertificateException {
File certificate = new File(dockerCertPath, "cert.pem");
BufferedReader reader = new BufferedReader(new FileReader(certificate));
PEMParser pemParser = null;

try {
try (PEMParser pemParser = new PEMParser(reader)) {
List<Certificate> certificates = new ArrayList<>();
pemParser = new PEMParser(reader);

JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter().setProvider("BC");
Object certObj = pemParser.readObject();

while (certObj != null) {
if (certObj instanceof X509CertificateHolder) {
X509CertificateHolder certificateHolder = (X509CertificateHolder) certObj;
certificates.add(certificateConverter.getCertificate(certificateHolder));

certObj = pemParser.readObject();
}

return certificates;
} finally {
if (pemParser != null) {
IOUtils.closeQuietly(pemParser);
}

if (reader != null) {
IOUtils.closeQuietly(reader);
}
}

}

private static KeyPair loadPrivateKey(final String dockerCertPath) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
File certificate = new File(dockerCertPath, "key.pem");
BufferedReader reader = new BufferedReader(new FileReader(certificate));

PEMParser pemParser = null;
/**
* Return private key ("key.pem") from Reader
*/
@CheckForNull
private static PrivateKey loadPrivateKey(final Reader reader) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
try (PEMParser pemParser = new PEMParser(reader)) {
Object readObject = pemParser.readObject();
while (readObject != null) {
if (readObject instanceof PEMKeyPair) {
PEMKeyPair pemKeyPair = (PEMKeyPair) readObject;
PrivateKey privateKey = guessKey(pemKeyPair.getPrivateKeyInfo().getEncoded());
if (privateKey != null) {
return privateKey;
}
} else if (readObject instanceof PrivateKeyInfo) {
PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) readObject;
PrivateKey privateKey = guessKey(privateKeyInfo.getEncoded());
if (privateKey != null) {
return privateKey;
}
} else if (readObject instanceof ASN1ObjectIdentifier) {
// no idea how it can be used
final ASN1ObjectIdentifier asn1ObjectIdentifier = (ASN1ObjectIdentifier) readObject;
LOG.trace("Ignoring asn1ObjectIdentifier {}", asn1ObjectIdentifier);
} else {
LOG.warn("Unknown object '{}' from PEMParser", readObject);
}

readObject = pemParser.readObject();
}
}

try {
pemParser = new PEMParser(reader);
return null;
}

PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
@CheckForNull
private static PrivateKey guessKey(byte[] encodedKey) throws NoSuchAlgorithmException {
//no way to know, so iterate
for (String guessFactory : new String[]{"RSA", "ECDSA"}) {
try {
KeyFactory factory = KeyFactory.getInstance(guessFactory);

byte[] pemPrivateKeyEncoded = pemKeyPair.getPrivateKeyInfo().getEncoded();
byte[] pemPublicKeyEncoded = pemKeyPair.getPublicKeyInfo().getEncoded();
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
return factory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException ignore) {
}
}

KeyFactory factory = KeyFactory.getInstance("RSA");
return null;
}

X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pemPublicKeyEncoded);
PublicKey publicKey = factory.generatePublic(publicKeySpec);
/**
* Return KeyPair from "key.pem"
*/
@CheckForNull
private static PrivateKey loadPrivateKey(final String keypem) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
try (StringReader certReader = new StringReader(keypem);
BufferedReader reader = new BufferedReader(certReader)) {
return loadPrivateKey(reader);
}
}

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pemPrivateKeyEncoded);
PrivateKey privateKey = factory.generatePrivate(privateKeySpec);
/**
* "ca.pem" from String
*/
public static KeyStore createTrustStore(String capem) throws IOException, CertificateException,
KeyStoreException, NoSuchAlgorithmException {
try (Reader certReader = new StringReader(capem)) {
return createTrustStore(certReader);
}
}

return new KeyPair(publicKey, privateKey);
/**
* "ca.pem" from Reader
*/
public static KeyStore createTrustStore(final Reader certReader) throws IOException, CertificateException,
KeyStoreException, NoSuchAlgorithmException {
try (PEMParser pemParser = new PEMParser(certReader)) {
X509CertificateHolder certificateHolder = (X509CertificateHolder) pemParser.readObject();
Certificate caCertificate = new JcaX509CertificateConverter()
.setProvider("BC")
.getCertificate(certificateHolder);

} finally {
if (pemParser != null) {
IOUtils.closeQuietly(pemParser);
}
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null);
trustStore.setCertificateEntry("ca", caCertificate);

if (reader != null) {
IOUtils.closeQuietly(reader);
}
return trustStore;
}

}

}