summaryrefslogtreecommitdiffstats
path: root/security-utils
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-11-27 15:00:26 +0100
committerBjørn Christian Seime <bjorncs@oath.com>2018-11-27 16:07:12 +0100
commitc564cfd01c942faa0968f544e76ca305bde3fefc (patch)
tree3383d37108e29fc9a415e8e36aa22f18ac053447 /security-utils
parent28db4abed54ea0fdf7b06203e47f310df2680ee2 (diff)
Add a X509ExtendedTrustManager based on PeerAuthorizer
Diffstat (limited to 'security-utils')
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java124
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManagersFactory.java27
2 files changed, 151 insertions, 0 deletions
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
new file mode 100644
index 00000000000..95d590b2e56
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManager.java
@@ -0,0 +1,124 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security.tls.authz;
+
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.security.tls.policy.AuthorizedPeers;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.net.Socket;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.logging.Logger;
+
+/**
+ * A {@link X509ExtendedTrustManager} that performs additional certificate verification through {@link PeerAuthorizer}.
+ *
+ * @author bjorncs
+ */
+// TODO Propagate verification results
+// Note: Implementation assumes that provided X509ExtendedTrustManager will throw IllegalArgumentException when chain is empty or null
+public class PeerAuthorizerTrustManager extends X509ExtendedTrustManager {
+
+ private static final Logger log = Logger.getLogger(PeerAuthorizerTrustManager.class.getName());
+
+ public enum Mode { DRY_RUN, ENFORCE }
+
+ private final PeerAuthorizer authorizer;
+ private final X509ExtendedTrustManager defaultTrustManager;
+ private final Mode mode;
+
+ public PeerAuthorizerTrustManager(AuthorizedPeers authorizedPeers, Mode mode, X509ExtendedTrustManager defaultTrustManager) {
+ this.authorizer = new PeerAuthorizer(authorizedPeers);
+ this.mode = mode;
+ this.defaultTrustManager = defaultTrustManager;
+ }
+
+ public static TrustManager[] wrapTrustManagersFromKeystore(AuthorizedPeers authorizedPeers, Mode mode, KeyStore keystore) throws GeneralSecurityException {
+ TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(keystore);
+ return wrapTrustManagers(authorizedPeers, mode, factory.getTrustManagers());
+ }
+
+ public static TrustManager[] wrapTrustManagers(AuthorizedPeers authorizedPeers, Mode mode, TrustManager[] managers) {
+ TrustManager[] wrappedManagers = new TrustManager[managers.length];
+ for (int i = 0; i < managers.length; i++) {
+ if (managers[i] instanceof X509ExtendedTrustManager) {
+ wrappedManagers[i] = new PeerAuthorizerTrustManager(authorizedPeers, mode, (X509ExtendedTrustManager) managers[i]);
+ } else {
+ wrappedManagers[i] = managers[i];
+ }
+ }
+ return wrappedManagers;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ defaultTrustManager.checkClientTrusted(chain, authType);
+ authorizePeer(chain[0], authType, true);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ defaultTrustManager.checkServerTrusted(chain, authType);
+ authorizePeer(chain[0], authType, false);
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
+ defaultTrustManager.checkClientTrusted(chain, authType, socket);
+ authorizePeer(chain[0], authType, true);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
+ defaultTrustManager.checkServerTrusted(chain, authType, socket);
+ authorizePeer(chain[0], authType, false);
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
+ defaultTrustManager.checkClientTrusted(chain, authType, sslEngine);
+ authorizePeer(chain[0], authType, true);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine sslEngine) throws CertificateException {
+ defaultTrustManager.checkServerTrusted(chain, authType, sslEngine);
+ authorizePeer(chain[0], authType, false);
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return defaultTrustManager.getAcceptedIssuers();
+ }
+
+ private void authorizePeer(X509Certificate certificate, String authType, boolean isVerifyingClient) throws CertificateException {
+ log.fine(() -> "Verifying certificate: " + createInfoString(certificate, authType, isVerifyingClient));
+ AuthorizationResult result = authorizer.authorizePeer(certificate);
+ if (result.succeeded()) {
+ log.fine(() -> String.format("Verification result: %s", result));
+ } else {
+ String errorMessage = "Authorization failed: " + createInfoString(certificate, authType, isVerifyingClient);
+ switch (mode) {
+ case ENFORCE:
+ throw new CertificateException(errorMessage);
+ case DRY_RUN:
+ log.warning(errorMessage);
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ private static String createInfoString(X509Certificate certificate, String authType, boolean isVerifyingClient) {
+ return String.format("DN='%s', SANs=%s, authType='%s', isVerifyingClient='%b'",
+ certificate.getSubjectX500Principal(), X509CertificateUtils.getSubjectAlternativeNames(certificate), authType, isVerifyingClient);
+ }
+
+}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManagersFactory.java b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManagersFactory.java
new file mode 100644
index 00000000000..0bb99aea886
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/tls/authz/PeerAuthorizerTrustManagersFactory.java
@@ -0,0 +1,27 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security.tls.authz;
+
+import com.yahoo.security.SslContextBuilder;
+import com.yahoo.security.tls.policy.AuthorizedPeers;
+
+import javax.net.ssl.TrustManager;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+/**
+ * @author bjorncs
+ */
+public class PeerAuthorizerTrustManagersFactory implements SslContextBuilder.TrustManagersFactory {
+ private final AuthorizedPeers authorizedPeers;
+ private PeerAuthorizerTrustManager.Mode mode;
+
+ public PeerAuthorizerTrustManagersFactory(AuthorizedPeers authorizedPeers, PeerAuthorizerTrustManager.Mode mode) {
+ this.authorizedPeers = authorizedPeers;
+ this.mode = mode;
+ }
+
+ @Override
+ public TrustManager[] createTrustManagers(KeyStore truststore) throws GeneralSecurityException {
+ return PeerAuthorizerTrustManager.wrapTrustManagersFromKeystore(authorizedPeers, mode, truststore);
+ }
+}