1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.ssl;
17
18 import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
19 import static io.netty.util.internal.ObjectUtil.checkNotNull;
20
21 import io.netty.buffer.ByteBufAllocator;
22 import io.netty.buffer.UnpooledByteBufAllocator;
23 import io.netty.internal.tcnative.SSL;
24 import io.netty.util.ReferenceCountUtil;
25 import io.netty.util.internal.ObjectUtil;
26
27 import javax.net.ssl.KeyManager;
28 import javax.net.ssl.KeyManagerFactory;
29 import javax.net.ssl.KeyManagerFactorySpi;
30 import javax.net.ssl.ManagerFactoryParameters;
31 import javax.net.ssl.X509KeyManager;
32 import java.io.File;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.security.InvalidAlgorithmParameterException;
37 import java.security.Key;
38 import java.security.KeyStore;
39 import java.security.KeyStoreException;
40 import java.security.KeyStoreSpi;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.PrivateKey;
43 import java.security.Provider;
44 import java.security.UnrecoverableKeyException;
45 import java.security.cert.Certificate;
46 import java.security.cert.CertificateException;
47 import java.security.cert.X509Certificate;
48 import java.util.Collections;
49 import java.util.Date;
50 import java.util.Enumeration;
51 import java.util.HashMap;
52 import java.util.Map;
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public final class OpenSslX509KeyManagerFactory extends KeyManagerFactory {
67
68 private final OpenSslKeyManagerFactorySpi spi;
69
70 public OpenSslX509KeyManagerFactory() {
71 this(newOpenSslKeyManagerFactorySpi(null));
72 }
73
74 public OpenSslX509KeyManagerFactory(Provider provider) {
75 this(newOpenSslKeyManagerFactorySpi(provider));
76 }
77
78 public OpenSslX509KeyManagerFactory(String algorithm, Provider provider) throws NoSuchAlgorithmException {
79 this(newOpenSslKeyManagerFactorySpi(algorithm, provider));
80 }
81
82 private OpenSslX509KeyManagerFactory(OpenSslKeyManagerFactorySpi spi) {
83 super(spi, spi.kmf.getProvider(), spi.kmf.getAlgorithm());
84 this.spi = spi;
85 }
86
87 private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(Provider provider) {
88 try {
89 return newOpenSslKeyManagerFactorySpi(null, provider);
90 } catch (NoSuchAlgorithmException e) {
91
92 throw new IllegalStateException(e);
93 }
94 }
95
96 private static OpenSslKeyManagerFactorySpi newOpenSslKeyManagerFactorySpi(String algorithm, Provider provider)
97 throws NoSuchAlgorithmException {
98 if (algorithm == null) {
99 algorithm = KeyManagerFactory.getDefaultAlgorithm();
100 }
101 return new OpenSslKeyManagerFactorySpi(
102 provider == null ? KeyManagerFactory.getInstance(algorithm) :
103 KeyManagerFactory.getInstance(algorithm, provider));
104 }
105
106 OpenSslKeyMaterialProvider newProvider() {
107 return spi.newProvider();
108 }
109
110 private static final class OpenSslKeyManagerFactorySpi extends KeyManagerFactorySpi {
111 final KeyManagerFactory kmf;
112 private volatile ProviderFactory providerFactory;
113
114 OpenSslKeyManagerFactorySpi(KeyManagerFactory kmf) {
115 this.kmf = ObjectUtil.checkNotNull(kmf, "kmf");
116 }
117
118 @Override
119 protected synchronized void engineInit(KeyStore keyStore, char[] chars)
120 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
121 if (providerFactory != null) {
122 throw new KeyStoreException("Already initialized");
123 }
124 if (!keyStore.aliases().hasMoreElements()) {
125 throw new KeyStoreException("No aliases found");
126 }
127
128 kmf.init(keyStore, chars);
129 providerFactory = new ProviderFactory(ReferenceCountedOpenSslContext.chooseX509KeyManager(
130 kmf.getKeyManagers()), password(chars), Collections.list(keyStore.aliases()));
131 }
132
133 private static String password(char[] password) {
134 if (password == null || password.length == 0) {
135 return null;
136 }
137 return new String(password);
138 }
139
140 @Override
141 protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
142 throws InvalidAlgorithmParameterException {
143 throw new InvalidAlgorithmParameterException("Not supported");
144 }
145
146 @Override
147 protected KeyManager[] engineGetKeyManagers() {
148 ProviderFactory providerFactory = this.providerFactory;
149 if (providerFactory == null) {
150 throw new IllegalStateException("engineInit(...) not called yet");
151 }
152 return new KeyManager[] { providerFactory.keyManager };
153 }
154
155 OpenSslKeyMaterialProvider newProvider() {
156 ProviderFactory providerFactory = this.providerFactory;
157 if (providerFactory == null) {
158 throw new IllegalStateException("engineInit(...) not called yet");
159 }
160 return providerFactory.newProvider();
161 }
162
163 private static final class ProviderFactory {
164 private final X509KeyManager keyManager;
165 private final String password;
166 private final Iterable<String> aliases;
167
168 ProviderFactory(X509KeyManager keyManager, String password, Iterable<String> aliases) {
169 this.keyManager = keyManager;
170 this.password = password;
171 this.aliases = aliases;
172 }
173
174 OpenSslKeyMaterialProvider newProvider() {
175 return new OpenSslPopulatedKeyMaterialProvider(keyManager,
176 password, aliases);
177 }
178
179
180
181
182
183 private static final class OpenSslPopulatedKeyMaterialProvider extends OpenSslKeyMaterialProvider {
184 private final Map<String, Object> materialMap;
185
186 OpenSslPopulatedKeyMaterialProvider(
187 X509KeyManager keyManager, String password, Iterable<String> aliases) {
188 super(keyManager, password);
189 materialMap = new HashMap<String, Object>();
190 boolean initComplete = false;
191 try {
192 for (String alias: aliases) {
193 if (alias != null && !materialMap.containsKey(alias)) {
194 try {
195 materialMap.put(alias, super.chooseKeyMaterial(
196 UnpooledByteBufAllocator.DEFAULT, alias));
197 } catch (Exception e) {
198
199
200 materialMap.put(alias, e);
201 }
202 }
203 }
204 initComplete = true;
205 } finally {
206 if (!initComplete) {
207 destroy();
208 }
209 }
210 checkNonEmpty(materialMap, "materialMap");
211 }
212
213 @Override
214 OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
215 Object value = materialMap.get(alias);
216 if (value == null) {
217
218 return null;
219 }
220 if (value instanceof OpenSslKeyMaterial) {
221 return ((OpenSslKeyMaterial) value).retain();
222 }
223 throw (Exception) value;
224 }
225
226 @Override
227 void destroy() {
228 try {
229 for (Object material: materialMap.values()) {
230 ReferenceCountUtil.release(material);
231 }
232 materialMap.clear();
233 } finally {
234 super.destroy();
235 }
236 }
237 }
238 }
239 }
240
241
242
243
244
245
246
247 public static OpenSslX509KeyManagerFactory newEngineBased(File certificateChain, String password)
248 throws CertificateException, IOException,
249 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
250 return newEngineBased(SslContext.toX509Certificates(certificateChain), password);
251 }
252
253
254
255
256
257
258
259 public static OpenSslX509KeyManagerFactory newEngineBased(X509Certificate[] certificateChain, String password)
260 throws CertificateException, IOException,
261 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
262 checkNotNull(certificateChain, "certificateChain");
263 KeyStore store = new OpenSslKeyStore(certificateChain.clone(), false);
264 store.load(null, null);
265 OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory();
266 factory.init(store, password == null ? null : password.toCharArray());
267 return factory;
268 }
269
270
271
272
273 public static OpenSslX509KeyManagerFactory newKeyless(File chain)
274 throws CertificateException, IOException,
275 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
276 return newKeyless(SslContext.toX509Certificates(chain));
277 }
278
279
280
281
282 public static OpenSslX509KeyManagerFactory newKeyless(InputStream chain)
283 throws CertificateException, IOException,
284 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
285 return newKeyless(SslContext.toX509Certificates(chain));
286 }
287
288
289
290
291
292 public static OpenSslX509KeyManagerFactory newKeyless(X509Certificate... certificateChain)
293 throws CertificateException, IOException,
294 KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
295 checkNotNull(certificateChain, "certificateChain");
296 KeyStore store = new OpenSslKeyStore(certificateChain.clone(), true);
297 store.load(null, null);
298 OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory();
299 factory.init(store, null);
300 return factory;
301 }
302
303 private static final class OpenSslKeyStore extends KeyStore {
304 private OpenSslKeyStore(final X509Certificate[] certificateChain, final boolean keyless) {
305 super(new KeyStoreSpi() {
306
307 private final Date creationDate = new Date();
308
309 @Override
310 public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyException {
311 if (engineContainsAlias(alias)) {
312 final long privateKeyAddress;
313 if (keyless) {
314 privateKeyAddress = 0;
315 } else {
316 try {
317 privateKeyAddress = SSL.loadPrivateKeyFromEngine(
318 alias, password == null ? null : new String(password));
319 } catch (Exception e) {
320 UnrecoverableKeyException keyException =
321 new UnrecoverableKeyException("Unable to load key from engine");
322 keyException.initCause(e);
323 throw keyException;
324 }
325 }
326 return new OpenSslPrivateKey(privateKeyAddress);
327 }
328 return null;
329 }
330
331 @Override
332 public Certificate[] engineGetCertificateChain(String alias) {
333 return engineContainsAlias(alias)? certificateChain.clone() : null;
334 }
335
336 @Override
337 public Certificate engineGetCertificate(String alias) {
338 return engineContainsAlias(alias)? certificateChain[0] : null;
339 }
340
341 @Override
342 public Date engineGetCreationDate(String alias) {
343 return engineContainsAlias(alias)? creationDate : null;
344 }
345
346 @Override
347 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
348 throws KeyStoreException {
349 throw new KeyStoreException("Not supported");
350 }
351
352 @Override
353 public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException {
354 throw new KeyStoreException("Not supported");
355 }
356
357 @Override
358 public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
359 throw new KeyStoreException("Not supported");
360 }
361
362 @Override
363 public void engineDeleteEntry(String alias) throws KeyStoreException {
364 throw new KeyStoreException("Not supported");
365 }
366
367 @Override
368 public Enumeration<String> engineAliases() {
369 return Collections.enumeration(Collections.singleton(SslContext.ALIAS));
370 }
371
372 @Override
373 public boolean engineContainsAlias(String alias) {
374 return SslContext.ALIAS.equals(alias);
375 }
376
377 @Override
378 public int engineSize() {
379 return 1;
380 }
381
382 @Override
383 public boolean engineIsKeyEntry(String alias) {
384 return engineContainsAlias(alias);
385 }
386
387 @Override
388 public boolean engineIsCertificateEntry(String alias) {
389 return engineContainsAlias(alias);
390 }
391
392 @Override
393 public String engineGetCertificateAlias(Certificate cert) {
394 if (cert instanceof X509Certificate) {
395 for (X509Certificate x509Certificate : certificateChain) {
396 if (x509Certificate.equals(cert)) {
397 return SslContext.ALIAS;
398 }
399 }
400 }
401 return null;
402 }
403
404 @Override
405 public void engineStore(OutputStream stream, char[] password) {
406 throw new UnsupportedOperationException();
407 }
408
409 @Override
410 public void engineLoad(InputStream stream, char[] password) {
411 if (stream != null && password != null) {
412 throw new UnsupportedOperationException();
413 }
414 }
415 }, null, "native");
416
417 OpenSsl.ensureAvailability();
418 }
419 }
420 }