From e69c68a2c4b9b8f8d556f376c8f023f602a95eff Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Wed, 13 Jul 2022 17:21:22 +0200 Subject: Include full certificate chain in auth context --- .../security/tls/authz/ConnectionAuthContext.java | 7 ++++++- .../yahoo/security/tls/authz/PeerAuthorizer.java | 12 ++++++++---- .../tls/authz/PeerAuthorizerTrustManager.java | 22 ++++++++++++---------- 3 files changed, 26 insertions(+), 15 deletions(-) (limited to 'security-utils') diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java index a5fb51da763..18f61fc7aa4 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/ConnectionAuthContext.java @@ -10,14 +10,19 @@ import java.util.TreeSet; /** * @author bjorncs */ -public record ConnectionAuthContext(List peerCertificate, +public record ConnectionAuthContext(List peerCertificateChain, CapabilitySet capabilities, SortedSet matchedPolicies) { public ConnectionAuthContext { + if (peerCertificateChain.isEmpty()) throw new IllegalArgumentException("Peer certificate chain is empty"); + peerCertificateChain = List.copyOf(peerCertificateChain); + if (matchedPolicies.isEmpty() && !CapabilitySet.none().equals(capabilities)) throw new AssertionError(); matchedPolicies = new TreeSet<>(matchedPolicies); } public boolean succeeded() { return matchedPolicies.size() > 0; } + public X509Certificate peerCertificate() { return peerCertificateChain.get(0); } + } diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java index 353f704b136..30b6ac3f34b 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizer.java @@ -37,11 +37,15 @@ public class PeerAuthorizer { this.authorizedPeers = authorizedPeers; } - public ConnectionAuthContext authorizePeer(X509Certificate peerCertificate) { + + public ConnectionAuthContext authorizePeer(X509Certificate cert) { return authorizePeer(List.of(cert)); } + + public ConnectionAuthContext authorizePeer(List certChain) { + X509Certificate cert = certChain.get(0); SortedSet matchedPolicies = new TreeSet<>(); Set grantedCapabilities = new HashSet<>(); - String cn = getCommonName(peerCertificate).orElse(null); - List sans = getSubjectAlternativeNames(peerCertificate); + String cn = getCommonName(cert).orElse(null); + List sans = getSubjectAlternativeNames(cert); log.fine(() -> String.format("Subject info from x509 certificate: CN=[%s], 'SAN=%s", cn, sans)); for (PeerPolicy peerPolicy : authorizedPeers.peerPolicies()) { if (matchesPolicy(peerPolicy, cn, sans)) { @@ -49,7 +53,7 @@ public class PeerAuthorizer { grantedCapabilities.add(peerPolicy.capabilities()); } } - return new ConnectionAuthContext(List.of(peerCertificate), CapabilitySet.unionOf(grantedCapabilities), matchedPolicies); + return new ConnectionAuthContext(certChain, CapabilitySet.unionOf(grantedCapabilities), matchedPolicies); } private static boolean matchesPolicy(PeerPolicy peerPolicy, String cn, List sans) { diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java index 925e21c63ff..ea920a90c7b 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java @@ -15,6 +15,7 @@ import java.net.Socket; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Optional; import java.util.logging.Logger; @@ -55,39 +56,39 @@ public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { defaultTrustManager.checkClientTrusted(chain, authType); - authorizePeer(chain[0], authType, true, null); + authorizePeer(chain, authType, true, null); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { defaultTrustManager.checkServerTrusted(chain, authType); - authorizePeer(chain[0], authType, false, null); + authorizePeer(chain, authType, false, null); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { defaultTrustManager.checkClientTrusted(chain, authType, socket); - authorizePeer(chain[0], authType, true, null); + authorizePeer(chain, authType, true, null); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { overrideHostnameVerificationForClient(socket); defaultTrustManager.checkServerTrusted(chain, authType, socket); - authorizePeer(chain[0], authType, false, null); + authorizePeer(chain, authType, false, null); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException { defaultTrustManager.checkClientTrusted(chain, authType, sslEngine); - authorizePeer(chain[0], authType, true, sslEngine); + authorizePeer(chain, authType, true, sslEngine); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException { overrideHostnameVerificationForClient(sslEngine); defaultTrustManager.checkServerTrusted(chain, authType, sslEngine); - authorizePeer(chain[0], authType, false, sslEngine); + authorizePeer(chain, authType, false, sslEngine); } @Override @@ -103,18 +104,19 @@ public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager { .flatMap(session -> Optional.ofNullable((ConnectionAuthContext) session.getValue(HANDSHAKE_SESSION_AUTH_CONTEXT_PROPERTY))); } - private void authorizePeer(X509Certificate certificate, String authType, boolean isVerifyingClient, SSLEngine sslEngine) throws CertificateException { + private void authorizePeer(X509Certificate[] certChain, String authType, boolean isVerifyingClient, SSLEngine sslEngine) throws CertificateException { if (mode == AuthorizationMode.DISABLE) return; - log.fine(() -> "Verifying certificate: " + createInfoString(certificate, authType, isVerifyingClient)); - ConnectionAuthContext result = authorizer.authorizePeer(certificate); + + log.fine(() -> "Verifying certificate: " + createInfoString(certChain[0], authType, isVerifyingClient)); + ConnectionAuthContext result = authorizer.authorizePeer(List.of(certChain)); if (sslEngine != null) { // getHandshakeSession() will never return null in this context sslEngine.getHandshakeSession().putValue(HANDSHAKE_SESSION_AUTH_CONTEXT_PROPERTY, result); } if (result.succeeded()) { log.fine(() -> String.format("Verification result: %s", result)); } else { - String errorMessage = "Authorization failed: " + createInfoString(certificate, authType, isVerifyingClient); + String errorMessage = "Authorization failed: " + createInfoString(certChain[0], authType, isVerifyingClient); log.warning(errorMessage); if (mode == AuthorizationMode.ENFORCE) { throw new CertificateException(errorMessage); -- cgit v1.2.3