-
Notifications
You must be signed in to change notification settings - Fork 25.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Limit the scope of BouncyCastle dependency #30358
Changes from 9 commits
c7121a8
522c2c5
22bc268
02c7c58
baaa4d4
5d3ea23
fd46142
ac516a1
17fddd4
397e952
4b26b2b
4cd7f81
dbb4dcc
36620c5
49bd8aa
ecd1e3a
3598482
aa68c59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,6 +88,7 @@ compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,- | |
licenseHeaders { | ||
approvedLicenses << 'BCrypt (BSD-like)' | ||
additionalLicense 'BCRYP', 'BCrypt (BSD-like)', 'Copyright (c) 2006 Damien Miller <[email protected]>' | ||
excludes << 'org/elasticsearch/xpack/core/ssl/DerParser.java' | ||
} | ||
|
||
// make LicenseSigner available for testing signed licenses | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.ssl; | ||
|
||
import org.elasticsearch.common.Nullable; | ||
import org.elasticsearch.common.SuppressForbidden; | ||
import org.elasticsearch.common.io.PathUtils; | ||
import org.elasticsearch.common.settings.SecureString; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.env.Environment; | ||
|
||
import javax.net.ssl.KeyManager; | ||
import javax.net.ssl.KeyManagerFactory; | ||
import javax.net.ssl.TrustManager; | ||
import javax.net.ssl.TrustManagerFactory; | ||
import javax.net.ssl.X509ExtendedKeyManager; | ||
import javax.net.ssl.X509ExtendedTrustManager; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
|
||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.security.Key; | ||
import java.security.KeyStore; | ||
import java.security.KeyStoreException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.PrivateKey; | ||
import java.security.UnrecoverableKeyException; | ||
import java.security.cert.Certificate; | ||
import java.security.cert.CertificateException; | ||
import java.security.cert.CertificateFactory; | ||
import java.security.cert.X509Certificate; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Enumeration; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
import static org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings.getKeyStoreType; | ||
|
||
public class CertParsingUtils { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a private constructor as we should never create an instance of this class |
||
/** | ||
* Resolves a path with or without an {@link Environment} as we may be running in a transport client where we do not have access to | ||
* the environment | ||
*/ | ||
@SuppressForbidden(reason = "we don't have the environment to resolve files from when running in a transport client") | ||
static Path resolvePath(String path, @Nullable Environment environment) { | ||
if (environment != null) { | ||
return environment.configFile().resolve(path); | ||
} | ||
return PathUtils.get(path).normalize(); | ||
} | ||
|
||
static KeyStore readKeyStore(Path path, String type, char[] password) | ||
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { | ||
try (InputStream in = Files.newInputStream(path)) { | ||
KeyStore store = KeyStore.getInstance(type); | ||
assert password != null; | ||
store.load(in, password); | ||
return store; | ||
} | ||
} | ||
|
||
/** | ||
* Reads the provided paths and parses them into {@link Certificate} objects | ||
* | ||
* @param certPaths the paths to the PEM encoded certificates | ||
* @param environment the environment to resolve files against. May be {@code null} | ||
* @return an array of {@link Certificate} objects | ||
*/ | ||
public static Certificate[] readCertificates(List<String> certPaths, @Nullable Environment environment) | ||
throws CertificateException, IOException { | ||
final List<Path> resolvedPaths = certPaths.stream().map(p -> resolvePath(p, environment)).collect(Collectors.toList()); | ||
return readCertificates(resolvedPaths); | ||
} | ||
|
||
public static Certificate[] readCertificates(List<Path> certPaths) throws CertificateException, IOException { | ||
Collection<Certificate> certificates = new ArrayList<>(); | ||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); | ||
for (Path path : certPaths) { | ||
try (InputStream input = Files.newInputStream(path)) { | ||
certificates.addAll((Collection<Certificate>) certFactory.generateCertificates(input)); | ||
} | ||
} | ||
return certificates.toArray(new Certificate[0]); | ||
} | ||
|
||
public static X509Certificate[] readX509Certificates(List<Path> certPaths) throws CertificateException, IOException { | ||
Collection<X509Certificate> certificates = new ArrayList<>(); | ||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); | ||
for (Path path : certPaths) { | ||
try (InputStream input = Files.newInputStream(path)) { | ||
certificates.addAll((Collection<X509Certificate>) certFactory.generateCertificates(input)); | ||
} | ||
} | ||
return certificates.toArray(new X509Certificate[0]); | ||
} | ||
|
||
static List<Certificate> readCertificates(InputStream input) throws CertificateException, IOException { | ||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); | ||
Collection<Certificate> certificates = (Collection<Certificate>) certFactory.generateCertificates(input); | ||
return new ArrayList<>(certificates); | ||
} | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: extra line |
||
/** | ||
* Read all certificate-key pairs from a PKCS#12 container. | ||
* | ||
* @param path The path to the PKCS#12 container file. | ||
* @param password The password for the container file | ||
* @param keyPassword A supplier for the password for each key. The key alias is supplied as an argument to the function, and it should | ||
* return the password for that key. If it returns {@code null}, then the key-pair for that alias is not read. | ||
*/ | ||
public static Map<Certificate, Key> readPkcs12KeyPairs(Path path, char[] password, Function<String, char[]> keyPassword) | ||
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException { | ||
final KeyStore store = readKeyStore(path, "PKCS12", password); | ||
final Enumeration<String> enumeration = store.aliases(); | ||
final Map<Certificate, Key> map = new HashMap<>(store.size()); | ||
while (enumeration.hasMoreElements()) { | ||
final String alias = enumeration.nextElement(); | ||
if (store.isKeyEntry(alias)) { | ||
final char[] pass = keyPassword.apply(alias); | ||
map.put(store.getCertificate(alias), store.getKey(alias, pass)); | ||
} | ||
} | ||
return map; | ||
} | ||
|
||
/** | ||
* Creates a {@link KeyStore} from a PEM encoded certificate and key file | ||
*/ | ||
static KeyStore getKeyStoreFromPEM(Path certificatePath, Path keyPath, char[] keyPassword) | ||
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException { | ||
final PrivateKey key = PemUtils.readPrivateKey(keyPath, () -> keyPassword); | ||
final Certificate[] certificates = readCertificates(Collections.singletonList(certificatePath)); | ||
return getKeyStore(certificates, key, keyPassword); | ||
} | ||
|
||
/** | ||
* Returns a {@link X509ExtendedKeyManager} that is built from the provided private key and certificate chain | ||
*/ | ||
public static X509ExtendedKeyManager keyManager(Certificate[] certificateChain, PrivateKey privateKey, char[] password) | ||
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException { | ||
KeyStore keyStore = getKeyStore(certificateChain, privateKey, password); | ||
return keyManager(keyStore, password, KeyManagerFactory.getDefaultAlgorithm()); | ||
} | ||
|
||
private static KeyStore getKeyStore(Certificate[] certificateChain, PrivateKey privateKey, char[] password) | ||
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { | ||
KeyStore keyStore = KeyStore.getInstance("jks"); | ||
keyStore.load(null, null); | ||
// password must be non-null for keystore... | ||
keyStore.setKeyEntry("key", privateKey, password, certificateChain); | ||
return keyStore; | ||
} | ||
|
||
/** | ||
* Returns a {@link X509ExtendedKeyManager} that is built from the provided keystore | ||
*/ | ||
static X509ExtendedKeyManager keyManager(KeyStore keyStore, char[] password, String algorithm) | ||
throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException { | ||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); | ||
kmf.init(keyStore, password); | ||
KeyManager[] keyManagers = kmf.getKeyManagers(); | ||
for (KeyManager keyManager : keyManagers) { | ||
if (keyManager instanceof X509ExtendedKeyManager) { | ||
return (X509ExtendedKeyManager) keyManager; | ||
} | ||
} | ||
throw new IllegalStateException("failed to find a X509ExtendedKeyManager"); | ||
} | ||
|
||
public static X509ExtendedKeyManager getKeyManager(X509KeyPairSettings keyPair, Settings settings, | ||
@Nullable String trustStoreAlgorithm, Environment environment) { | ||
if (trustStoreAlgorithm == null) { | ||
trustStoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); | ||
} | ||
final KeyConfig keyConfig = createKeyConfig(keyPair, settings, trustStoreAlgorithm); | ||
if (keyConfig == null) { | ||
return null; | ||
} else { | ||
return keyConfig.createKeyManager(environment); | ||
} | ||
} | ||
|
||
static KeyConfig createKeyConfig(X509KeyPairSettings keyPair, Settings settings, String trustStoreAlgorithm) { | ||
String keyPath = keyPair.keyPath.get(settings).orElse(null); | ||
String keyStorePath = keyPair.keystorePath.get(settings).orElse(null); | ||
|
||
if (keyPath != null && keyStorePath != null) { | ||
throw new IllegalArgumentException("you cannot specify a keystore and key file"); | ||
} | ||
|
||
if (keyPath != null) { | ||
SecureString keyPassword = keyPair.keyPassword.get(settings); | ||
String certPath = keyPair.certificatePath.get(settings).orElse(null); | ||
if (certPath == null) { | ||
throw new IllegalArgumentException("you must specify the certificates [" + keyPair.certificatePath.getKey() | ||
+ "] to use with the key [" + keyPair.keyPath.getKey() + "]"); | ||
} | ||
return new PEMKeyConfig(keyPath, keyPassword, certPath); | ||
} | ||
|
||
if (keyStorePath != null) { | ||
SecureString keyStorePassword = keyPair.keystorePassword.get(settings); | ||
String keyStoreAlgorithm = keyPair.keystoreAlgorithm.get(settings); | ||
String keyStoreType = getKeyStoreType(keyPair.keystoreType, settings, keyStorePath); | ||
SecureString keyStoreKeyPassword = keyPair.keystoreKeyPassword.get(settings); | ||
if (keyStoreKeyPassword.length() == 0) { | ||
keyStoreKeyPassword = keyStorePassword; | ||
} | ||
return new StoreKeyConfig(keyStorePath, keyStoreType, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, | ||
trustStoreAlgorithm); | ||
} | ||
return null; | ||
|
||
} | ||
|
||
/** | ||
* Creates a {@link X509ExtendedTrustManager} based on the provided certificates | ||
* | ||
* @param certificates the certificates to trust | ||
* @return a trust manager that trusts the provided certificates | ||
*/ | ||
public static X509ExtendedTrustManager trustManager(Certificate[] certificates) | ||
throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { | ||
KeyStore store = trustStore(certificates); | ||
return trustManager(store, TrustManagerFactory.getDefaultAlgorithm()); | ||
} | ||
|
||
static KeyStore trustStore(Certificate[] certificates) | ||
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { | ||
assert certificates != null : "Cannot create trust store with null certificates"; | ||
KeyStore store = KeyStore.getInstance("jks"); | ||
store.load(null, null); | ||
int counter = 0; | ||
for (Certificate certificate : certificates) { | ||
store.setCertificateEntry("cert" + counter, certificate); | ||
counter++; | ||
} | ||
return store; | ||
} | ||
|
||
/** | ||
* Loads the truststore and creates a {@link X509ExtendedTrustManager} | ||
* | ||
* @param trustStorePath the path to the truststore | ||
* @param trustStorePassword the password to the truststore | ||
* @param trustStoreAlgorithm the algorithm to use for the truststore | ||
* @param env the environment to use for file resolution. May be {@code null} | ||
* @return a trust manager with the trust material from the store | ||
*/ | ||
public static X509ExtendedTrustManager trustManager(String trustStorePath, String trustStoreType, char[] trustStorePassword, | ||
String trustStoreAlgorithm, @Nullable Environment env) | ||
throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { | ||
KeyStore trustStore = readKeyStore(resolvePath(trustStorePath, env), trustStoreType, trustStorePassword); | ||
return trustManager(trustStore, trustStoreAlgorithm); | ||
} | ||
|
||
/** | ||
* Creates a {@link X509ExtendedTrustManager} based on the trust material in the provided {@link KeyStore} | ||
*/ | ||
static X509ExtendedTrustManager trustManager(KeyStore keyStore, String algorithm) | ||
throws NoSuchAlgorithmException, KeyStoreException { | ||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); | ||
tmf.init(keyStore); | ||
TrustManager[] trustManagers = tmf.getTrustManagers(); | ||
for (TrustManager trustManager : trustManagers) { | ||
if (trustManager instanceof X509ExtendedTrustManager) { | ||
return (X509ExtendedTrustManager) trustManager; | ||
} | ||
} | ||
throw new IllegalStateException("failed to find a X509ExtendedTrustManager"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
per the latest guidance, let's remove the changelog update from the PR