1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.jboss.netty.handler.ssl.util;
18
19 import org.jboss.netty.buffer.ChannelBuffers;
20 import org.jboss.netty.util.internal.EmptyArrays;
21
22 import javax.net.ssl.ManagerFactoryParameters;
23 import javax.net.ssl.TrustManager;
24 import javax.net.ssl.TrustManagerFactory;
25 import javax.net.ssl.X509TrustManager;
26 import java.security.KeyStore;
27 import java.security.MessageDigest;
28 import java.security.NoSuchAlgorithmException;
29 import java.security.cert.CertificateEncodingException;
30 import java.security.cert.CertificateException;
31 import java.security.cert.X509Certificate;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.regex.Pattern;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFactory {
63
64 private static final Pattern FINGERPRINT_PATTERN = Pattern.compile("^[0-9a-fA-F:]+$");
65 private static final Pattern FINGERPRINT_STRIP_PATTERN = Pattern.compile(":");
66 private static final int SHA1_BYTE_LEN = 20;
67 private static final int SHA1_HEX_LEN = SHA1_BYTE_LEN * 2;
68
69 private static final ThreadLocal<MessageDigest> tlmd = new ThreadLocal<MessageDigest>() {
70 @Override
71 protected MessageDigest initialValue() {
72 try {
73 return MessageDigest.getInstance("SHA1");
74 } catch (NoSuchAlgorithmException e) {
75
76 throw new Error(e);
77 }
78 }
79 };
80
81 private final TrustManager tm = new X509TrustManager() {
82
83 public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException {
84 checkTrusted("client", chain);
85 }
86
87 public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException {
88 checkTrusted("server", chain);
89 }
90
91 private void checkTrusted(String type, X509Certificate[] chain) throws CertificateException {
92 X509Certificate cert = chain[0];
93 byte[] fingerprint = fingerprint(cert);
94 boolean found = false;
95 for (byte[] allowedFingerprint: fingerprints) {
96 if (Arrays.equals(fingerprint, allowedFingerprint)) {
97 found = true;
98 break;
99 }
100 }
101
102 if (!found) {
103 throw new CertificateException(
104 type + " certificate with unknown fingerprint: " + cert.getSubjectDN());
105 }
106 }
107
108 private byte[] fingerprint(X509Certificate cert) throws CertificateEncodingException {
109 MessageDigest md = tlmd.get();
110 md.reset();
111 return md.digest(cert.getEncoded());
112 }
113
114 public X509Certificate[] getAcceptedIssuers() {
115 return EmptyArrays.EMPTY_X509_CERTIFICATES;
116 }
117 };
118
119 private final byte[][] fingerprints;
120
121
122
123
124
125
126 public FingerprintTrustManagerFactory(Iterable<String> fingerprints) {
127 this(toFingerprintArray(fingerprints));
128 }
129
130
131
132
133
134
135 public FingerprintTrustManagerFactory(String... fingerprints) {
136 this(toFingerprintArray(Arrays.asList(fingerprints)));
137 }
138
139
140
141
142
143
144 public FingerprintTrustManagerFactory(byte[]... fingerprints) {
145 if (fingerprints == null) {
146 throw new NullPointerException("fingerprints");
147 }
148
149 List<byte[]> list = new ArrayList<byte[]>();
150 for (byte[] f: fingerprints) {
151 if (f == null) {
152 break;
153 }
154 if (f.length != SHA1_BYTE_LEN) {
155 throw new IllegalArgumentException("malformed fingerprint: " +
156 ChannelBuffers.hexDump(ChannelBuffers.wrappedBuffer(f)) + " (expected: SHA1)");
157 }
158 list.add(f.clone());
159 }
160
161 this.fingerprints = list.toArray(new byte[list.size()][]);
162 }
163
164 private static byte[][] toFingerprintArray(Iterable<String> fingerprints) {
165 if (fingerprints == null) {
166 throw new NullPointerException("fingerprints");
167 }
168
169 List<byte[]> list = new ArrayList<byte[]>();
170 for (String f: fingerprints) {
171 if (f == null) {
172 break;
173 }
174
175 if (!FINGERPRINT_PATTERN.matcher(f).matches()) {
176 throw new IllegalArgumentException("malformed fingerprint: " + f);
177 }
178 f = FINGERPRINT_STRIP_PATTERN.matcher(f).replaceAll("");
179 if (f.length() != SHA1_HEX_LEN) {
180 throw new IllegalArgumentException("malformed fingerprint: " + f + " (expected: SHA1)");
181 }
182
183 byte[] farr = new byte[SHA1_BYTE_LEN];
184 for (int i = 0; i < farr.length; i ++) {
185 int strIdx = i << 1;
186 farr[i] = (byte) Integer.parseInt(f.substring(strIdx, strIdx + 2), 16);
187 }
188 }
189
190 return list.toArray(new byte[list.size()][]);
191 }
192
193 @Override
194 protected void engineInit(KeyStore keyStore) throws Exception { }
195
196 @Override
197 protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { }
198
199 @Override
200 protected TrustManager[] engineGetTrustManagers() {
201 return new TrustManager[] { tm };
202 }
203 }