Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.opensearch.test.framework.certificate.CertificateData;
import org.opensearch.test.framework.certificate.TestCertificates;

import static org.opensearch.security.ssl.util.SSLConfigConstants.DEFAULT_STORE_TYPE;
import static org.opensearch.test.framework.cluster.TestRestClientConfiguration.getBasicAuthHeader;

/**
Expand Down Expand Up @@ -273,7 +274,7 @@ private SSLContext getSSLContext(CertificateData useCertificateData) {
trustCertificates = PemKeyReader.loadCertificatesFromFile(getTestCertificates().getRootCertificate());

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore ks = KeyStore.getInstance(DEFAULT_STORE_TYPE);

ks.load(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import com.unboundid.ldif.LDIFReader;
import com.unboundid.util.ssl.SSLUtil;

import static org.opensearch.security.ssl.util.SSLConfigConstants.DEFAULT_STORE_TYPE;

/**
* Based on class org.opensearch.security.auth.ldap.srv.LdapServer from older tests
*/
Expand Down Expand Up @@ -154,7 +156,7 @@ private void addLdapCertificatesToKeystore(KeyStore keyStore) throws KeyStoreExc
}

private static KeyStore createEmptyKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore keyStore = KeyStore.getInstance(DEFAULT_STORE_TYPE);
keyStore.load(null);
return keyStore;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,14 @@
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -51,20 +47,14 @@
import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.ASN1TaggedObject;

import org.opensearch.OpenSearchException;
import org.opensearch.OpenSearchSecurityException;
import org.opensearch.common.settings.Settings;
import org.opensearch.env.Environment;
import org.opensearch.secure_sm.AccessController;
import org.opensearch.security.ssl.config.CertType;
import org.opensearch.security.ssl.config.SanParser;
import org.opensearch.security.ssl.util.CertFileProps;
import org.opensearch.security.ssl.util.CertFromFile;
import org.opensearch.security.ssl.util.CertFromKeystore;
Expand Down Expand Up @@ -1026,59 +1016,6 @@ private static void checkPath(String keystoreFilePath, String fileNameLogOnly) {

@Override
public String getSubjectAlternativeNames(X509Certificate cert) {
String san = "";
try {
Collection<List<?>> altNames = cert != null && cert.getSubjectAlternativeNames() != null
? cert.getSubjectAlternativeNames()
: null;
if (altNames != null) {
Comparator<List<?>> comparator = Comparator.comparing((List<?> altName) -> (Integer) altName.get(0))
.thenComparing((List<?> altName) -> (String) altName.get(1));

Set<List<?>> sans = new TreeSet<>(comparator);
for (List<?> altName : altNames) {
Integer type = (Integer) altName.get(0);
// otherName requires parsing to string
if (type == 0) {
List<?> otherName = getOtherName(altName);
if (otherName != null) {
sans.add(Arrays.asList(type, otherName));
}
} else {
sans.add(altName);
}
}
san = sans.toString();
}
} catch (CertificateParsingException e) {
log.error("Issue parsing SubjectAlternativeName:", e);
}

return san;
}

private List<String> getOtherName(List<?> altName) {
if (altName.size() < 2) {
log.warn("Couldn't parse subject alternative names");
return null;
}
try (final ASN1InputStream in = new ASN1InputStream((byte[]) altName.get(1))) {
final ASN1Primitive asn1Primitive = in.readObject();
final ASN1Sequence sequence = ASN1Sequence.getInstance(asn1Primitive);
final ASN1ObjectIdentifier asn1ObjectIdentifier = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0));
final ASN1TaggedObject asn1TaggedObject = ASN1TaggedObject.getInstance(sequence.getObjectAt(1));
ASN1Object maybeTaggedAsn1Primitive = asn1TaggedObject.getObject();
if (maybeTaggedAsn1Primitive instanceof ASN1TaggedObject) {
maybeTaggedAsn1Primitive = ASN1TaggedObject.getInstance(maybeTaggedAsn1Primitive).getObject();
}
if (maybeTaggedAsn1Primitive instanceof ASN1String) {
return ImmutableList.of(asn1ObjectIdentifier.getId(), maybeTaggedAsn1Primitive.toString());
} else {
log.warn("Couldn't parse subject alternative names");
return null;
}
} catch (final Exception ioe) { // catch all exception here since BC throws diff exceptions
throw new RuntimeException("Couldn't parse subject alternative names", ioe);
}
return SanParser.parse(cert);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,11 @@

package org.opensearch.security.ssl.config;

import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

import com.google.common.collect.ImmutableList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.ASN1TaggedObject;

public class Certificate {

private final static Logger LOGGER = LogManager.getLogger(Certificate.class);

private final X509Certificate certificate;

private final String format;
Expand Down Expand Up @@ -72,72 +52,13 @@ public boolean hasPrivateKey() {
}

public String subjectAlternativeNames() {
return loadSubjectAlternativeNames();
return SanParser.parse(certificate);
}

public byte[] signature() {
return certificate.getSignature();
}

@Deprecated(since = "since JDK 21", forRemoval = true)
public String loadSubjectAlternativeNames() {
String san = "";
try {
Collection<List<?>> altNames = certificate != null && certificate.getSubjectAlternativeNames() != null
? certificate.getSubjectAlternativeNames()
: null;
if (altNames != null) {
Comparator<List<?>> comparator = Comparator.comparing((List<?> altName) -> (Integer) altName.get(0))
.thenComparing((List<?> altName) -> (String) altName.get(1));

Set<List<?>> sans = new TreeSet<>(comparator);
for (List<?> altName : altNames) {
Integer type = (Integer) altName.get(0);
// otherName requires parsing to string
if (type == 0) {
List<?> otherName = parseOtherName(altName);
if (otherName != null) {
sans.add(Arrays.asList(type, otherName));
}
} else {
sans.add(altName);
}
}
san = sans.toString();
}
} catch (CertificateParsingException e) {
LOGGER.error("Issue parsing SubjectAlternativeName:", e);
}

return san;
}

@Deprecated(since = "since JDK 21", forRemoval = true)
private List<String> parseOtherName(List<?> altName) {
if (altName.size() < 2) {
LOGGER.warn("Couldn't parse subject alternative names");
return null;
}
try (final ASN1InputStream in = new ASN1InputStream((byte[]) altName.get(1))) {
final ASN1Primitive asn1Primitive = in.readObject();
final ASN1Sequence sequence = ASN1Sequence.getInstance(asn1Primitive);
final ASN1ObjectIdentifier asn1ObjectIdentifier = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0));
final ASN1TaggedObject asn1TaggedObject = ASN1TaggedObject.getInstance(sequence.getObjectAt(1));
ASN1Object maybeTaggedAsn1Primitive = asn1TaggedObject.getObject();
if (maybeTaggedAsn1Primitive instanceof ASN1TaggedObject) {
maybeTaggedAsn1Primitive = ASN1TaggedObject.getInstance(maybeTaggedAsn1Primitive).getObject();
}
if (maybeTaggedAsn1Primitive instanceof ASN1String) {
return ImmutableList.of(asn1ObjectIdentifier.getId(), maybeTaggedAsn1Primitive.toString());
} else {
LOGGER.warn("Couldn't parse subject alternative names");
return null;
}
} catch (final Exception ioe) { // catch all exception here since BC throws diff exceptions
throw new RuntimeException("Couldn't parse subject alternative names", ioe);
}
}

public String serialNumber() {
return certificate.getSerialNumber().toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
import io.netty.handler.ssl.ApplicationProtocolNegotiator;
import io.netty.handler.ssl.SslContext;

import static org.opensearch.security.ssl.util.SSLConfigConstants.DEFAULT_STORE_TYPE;

final class KeyStoreUtils {

private final static Logger log = LogManager.getLogger(KeyStoreUtils.class);
Expand Down Expand Up @@ -113,7 +115,7 @@ public static KeyStore loadTrustStore(final Path path, final String type, final
if (aliasCertificate == null) {
throw new OpenSearchException("Couldn't find SSL certificate for alias " + alias);
}
keyStore = newKeyStore();
keyStore = newKeyStore(type);
keyStore.setCertificateEntry(alias, aliasCertificate);
}
return keyStore;
Expand All @@ -137,7 +139,11 @@ public static KeyStore newTrustStoreFromPem(final Path pemFile) {
}

private static KeyStore newKeyStore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
final var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
return newKeyStore(DEFAULT_STORE_TYPE);
}

private static KeyStore newKeyStore(String type) throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
final var keyStore = KeyStore.getInstance(type);
keyStore.load(null, null);
return keyStore;
}
Expand Down Expand Up @@ -235,7 +241,7 @@ public static KeyStore newKeyStore(
throw new CertificateException("Couldn't find certificate chain for alias " + alias);
}
final var key = keyStore.getKey(alias, keyPassword);
keyStore = newKeyStore();
keyStore = newKeyStore(type);
keyStore.setKeyEntry(alias, key, keyPassword, certificateChain);
}
return keyStore;
Expand Down
78 changes: 78 additions & 0 deletions src/main/java/org/opensearch/security/ssl/config/SanParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.security.ssl.config;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.OtherName;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.crypto.CryptoServicesRegistrar;

public class SanParser {

private static final Logger LOGGER = LogManager.getLogger(SanParser.class);

private SanParser() {}

public static String parse(X509Certificate certificate) {
Comment thread
cwperks marked this conversation as resolved.
try {
X509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
GeneralNames generalNames = GeneralNames.fromExtensions(holder.getExtensions(), Extension.subjectAlternativeName);
if (generalNames == null) return "";

Comparator<List<?>> comparator = Comparator.comparing((List<?> n) -> (Integer) n.get(0))
.thenComparing((List<?> n) -> n.get(1).toString());
Set<List<?>> sans = new TreeSet<>(comparator);

for (GeneralName gn : generalNames.getNames()) {
int type = gn.getTagNo();
if (type == GeneralName.otherName) {
OtherName on = OtherName.getInstance(gn.getName());
ASN1Encodable value = on.getValue();
if (value instanceof ASN1String) {
sans.add(List.of(type, List.of(on.getTypeID().getId(), value.toString())));
} else {
LOGGER.warn("Couldn't parse OtherName SAN value");
}
} else if (type == GeneralName.iPAddress) {
byte[] octets = ASN1OctetString.getInstance(gn.getName()).getOctets();
sans.add(List.of(type, InetAddress.getByAddress(octets).getHostAddress()));
} else {
sans.add(List.of(type, gn.getName().toString()));
}
}
return sans.isEmpty() ? "" : sans.toString();
} catch (final CertificateEncodingException | UnknownHostException e) {
LOGGER.error("Couldn't parse subject alternative names", e);
if (CryptoServicesRegistrar.isInApprovedOnlyMode()) {
throw new RuntimeException("Couldn't parse subject alternative names", e);
}
return "";
}
}
}
Loading
Loading