1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.ssl;
17
18 import java.net.Socket;
19 import java.security.cert.Certificate;
20 import java.security.cert.CertificateException;
21 import java.security.cert.X509Certificate;
22 import java.util.Collections;
23 import java.util.Set;
24 import java.util.WeakHashMap;
25 import java.util.concurrent.atomic.AtomicReference;
26 import javax.net.ssl.SSLEngine;
27 import javax.net.ssl.SSLPeerUnverifiedException;
28 import javax.net.ssl.SSLSession;
29 import javax.net.ssl.TrustManager;
30 import javax.net.ssl.X509ExtendedTrustManager;
31
32 final class ResumptionController {
33 private final Set<SSLEngine> confirmedValidations;
34 private final AtomicReference<ResumableX509ExtendedTrustManager> resumableTm;
35
36 ResumptionController() {
37 confirmedValidations = Collections.synchronizedSet(
38 Collections.newSetFromMap(new WeakHashMap<SSLEngine, Boolean>()));
39 resumableTm = new AtomicReference<ResumableX509ExtendedTrustManager>();
40 }
41
42 public TrustManager wrapIfNeeded(TrustManager tm) {
43 if (tm instanceof ResumableX509ExtendedTrustManager) {
44 if (!(tm instanceof X509ExtendedTrustManager)) {
45 throw new IllegalStateException("ResumableX509ExtendedTrustManager implementation must be a " +
46 "subclass of X509ExtendedTrustManager, found: " + (tm == null ? null : tm.getClass()));
47 }
48 if (!resumableTm.compareAndSet(null, (ResumableX509ExtendedTrustManager) tm)) {
49 throw new IllegalStateException(
50 "Only one ResumableX509ExtendedTrustManager can be configured for resumed sessions");
51 }
52 return new X509ExtendedWrapTrustManager((X509ExtendedTrustManager) tm, confirmedValidations);
53 }
54 return tm;
55 }
56
57 public void remove(SSLEngine engine) {
58 if (resumableTm.get() != null) {
59 confirmedValidations.remove(unwrapEngine(engine));
60 }
61 }
62
63 public boolean validateResumeIfNeeded(SSLEngine engine)
64 throws CertificateException, SSLPeerUnverifiedException {
65 ResumableX509ExtendedTrustManager tm;
66 SSLSession session = engine.getSession();
67 boolean valid = session.isValid();
68
69
70
71
72
73
74
75
76 if (valid && (engine.getUseClientMode() || engine.getNeedClientAuth() || engine.getWantClientAuth()) &&
77 (tm = resumableTm.get()) != null) {
78
79 engine = unwrapEngine(engine);
80
81 if (!confirmedValidations.remove(engine)) {
82 Certificate[] peerCertificates;
83 try {
84 peerCertificates = session.getPeerCertificates();
85 } catch (SSLPeerUnverifiedException e) {
86 if (engine.getUseClientMode() || engine.getNeedClientAuth()) {
87
88 throw e;
89 }
90
91 return false;
92 }
93
94
95 if (engine.getUseClientMode()) {
96
97 tm.resumeServerTrusted(chainOf(peerCertificates), engine);
98 } else {
99
100 tm.resumeClientTrusted(chainOf(peerCertificates), engine);
101 }
102 return true;
103 }
104 }
105 return false;
106 }
107
108 private static SSLEngine unwrapEngine(SSLEngine engine) {
109 if (engine instanceof JdkSslEngine) {
110 return ((JdkSslEngine) engine).getWrappedEngine();
111 }
112 return engine;
113 }
114
115 private static X509Certificate[] chainOf(Certificate[] peerCertificates) {
116 if (peerCertificates instanceof X509Certificate[]) {
117
118 return (X509Certificate[]) peerCertificates;
119 }
120 X509Certificate[] chain = new X509Certificate[peerCertificates.length];
121 for (int i = 0; i < peerCertificates.length; i++) {
122 Certificate cert = peerCertificates[i];
123 if (cert instanceof X509Certificate || cert == null) {
124 chain[i] = (X509Certificate) cert;
125 } else {
126 throw new IllegalArgumentException("Only X509Certificates are supported, found: " + cert.getClass());
127 }
128 }
129 return chain;
130 }
131
132 private static final class X509ExtendedWrapTrustManager extends X509ExtendedTrustManager {
133 private final X509ExtendedTrustManager trustManager;
134 private final Set<SSLEngine> confirmedValidations;
135
136 X509ExtendedWrapTrustManager(X509ExtendedTrustManager trustManager, Set<SSLEngine> confirmedValidations) {
137 this.trustManager = trustManager;
138 this.confirmedValidations = confirmedValidations;
139 }
140
141 private static void unsupported() throws CertificateException {
142 throw new CertificateException(
143 new UnsupportedOperationException("Resumable trust managers require the SSLEngine parameter"));
144 }
145
146 @Override
147 public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
148 throws CertificateException {
149 unsupported();
150 }
151
152 @Override
153 public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
154 throws CertificateException {
155 unsupported();
156 }
157
158 @Override
159 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
160 unsupported();
161 }
162
163 @Override
164 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
165 unsupported();
166 }
167
168 @Override
169 public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
170 throws CertificateException {
171 trustManager.checkClientTrusted(chain, authType, engine);
172 confirmedValidations.add(engine);
173 }
174
175 @Override
176 public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
177 throws CertificateException {
178 trustManager.checkServerTrusted(chain, authType, engine);
179 confirmedValidations.add(engine);
180 }
181
182 @Override
183 public X509Certificate[] getAcceptedIssuers() {
184 return trustManager.getAcceptedIssuers();
185 }
186 }
187 }