|
15 | 15 | */ |
16 | 16 | package io.aklivity.zilla.runtime.binding.tls.internal.config; |
17 | 17 |
|
| 18 | +import java.security.cert.Certificate; |
| 19 | +import java.security.cert.CertificateException; |
| 20 | +import java.security.cert.X509Certificate; |
18 | 21 | import java.util.Arrays; |
19 | 22 | import java.util.regex.Matcher; |
20 | 23 | import java.util.regex.Pattern; |
21 | 24 |
|
| 25 | +import javax.net.ssl.TrustManager; |
| 26 | +import javax.net.ssl.TrustManagerFactory; |
| 27 | +import javax.net.ssl.X509TrustManager; |
| 28 | + |
22 | 29 | import org.agrona.collections.IntHashSet; |
23 | 30 |
|
24 | 31 | import io.aklivity.zilla.runtime.binding.tls.config.TlsConditionConfig; |
| 32 | +import io.aklivity.zilla.runtime.binding.tls.config.TlsMutualConfig; |
25 | 33 |
|
26 | 34 | public final class TlsConditionMatcher |
27 | 35 | { |
28 | 36 | public final Matcher authorityMatch; |
29 | 37 | public final Matcher alpnMatch; |
30 | 38 | public final IntHashSet ports; |
| 39 | + public final TlsMutualConfig mutual; |
| 40 | + public final TrustManagerFactory trustFactory; |
31 | 41 |
|
32 | 42 | public TlsConditionMatcher( |
33 | | - TlsConditionConfig condition) |
| 43 | + TlsConditionConfig condition, |
| 44 | + TrustManagerFactory trustFactory) |
34 | 45 | { |
35 | 46 | this.authorityMatch = condition.authority != null ? asMatcher(condition.authority) : null; |
36 | 47 | this.alpnMatch = condition.alpn != null ? asMatcher(condition.alpn) : null; |
37 | 48 | this.ports = condition.ports != null ? asIntHashSet(condition.ports) : null; |
| 49 | + // `trust` implies `mutual: required` |
| 50 | + this.mutual = condition.mutual != null |
| 51 | + ? condition.mutual |
| 52 | + : condition.trust != null ? TlsMutualConfig.REQUIRED : null; |
| 53 | + this.trustFactory = trustFactory; |
38 | 54 | } |
39 | 55 |
|
40 | 56 | public boolean matches( |
| 57 | + String authority, |
| 58 | + String alpn, |
| 59 | + int port, |
| 60 | + Certificate[] clientCerts) |
| 61 | + { |
| 62 | + return matchesAuthority(authority) && |
| 63 | + matchesAlpn(alpn) && |
| 64 | + matchesPort(port) && |
| 65 | + matchesMutual(clientCerts) && |
| 66 | + matchesTrust(clientCerts); |
| 67 | + } |
| 68 | + |
| 69 | + public boolean matchesIgnoringCert( |
41 | 70 | String authority, |
42 | 71 | String alpn, |
43 | 72 | int port) |
@@ -71,6 +100,72 @@ private boolean matchesPort( |
71 | 100 | return ports == null || ports.contains(port); |
72 | 101 | } |
73 | 102 |
|
| 103 | + private boolean matchesMutual( |
| 104 | + Certificate[] clientCerts) |
| 105 | + { |
| 106 | + boolean matches = true; |
| 107 | + boolean present = clientCerts != null && clientCerts.length > 0; |
| 108 | + if (mutual == TlsMutualConfig.REQUIRED) |
| 109 | + { |
| 110 | + matches = present; |
| 111 | + } |
| 112 | + else if (mutual == TlsMutualConfig.NONE) |
| 113 | + { |
| 114 | + matches = !present; |
| 115 | + } |
| 116 | + return matches; |
| 117 | + } |
| 118 | + |
| 119 | + private boolean matchesTrust( |
| 120 | + Certificate[] clientCerts) |
| 121 | + { |
| 122 | + boolean matches = true; |
| 123 | + if (trustFactory != null) |
| 124 | + { |
| 125 | + matches = false; |
| 126 | + if (clientCerts != null && clientCerts.length > 0) |
| 127 | + { |
| 128 | + X509Certificate[] chain = asX509Chain(clientCerts); |
| 129 | + if (chain != null) |
| 130 | + { |
| 131 | + String authType = chain[0].getPublicKey().getAlgorithm(); |
| 132 | + for (TrustManager tm : trustFactory.getTrustManagers()) |
| 133 | + { |
| 134 | + if (tm instanceof X509TrustManager x509tm) |
| 135 | + { |
| 136 | + try |
| 137 | + { |
| 138 | + x509tm.checkClientTrusted(chain, authType); |
| 139 | + matches = true; |
| 140 | + break; |
| 141 | + } |
| 142 | + catch (CertificateException ex) |
| 143 | + { |
| 144 | + // not trusted by this trust manager |
| 145 | + } |
| 146 | + } |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | + } |
| 151 | + return matches; |
| 152 | + } |
| 153 | + |
| 154 | + private static X509Certificate[] asX509Chain( |
| 155 | + Certificate[] certs) |
| 156 | + { |
| 157 | + X509Certificate[] chain = new X509Certificate[certs.length]; |
| 158 | + for (int i = 0; i < certs.length; i++) |
| 159 | + { |
| 160 | + if (!(certs[i] instanceof X509Certificate x509)) |
| 161 | + { |
| 162 | + return null; |
| 163 | + } |
| 164 | + chain[i] = x509; |
| 165 | + } |
| 166 | + return chain; |
| 167 | + } |
| 168 | + |
74 | 169 | private static Matcher asMatcher( |
75 | 170 | String wildcard) |
76 | 171 | { |
|
0 commit comments