1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.ssl;
18
19 import java.net.Socket;
20 import java.security.cert.CertificateException;
21 import java.security.cert.X509Certificate;
22 import java.util.Collection;
23 import java.util.List;
24 import javax.naming.ldap.LdapName;
25 import javax.naming.ldap.Rdn;
26 import javax.net.ssl.ExtendedSSLSession;
27 import javax.net.ssl.SNIHostName;
28 import javax.net.ssl.SNIServerName;
29 import javax.net.ssl.SSLEngine;
30 import javax.net.ssl.SSLSession;
31 import javax.net.ssl.SSLSocket;
32 import javax.net.ssl.X509ExtendedTrustManager;
33 import javax.net.ssl.X509TrustManager;
34 import javax.security.auth.x500.X500Principal;
35
36
37
38
39
40 final class EnhancingX509ExtendedTrustManager extends X509ExtendedTrustManager {
41
42
43 static final int ALTNAME_DNS = 2;
44 static final int ALTNAME_URI = 6;
45 static final int ALTNAME_IP = 7;
46 private static final String SEPARATOR = ", ";
47
48 private final X509ExtendedTrustManager wrapped;
49
50 EnhancingX509ExtendedTrustManager(X509TrustManager wrapped) {
51 this.wrapped = (X509ExtendedTrustManager) wrapped;
52 }
53
54 @Override
55 public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
56 throws CertificateException {
57 wrapped.checkClientTrusted(chain, authType, socket);
58 }
59
60 @Override
61 public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
62 throws CertificateException {
63 try {
64 wrapped.checkServerTrusted(chain, authType, socket);
65 } catch (CertificateException e) {
66 throwEnhancedCertificateException(e, chain,
67 socket instanceof SSLSocket ? ((SSLSocket) socket).getHandshakeSession() : null);
68 }
69 }
70
71 @Override
72 public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
73 throws CertificateException {
74 wrapped.checkClientTrusted(chain, authType, engine);
75 }
76
77 @Override
78 public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
79 throws CertificateException {
80 try {
81 wrapped.checkServerTrusted(chain, authType, engine);
82 } catch (CertificateException e) {
83 throwEnhancedCertificateException(e, chain, engine != null ? engine.getHandshakeSession() : null);
84 }
85 }
86
87 @Override
88 public void checkClientTrusted(X509Certificate[] chain, String authType)
89 throws CertificateException {
90 wrapped.checkClientTrusted(chain, authType);
91 }
92
93 @Override
94 public void checkServerTrusted(X509Certificate[] chain, String authType)
95 throws CertificateException {
96 try {
97 wrapped.checkServerTrusted(chain, authType);
98 } catch (CertificateException e) {
99 throwEnhancedCertificateException(e, chain, null);
100 }
101 }
102
103 @Override
104 public X509Certificate[] getAcceptedIssuers() {
105 return wrapped.getAcceptedIssuers();
106 }
107
108 private static void throwEnhancedCertificateException(CertificateException e, X509Certificate[] chain,
109 SSLSession session) throws CertificateException {
110
111 String message = e.getMessage();
112 if (message != null &&
113 (message.startsWith("No subject alternative") || message.startsWith("No name matching"))) {
114 StringBuilder sb = new StringBuilder(128);
115 sb.append(message);
116
117 if (message.charAt(message.length() - 1) == '.') {
118 sb.setLength(sb.length() - 1);
119 }
120 if (session != null) {
121 sb.append(" for SNIHostName=").append(getSNIHostName(session))
122 .append(" and peerHost=").append(session.getPeerHost());
123 }
124 sb.append(" in the chain of ").append(chain.length).append(" certificate(s):");
125 for (int i = 0; i < chain.length; i++) {
126 X509Certificate cert = chain[i];
127 Collection<List<?>> collection = cert.getSubjectAlternativeNames();
128 sb.append(' ').append(i + 1).append(". subjectAlternativeNames=[");
129 if (collection != null) {
130 boolean hasNames = false;
131 for (List<?> altNames : collection) {
132 if (altNames.size() < 2) {
133
134 continue;
135 }
136 final int nameType = ((Integer) altNames.get(0)).intValue();
137 if (nameType == ALTNAME_DNS) {
138 sb.append("DNS");
139 } else if (nameType == ALTNAME_IP) {
140 sb.append("IP");
141 } else if (nameType == ALTNAME_URI) {
142
143
144
145 sb.append("URI");
146 } else {
147 continue;
148 }
149 sb.append(':').append((String) altNames.get(1)).append(SEPARATOR);
150 hasNames = true;
151 }
152 if (hasNames) {
153
154 sb.setLength(sb.length() - SEPARATOR.length());
155 }
156 }
157 sb.append("], CN=").append(getCommonName(cert)).append('.');
158 }
159 throw new CertificateException(sb.toString(), e);
160 }
161 throw e;
162 }
163
164 private static String getSNIHostName(SSLSession session) {
165 if (!(session instanceof ExtendedSSLSession)) {
166 return null;
167 }
168 List<SNIServerName> names = ((ExtendedSSLSession) session).getRequestedServerNames();
169 for (SNIServerName sni : names) {
170 if (sni instanceof SNIHostName) {
171 SNIHostName hostName = (SNIHostName) sni;
172 return hostName.getAsciiName();
173 }
174 }
175 return null;
176 }
177
178 private static String getCommonName(X509Certificate cert) {
179 try {
180
181 X500Principal principal = cert.getSubjectX500Principal();
182
183 LdapName ldapName = new LdapName(principal.getName());
184
185 for (Rdn rdn : ldapName.getRdns()) {
186 if (rdn.getType().equalsIgnoreCase("CN")) {
187 return rdn.getValue().toString();
188 }
189 }
190 } catch (Exception ignore) {
191
192 }
193 return "null";
194 }
195 }