Skip to content

Commit

Permalink
Add (Extended)KeyUsage KeyUsage, CipherSuite & Protocol to SSL diagno…
Browse files Browse the repository at this point in the history
…stics (#65634)

This commit extends the SSL diagnostics message to include descriptions of the

- The KeyUsage and ExtendedKeyUsage of the peer's certificate
- The CipherSuite & Protocol (TLS/SSL version) of the current session 

These can be helpful in diagnosing SSL errors.

Co-authored-by: Tim Vernum <tim@adjective.org>
  • Loading branch information
sindhusp and tvernum authored Aug 16, 2021
1 parent 51d524b commit f4e3f33
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SslDiagnostics {


public static List<String> describeValidHostnames(X509Certificate certificate) {
try {
final Collection<List<?>> names = certificate.getSubjectAlternativeNames();
Expand Down Expand Up @@ -140,6 +143,44 @@ boolean isSameCertificate() {
}
}

/**
* These names align with the values (and indices) defined by {@link X509Certificate#getKeyUsage()}
*/
private static final String[] KEY_USAGE_NAMES = new String[] {
"digitalSignature",
"nonRepudiation",
"keyEncipherment",
"dataEncipherment",
"keyAgreement",
"keyCertSign",
"cRLSign",
"encipherOnly",
"decipherOnly" };

private enum ExtendedKeyUsage {
serverAuth ("1.3.6.1.5.5.7.3.1"),
clientAuth ("1.3.6.1.5.5.7.3.2"),
codeSigning ("1.3.6.1.5.5.7.3.3"),
emailProtection ("1.3.6.1.5.5.7.3.4"),
timeStamping ("1.3.6.1.5.5.7.3.8"),
ocspSigning ("1.3.6.1.5.5.7.3.9");

private String oid;

ExtendedKeyUsage(String oid) {
this.oid = Objects.requireNonNull(oid);
}

public static String decodeOid(String oid) {
for (ExtendedKeyUsage e : values()) {
if (e.oid.equals(oid)) {
return e.name();
}
}
return oid;
}
}

/**
* @param contextName The descriptive name of this SSL context (e.g. "xpack.security.transport.ssl")
* @param trustedIssuers A Map of DN to Certificate, for the issuers that were trusted in the context in which this failure occurred
Expand All @@ -166,8 +207,14 @@ public static String getTrustDiagnosticFailure(X509Certificate[] chain, PeerType
.append(peerType.name().toLowerCase(Locale.ROOT))
.append(" provided a certificate with subject name [")
.append(peerCert.getSubjectX500Principal().getName())
.append("] and ")
.append(fingerprintDescription(peerCert));
.append("], ")
.append(fingerprintDescription(peerCert))
.append(", ")
.append(keyUsageDescription(peerCert))
.append(" and ")
.append(extendedKeyUsageDescription(peerCert));

addSessionDescription(session, message);

if (peerType == PeerType.SERVER) {
try {
Expand Down Expand Up @@ -395,4 +442,47 @@ private static boolean checkIssuer(X509Certificate certificate, X509Certificate
private static boolean isSelfIssued(X509Certificate certificate) {
return certificate.getIssuerX500Principal().equals(certificate.getSubjectX500Principal());
}

private static String keyUsageDescription(X509Certificate certificate) {
boolean[] keyUsage = certificate.getKeyUsage();
if (keyUsage == null || keyUsage.length == 0) {
return "no keyUsage";
}
final String keyUsageDescription = IntStream.range(0, keyUsage.length)
.filter(i -> keyUsage[i])
.mapToObj(i -> (i < KEY_USAGE_NAMES.length) ? KEY_USAGE_NAMES[i] : ("#" + i))
.collect(Collectors.joining(", "));
return keyUsageDescription.isEmpty() ? "no keyUsage" : ("keyUsage [" + keyUsageDescription + "]");
}

private static String extendedKeyUsageDescription(X509Certificate certificate) {
try {
return Optional.ofNullable(certificate.getExtendedKeyUsage())
.flatMap(keyUsage -> generateExtendedKeyUsageDescription(keyUsage))
.orElse("no extendedKeyUsage");
} catch (CertificateParsingException e) {
return "invalid extendedKeyUsage [" + e + "]";
}
}

private static Optional<String> generateExtendedKeyUsageDescription(List<String> oids) {
return oids.stream()
.map(ExtendedKeyUsage::decodeOid)
.reduce((x, y) -> x + ", " + y)
.map(str -> "extendedKeyUsage [" + str + "]");
}

private static void addSessionDescription(SSLSession session, StringBuilder message) {
String cipherSuite = Optional.ofNullable(session)
.map(SSLSession::getCipherSuite)
.orElse("<unknown cipherSuite>");
String protocol = Optional.ofNullable(session)
.map(SSLSession::getProtocol)
.orElse("<unknown protocol>");
message.append("; the session uses cipher suite [")
.append(cipherSuite)
.append("] and protocol [")
.append(protocol)
.append("]");
}
}
Loading

0 comments on commit f4e3f33

Please sign in to comment.