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.buffer.ByteBufAllocator;
19 import io.netty.buffer.UnpooledByteBufAllocator;
20 import io.netty.internal.tcnative.SSL;
21 import io.netty.util.IllegalReferenceCountException;
22
23 import javax.net.ssl.SSLException;
24 import javax.net.ssl.X509KeyManager;
25 import java.security.PrivateKey;
26 import java.security.cert.X509Certificate;
27 import java.util.concurrent.atomic.AtomicReference;
28
29 import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO;
30
31
32
33
34 class OpenSslKeyMaterialProvider {
35 private static final MaterialCache SENTINEL_DESTROYED = new MaterialCache(null, null, null);
36
37 private final X509KeyManager keyManager;
38 private final String password;
39 private final AtomicReference<MaterialCache> cache;
40
41 OpenSslKeyMaterialProvider(X509KeyManager keyManager, String password) {
42 this.keyManager = keyManager;
43 this.password = password;
44 cache = new AtomicReference<>();
45 }
46
47 static void validateKeyMaterialSupported(X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
48 boolean allowSignatureFallback)
49 throws SSLException {
50 validateSupported(keyCertChain);
51 validateSupported(key, keyPassword, allowSignatureFallback);
52 }
53
54 private static void validateSupported(PrivateKey key, String password,
55 boolean allowSignatureFallback) throws SSLException {
56 if (key == null) {
57 return;
58 }
59
60
61
62 if (key.getEncoded() == null && allowSignatureFallback) {
63 return;
64 }
65
66 long pkeyBio = 0;
67 long pkey = 0;
68
69 try {
70 pkeyBio = toBIO(UnpooledByteBufAllocator.DEFAULT, key);
71 pkey = SSL.parsePrivateKey(pkeyBio, password);
72 } catch (Exception e) {
73 throw new SSLException("PrivateKey type not supported " + key.getFormat(), e);
74 } finally {
75 SSL.freeBIO(pkeyBio);
76 if (pkey != 0) {
77 SSL.freePrivateKey(pkey);
78 }
79 }
80 }
81
82 private static void validateSupported(X509Certificate[] certificates) throws SSLException {
83 if (certificates == null || certificates.length == 0) {
84 return;
85 }
86
87 long chainBio = 0;
88 long chain = 0;
89 PemEncoded encoded = null;
90 try {
91 encoded = PemX509Certificate.toPEM(UnpooledByteBufAllocator.DEFAULT, true, certificates);
92 chainBio = toBIO(UnpooledByteBufAllocator.DEFAULT, encoded.retain());
93 chain = SSL.parseX509Chain(chainBio);
94 } catch (Exception e) {
95 throw new SSLException("Certificate type not supported", e);
96 } finally {
97 SSL.freeBIO(chainBio);
98 if (chain != 0) {
99 SSL.freeX509Chain(chain);
100 }
101 if (encoded != null) {
102 encoded.release();
103 }
104 }
105 }
106
107
108
109
110 X509KeyManager keyManager() {
111 return keyManager;
112 }
113
114
115
116
117
118 OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
119 X509Certificate[] certificates = keyManager.getCertificateChain(alias);
120 if (certificates == null || certificates.length == 0) {
121 return null;
122 }
123
124 PrivateKey key = keyManager.getPrivateKey(alias);
125 MaterialCache materialCache = cache.get();
126 if (materialCache != null && materialCache != SENTINEL_DESTROYED && materialCache.retain()) {
127 if (materialCache.sameInstances(key, certificates)) {
128 return materialCache.material();
129 } else {
130
131 materialCache.release();
132 }
133 }
134
135 OpenSslKeyMaterial keyMaterial = createKeyMaterial(allocator, certificates, key);
136 materialCache = new MaterialCache(key, certificates, keyMaterial);
137
138
139 materialCache.retain();
140 MaterialCache oldMaterial = cache.getAndSet(materialCache);
141 if (oldMaterial != null) {
142 if (oldMaterial == SENTINEL_DESTROYED) {
143 destroyCache();
144 } else {
145 oldMaterial.release();
146 }
147 }
148
149 return keyMaterial;
150 }
151
152 private OpenSslKeyMaterial createKeyMaterial(
153 ByteBufAllocator allocator, X509Certificate[] certificates, PrivateKey key)
154 throws Exception {
155 PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates);
156 long chainBio = 0;
157 long pkeyBio = 0;
158 long chain = 0;
159 long pkey = 0;
160 try {
161 chainBio = toBIO(allocator, encoded.retain());
162 chain = SSL.parseX509Chain(chainBio);
163
164 OpenSslKeyMaterial keyMaterial;
165 if (key instanceof OpenSslPrivateKey) {
166 keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates);
167 } else {
168 pkeyBio = toBIO(allocator, key);
169 pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password);
170 keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates);
171 }
172
173
174
175 chain = 0;
176 pkey = 0;
177 return keyMaterial;
178 } finally {
179 SSL.freeBIO(chainBio);
180 SSL.freeBIO(pkeyBio);
181 if (chain != 0) {
182 SSL.freeX509Chain(chain);
183 }
184 if (pkey != 0) {
185 SSL.freePrivateKey(pkey);
186 }
187 encoded.release();
188 }
189 }
190
191
192
193
194 void destroy() {
195 destroyCache();
196 }
197
198 private void destroyCache() {
199 MaterialCache oldMaterial;
200 while ((oldMaterial = cache.getAndSet(SENTINEL_DESTROYED)) != SENTINEL_DESTROYED) {
201 if (oldMaterial != null) {
202 oldMaterial.release();
203 }
204 }
205 }
206
207 private static final class MaterialCache {
208 private final PrivateKey key;
209 private final X509Certificate[] certs;
210 private final OpenSslKeyMaterial material;
211
212 private MaterialCache(PrivateKey key, X509Certificate[] certs, OpenSslKeyMaterial material) {
213 this.key = key;
214 this.certs = certs;
215 this.material = material;
216 }
217
218 OpenSslKeyMaterial material() {
219 return material;
220 }
221
222 boolean sameInstances(PrivateKey key, X509Certificate[] certs) {
223 X509Certificate[] existingCerts = this.certs;
224 int length = existingCerts.length;
225 if (this.key != key || length != certs.length) {
226 return false;
227 }
228 for (int i = 0; i < length; i++) {
229 if (certs[i] != existingCerts[i]) {
230 return false;
231 }
232 }
233 return true;
234 }
235
236 boolean retain() {
237 if (material.refCnt() != 0) {
238 try {
239 material.retain();
240 return true;
241 } catch (IllegalReferenceCountException ignore) {
242
243 }
244 }
245 return false;
246 }
247
248 void release() {
249 material.release();
250 }
251 }
252 }