1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.ssl.util;
18
19 import io.netty.buffer.ByteBufUtil;
20 import io.netty.buffer.Unpooled;
21 import io.netty.util.concurrent.FastThreadLocal;
22 import io.netty.util.internal.EmptyArrays;
23 import io.netty.util.internal.ObjectUtil;
24 import io.netty.util.internal.StringUtil;
25
26 import javax.net.ssl.ManagerFactoryParameters;
27 import javax.net.ssl.TrustManager;
28 import javax.net.ssl.TrustManagerFactory;
29 import javax.net.ssl.X509TrustManager;
30 import java.security.KeyStore;
31 import java.security.MessageDigest;
32 import java.security.NoSuchAlgorithmException;
33 import java.security.cert.CertificateEncodingException;
34 import java.security.cert.CertificateException;
35 import java.security.cert.X509Certificate;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.regex.Pattern;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFactory {
83
84 private static final Pattern FINGERPRINT_PATTERN = Pattern.compile("^[0-9a-fA-F:]+$");
85 private static final Pattern FINGERPRINT_STRIP_PATTERN = Pattern.compile(":");
86
87
88
89
90
91
92
93 public static FingerprintTrustManagerFactoryBuilder builder(String algorithm) {
94 return new FingerprintTrustManagerFactoryBuilder(algorithm);
95 }
96
97 private final FastThreadLocal<MessageDigest> tlmd;
98
99 private final TrustManager tm = new X509TrustManager() {
100
101 @Override
102 public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException {
103 checkTrusted("client", chain);
104 }
105
106 @Override
107 public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException {
108 checkTrusted("server", chain);
109 }
110
111 private void checkTrusted(String type, X509Certificate[] chain) throws CertificateException {
112 X509Certificate cert = chain[0];
113 byte[] fingerprint = fingerprint(cert);
114 boolean found = false;
115 for (byte[] allowedFingerprint: fingerprints) {
116 if (Arrays.equals(fingerprint, allowedFingerprint)) {
117 found = true;
118 break;
119 }
120 }
121
122 if (!found) {
123 throw new CertificateException(
124 type + " certificate with unknown fingerprint: " + cert.getSubjectDN());
125 }
126 }
127
128 private byte[] fingerprint(X509Certificate cert) throws CertificateEncodingException {
129 MessageDigest md = tlmd.get();
130 md.reset();
131 return md.digest(cert.getEncoded());
132 }
133
134 @Override
135 public X509Certificate[] getAcceptedIssuers() {
136 return EmptyArrays.EMPTY_X509_CERTIFICATES;
137 }
138 };
139
140 private final byte[][] fingerprints;
141
142
143
144
145
146
147
148
149
150
151 @Deprecated
152 public FingerprintTrustManagerFactory(Iterable<String> fingerprints) {
153 this("SHA1", toFingerprintArray(fingerprints));
154 }
155
156
157
158
159
160
161
162
163
164
165 @Deprecated
166 public FingerprintTrustManagerFactory(String... fingerprints) {
167 this("SHA1", toFingerprintArray(Arrays.asList(fingerprints)));
168 }
169
170
171
172
173
174
175
176
177
178
179 @Deprecated
180 public FingerprintTrustManagerFactory(byte[]... fingerprints) {
181 this("SHA1", fingerprints);
182 }
183
184
185
186
187
188
189
190 FingerprintTrustManagerFactory(final String algorithm, byte[][] fingerprints) {
191 ObjectUtil.checkNotNull(algorithm, "algorithm");
192 ObjectUtil.checkNotNull(fingerprints, "fingerprints");
193
194 if (fingerprints.length == 0) {
195 throw new IllegalArgumentException("No fingerprints provided");
196 }
197
198
199 final MessageDigest md;
200 try {
201 md = MessageDigest.getInstance(algorithm);
202 } catch (NoSuchAlgorithmException e) {
203 throw new IllegalArgumentException(
204 String.format("Unsupported hash algorithm: %s", algorithm), e);
205 }
206
207 int hashLength = md.getDigestLength();
208 List<byte[]> list = new ArrayList<byte[]>(fingerprints.length);
209 for (byte[] f: fingerprints) {
210 if (f == null) {
211 break;
212 }
213 if (f.length != hashLength) {
214 throw new IllegalArgumentException(
215 String.format("malformed fingerprint (length is %d but expected %d): %s",
216 f.length, hashLength, ByteBufUtil.hexDump(Unpooled.wrappedBuffer(f))));
217 }
218 list.add(f.clone());
219 }
220
221 this.tlmd = new FastThreadLocal<MessageDigest>() {
222
223 @Override
224 protected MessageDigest initialValue() {
225 try {
226 return MessageDigest.getInstance(algorithm);
227 } catch (NoSuchAlgorithmException e) {
228 throw new IllegalArgumentException(
229 String.format("Unsupported hash algorithm: %s", algorithm), e);
230 }
231 }
232 };
233
234 this.fingerprints = list.toArray(new byte[0][]);
235 }
236
237 static byte[][] toFingerprintArray(Iterable<String> fingerprints) {
238 ObjectUtil.checkNotNull(fingerprints, "fingerprints");
239
240 List<byte[]> list = new ArrayList<byte[]>();
241 for (String f: fingerprints) {
242 if (f == null) {
243 break;
244 }
245
246 if (!FINGERPRINT_PATTERN.matcher(f).matches()) {
247 throw new IllegalArgumentException("malformed fingerprint: " + f);
248 }
249 f = FINGERPRINT_STRIP_PATTERN.matcher(f).replaceAll("");
250
251 list.add(StringUtil.decodeHexDump(f));
252 }
253
254 return list.toArray(new byte[0][]);
255 }
256
257 @Override
258 protected void engineInit(KeyStore keyStore) throws Exception { }
259
260 @Override
261 protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { }
262
263 @Override
264 protected TrustManager[] engineGetTrustManagers() {
265 return new TrustManager[] { tm };
266 }
267 }