package com.github.dockerjava.core.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;
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.jce.provider.BouncyCastleProvider;
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
}
public static boolean verifyCertificatesExist(String dockerCertPath) {
String[] files = {"ca.pem", "cert.pem", "key.pem"};
boolean result = true;
for (String file : files) {
File path = new File(dockerCertPath, file);
result &= path.exists();
}
return result;
}
@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",
privateKey,
"docker".toCharArray(),
privateCertificates.toArray(new Certificate[privateCertificates.size()])
);
return keyStore;
}
/**
* from "cert.pem" String
*/
public 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);
}
}
/**
* "cert.pem" from reader
*/
public static List<Certificate> loadCertificates(final Reader reader) throws IOException,
CertificateException {
try (PEMParser pemParser = new PEMParser(reader)) {
List<Certificate> certificates = new ArrayList<>();
JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter()
.setProvider(BouncyCastleProvider.PROVIDER_NAME);
Object certObj = pemParser.readObject();
if (certObj instanceof X509CertificateHolder) {
X509CertificateHolder certificateHolder = (X509CertificateHolder) certObj;
certificates.add(certificateConverter.getCertificate(certificateHolder));
}
return certificates;
}
}
/**
* Return private key ("key.pem") from Reader
*/
@CheckForNull
public 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();
}
}
return null;
}
@CheckForNull
public 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);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
return factory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException ignore) {
}
}
return null;
}
/**
* Return KeyPair from "key.pem"
*/
@CheckForNull
public static PrivateKey loadPrivateKey(final String keypem) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException {
try (StringReader certReader = new StringReader(keypem);
BufferedReader reader = new BufferedReader(certReader)) {
return loadPrivateKey(reader);
}
}
/**
* "ca.pem" from String
*/
public static KeyStore createTrustStore(String capem) throws IOException, CertificateException,
KeyStoreException, NoSuchAlgorithmException {
try (Reader certReader = new StringReader(capem)) {
return createTrustStore(certReader);
}
}
/**
* "ca.pem" from Reader
*/
public static KeyStore createTrustStore(final Reader certReader) throws IOException, CertificateException,
KeyStoreException, NoSuchAlgorithmException {
try (PEMParser pemParser = new PEMParser(certReader)) {
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null);
int index = 1;
Object pemCert;
while ((pemCert = pemParser.readObject()) != null) {
Certificate caCertificate = new JcaX509CertificateConverter()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.getCertificate((X509CertificateHolder) pemCert);
trustStore.setCertificateEntry("ca-" + index, caCertificate);
index++;
}
return trustStore;
}
}
}