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