-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Add KeyUsage, ExtendedKeyUsage, CipherSuite & Protocol to SSL diagnos… #65634
Conversation
💚 CLA has been signed |
Hi @tvernum , I signed the Contributor Agreement successfully, but that check is failing on my PR. Any thoughts on what I could do to verify the signing? |
@sindhusp The issue seems to be that the email address you used in your git commit is not the same email address as you used to sign the CLA. Specifically your CLA has a gmail address, but your commit has a I think what you need to do is make sure your GitHub profile has both your |
Pinging @elastic/es-security (Team:Security) |
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.
Thanks for your contribution, I have some suggested changes.
|
||
private static String keyUsageDescription(X509Certificate certificate) { | ||
return Optional.ofNullable(certificate.getKeyUsage()) | ||
.map(keyUsage -> "keyUsage [" + Arrays.toString(keyUsage) + "]") |
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.
getKeyUsage()
returns boolean[]
, but we need to decode it into the actual usage types (see the Javadoc for getKeyUsage for a description of each index)
Output like
keyUsage [true,true,false,false,true,false,true,false,false]
isn't explanatory enough for this diagnostic message.
@@ -178,7 +179,13 @@ public static String getTrustDiagnosticFailure(X509Certificate[] chain, PeerType | |||
.append(" provided a certificate with subject name [") | |||
.append(peerCert.getSubjectX500Principal().getName()) | |||
.append("] and ") |
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.
.append("] and ") | |
.append("], ") |
@@ -178,7 +179,13 @@ public static String getTrustDiagnosticFailure(X509Certificate[] chain, PeerType | |||
.append(" provided a certificate with subject name [") | |||
.append(peerCert.getSubjectX500Principal().getName()) | |||
.append("] and ") | |||
.append(fingerprintDescription(peerCert)); | |||
.append(fingerprintDescription(peerCert)) | |||
.append(" and ") |
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.
.append(" and ") | |
.append(", ") |
} | ||
|
||
private static String generateExtendedKeyUsageDescription(List<String> list) { | ||
return list.stream() |
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.
getExtendedKeyUsage()
returns a List of OIDs.
For the standard OIDs (defined here https://tools.ietf.org/html/rfc5280#page-44), we need to decode those OIDs into useful names.
Non standard OIDs should be left in their dotted notation.
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 supports the cipher suite [") |
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.
For brevity we can omit articles from the diagnostic
message.append("; the session supports the cipher suite [") | |
message.append("; the session supports cipher suite [") |
.append("] and ") | ||
.append("the protocol [") |
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.
.append("] and ") | |
.append("the protocol [") | |
.append("] and protocol [") |
@@ -63,7 +63,8 @@ public void testDiagnosticMessageWhenServerProvidesAFullCertChainThatIsTrusted() | |||
final String message = SslDiagnostics.getTrustDiagnosticFailure(chain, SslDiagnostics.PeerType.SERVER, session, | |||
"xpack.http.ssl", trustIssuers); | |||
assertThat(message, Matchers.equalTo("failed to establish trust with server at [192.168.1.1];" + | |||
" the server provided a certificate with subject name [CN=cert1] and fingerprint [3bebe388a66362784afd6c51a9000961a4e10050];" + | |||
" the server provided a certificate with subject name [CN=cert1] and fingerprint [3bebe388a66362784afd6c51a9000961a4e10050] and no keyUsage and no extendedKeyUsage;" + | |||
" the session supports the cipher suite [<unknown cipherSuite>] and the protocol [<unknown protocol>];" + |
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.
We need some of the tests to have real values for all of these.
As it is we're only testing the "no value" cases, which is the least important case.
That will require regenerating some of the certs (and updating the fingerprints) to have key usage properties.
(see libs/ssl-config/src/test/resources/certs/README.txt)
and/or changing mockCertificateWithIssuer
and session
to provide mock values.
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.
I've edited the mockCertificateWithIssuer
and session
methods to provide mock values. I attempted to regenerate the cert yesterday without much success, so went with the mocking. Let me know if this works.
3d85623
to
88ecbd0
Compare
@tvernum thanks, my CLA problem is sorted, I've amended my commit to point to my gmail ID. |
* Fix formatting * Add descriptive keyUsage, extendedKeyUsage msgs * Mock cert's keyUsage, extendedKeyUsage * Mock session's cipherSuite, protocol
d9f814e
to
f6af049
Compare
@tvernum, I have addressed your comments. Please let me know if you have more suggestions. |
return "no keyUsage"; | ||
} | ||
|
||
final String[] keyUsageGlossary = {"digitalSignature", "nonRepudiation", "keyEncipherment", |
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.
Should I use an enum here too like I've done with ExtendedKeyUsage
, for uniformity? I could define the index as the Enum field.
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.
I think either is fine.
I do like the consistency of having an enum for each, but it would require care so that the ordinals matched correctly.
I think my personal preference would be to just move this array to be a constant that is declared in the same part of the file as ExtendedKeyUsage
A little nudge for review @tvernum :) |
@tvernum bump for review.. Is it okay to remind this way? Is there someone else I can loop in for this PR? |
He @sindhusp, Pinging here is fine but we're just a bit short of time at the moment and this PR has had to take a back seat. I hope to have time next week. |
Hi @sindhusp Sorry for taking so long on this - can you let me know if you're still interested in working on this? If you don't have time, then someone from Elastic can finish it. |
|
||
public static String decodeOid(String oid) { | ||
for (ExtendedKeyUsage e : values()) { | ||
if (e.oid != null && e.oid.equals(oid)) { |
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.
e.oid
can never be null, because this is an enum with fixed (non-null) values.
I think it's fine to skip the null check here.
If you'd prefer to be safe, you could use this.oid = Objects.requireNonNull(oid);
in the constructor.
return "no keyUsage"; | ||
} | ||
|
||
final String[] keyUsageGlossary = {"digitalSignature", "nonRepudiation", "keyEncipherment", |
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.
I think either is fine.
I do like the consistency of having an enum for each, but it would require care so that the ordinals matched correctly.
I think my personal preference would be to just move this array to be a constant that is declared in the same part of the file as ExtendedKeyUsage
List<String> keyUsageDescription = new ArrayList<>(); | ||
IntStream.range(0, keyUsage.length).forEach(i -> { | ||
if (keyUsage[i]) { | ||
keyUsageDescription.add(keyUsageGlossary[i]); |
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.
For safety we should check that i < keyUsageGlossary.length
List<String> keyUsageDescription = new ArrayList<>(); | ||
IntStream.range(0, keyUsage.length).forEach(i -> { | ||
if (keyUsage[i]) { | ||
keyUsageDescription.add(keyUsageGlossary[i]); | ||
} | ||
}); | ||
return keyUsageDescription.stream() | ||
.reduce((a, b) -> a + ", " + b) |
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.
The style use have used here is fine, but I would have written it like this:
List<String> keyUsageDescription = new ArrayList<>(); | |
IntStream.range(0, keyUsage.length).forEach(i -> { | |
if (keyUsage[i]) { | |
keyUsageDescription.add(keyUsageGlossary[i]); | |
} | |
}); | |
return keyUsageDescription.stream() | |
.reduce((a, b) -> a + ", " + b) | |
String keyUsageDescription = IntStream.range(0, keyUsage.length) | |
.filter(i -> keyUsage[i]) | |
.map(i -> i < keyUsageGlossary.length ? keyUsageGlossary[i] : "#" + i) | |
.collect(Collectors.joining(", ")); |
And then tested whether that was empty or not.
libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslDiagnostics.java
Show resolved
Hide resolved
String protocol = Optional.ofNullable(session) | ||
.map(SSLSession::getProtocol) | ||
.orElse("<unknown protocol>"); | ||
message.append("; the session supports cipher suite [") |
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.
message.append("; the session supports cipher suite [") | |
message.append("; the session uses cipher suite [") |
Strictly, by the time we get to a session being established it's not really "supports", it's "has selected for use"
@tvernum thanks for the comments, I didn't see it earlier. I shall address them today. |
@elasticmachine test this please 🙏 |
@elasticmachine test this please 🙏 |
@jkakavas I made some tweaks to this PR - can you give it a quick look? |
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.
LGTM, double checked key usage and extended key usage in relevant RFCs and messaging looks fine to me
…stics 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> Backport of: elastic#65634
* master: (868 commits) Query API key - Rest spec and yaml tests (elastic#76238) Delay shard reassignment from nodes which are known to be restarting (elastic#75606) Reenable bwc tests for elastic#76475 (elastic#76576) Set version to 7.15 in BWC code (elastic#76577) Don't remove warning headers on all failure (elastic#76434) Disable bwc tests for elastic#76475 (elastic#76541) Re-enable bwc tests (elastic#76567) Keep track of data recovered from snapshots in RecoveryState (elastic#76499) [Transform] Align transform checkpoint range with date_histogram interval for better performance (elastic#74004) EQL: Remove "wildcard" function (elastic#76099) Fix 'accept' and 'content_type' fields for search_mvt API Add persistent licensed feature tracking (elastic#76476) Add system data streams to feature state snapshots (elastic#75902) fix the error message for instance methods that don't exist (elastic#76512) ILM: Add validation of the number_of_shards parameter in Shrink Action of ILM (elastic#74219) remove dashboard only reserved role (elastic#76507) Fix Stack Overflow in UnassignedInfo in Corner Case (elastic#76480) Add (Extended)KeyUsage KeyUsage, CipherSuite & Protocol to SSL diagnostics (elastic#65634) Add recovery from snapshot to tests (elastic#76535) Reenable BwC Tests after elastic#76532 (elastic#76534) ...
Closes #63784