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.internal.EmptyArrays;
22 import io.netty.util.concurrent.FastThreadLocal;
23 import io.netty.util.internal.StringUtil;
24
25 import javax.net.ssl.ManagerFactoryParameters;
26 import javax.net.ssl.TrustManager;
27 import javax.net.ssl.TrustManagerFactory;
28 import javax.net.ssl.X509TrustManager;
29 import java.security.KeyStore;
30 import java.security.MessageDigest;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.cert.CertificateEncodingException;
33 import java.security.cert.CertificateException;
34 import java.security.cert.X509Certificate;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.regex.Pattern;
39
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 public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFactory {
74
75 private static final Pattern FINGERPRINT_PATTERN = Pattern.compile("^[0-9a-fA-F:]+$");
76 private static final Pattern FINGERPRINT_STRIP_PATTERN = Pattern.compile(":");
77 private static final int SHA1_BYTE_LEN = 20;
78 private static final int SHA1_HEX_LEN = SHA1_BYTE_LEN * 2;
79
80 private static final FastThreadLocal<MessageDigest> tlmd = new FastThreadLocal<MessageDigest>() {
81 @Override
82 protected MessageDigest initialValue() {
83 try {
84 return MessageDigest.getInstance("SHA1");
85 } catch (NoSuchAlgorithmException e) {
86
87 throw new Error(e);
88 }
89 }
90 };
91
92 private final TrustManager tm = new X509TrustManager() {
93
94 @Override
95 public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException {
96 checkTrusted("client", chain);
97 }
98
99 @Override
100 public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException {
101 checkTrusted("server", chain);
102 }
103
104 private void checkTrusted(String type, X509Certificate[] chain) throws CertificateException {
105 X509Certificate cert = chain[0];
106 byte[] fingerprint = fingerprint(cert);
107 boolean found = false;
108 for (byte[] allowedFingerprint: fingerprints) {
109 if (Arrays.equals(fingerprint, allowedFingerprint)) {
110 found = true;
111 break;
112 }
113 }
114
115 if (!found) {
116 throw new CertificateException(
117 type + " certificate with unknown fingerprint: " + cert.getSubjectDN());
118 }
119 }
120
121 private byte[] fingerprint(X509Certificate cert) throws CertificateEncodingException {
122 MessageDigest md = tlmd.get();
123 md.reset();
124 return md.digest(cert.getEncoded());
125 }
126
127 @Override
128 public X509Certificate[] getAcceptedIssuers() {
129 return EmptyArrays.EMPTY_X509_CERTIFICATES;
130 }
131 };
132
133 private final byte[][] fingerprints;
134
135
136
137
138
139
140 public FingerprintTrustManagerFactory(Iterable<String> fingerprints) {
141 this(toFingerprintArray(fingerprints));
142 }
143
144
145
146
147
148
149 public FingerprintTrustManagerFactory(String... fingerprints) {
150 this(toFingerprintArray(Arrays.asList(fingerprints)));
151 }
152
153
154
155
156
157
158 public FingerprintTrustManagerFactory(byte[]... fingerprints) {
159 if (fingerprints == null) {
160 throw new NullPointerException("fingerprints");
161 }
162
163 List<byte[]> list = new ArrayList<byte[]>(fingerprints.length);
164 for (byte[] f: fingerprints) {
165 if (f == null) {
166 break;
167 }
168 if (f.length != SHA1_BYTE_LEN) {
169 throw new IllegalArgumentException("malformed fingerprint: " +
170 ByteBufUtil.hexDump(Unpooled.wrappedBuffer(f)) + " (expected: SHA1)");
171 }
172 list.add(f.clone());
173 }
174
175 this.fingerprints = list.toArray(new byte[list.size()][]);
176 }
177
178 private static byte[][] toFingerprintArray(Iterable<String> fingerprints) {
179 if (fingerprints == null) {
180 throw new NullPointerException("fingerprints");
181 }
182
183 List<byte[]> list = new ArrayList<byte[]>();
184 for (String f: fingerprints) {
185 if (f == null) {
186 break;
187 }
188
189 if (!FINGERPRINT_PATTERN.matcher(f).matches()) {
190 throw new IllegalArgumentException("malformed fingerprint: " + f);
191 }
192 f = FINGERPRINT_STRIP_PATTERN.matcher(f).replaceAll("");
193 if (f.length() != SHA1_HEX_LEN) {
194 throw new IllegalArgumentException("malformed fingerprint: " + f + " (expected: SHA1)");
195 }
196
197 list.add(StringUtil.decodeHexDump(f));
198 }
199
200 return list.toArray(new byte[list.size()][]);
201 }
202
203 @Override
204 protected void engineInit(KeyStore keyStore) throws Exception { }
205
206 @Override
207 protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { }
208
209 @Override
210 protected TrustManager[] engineGetTrustManagers() {
211 return new TrustManager[] { tm };
212 }
213 }