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