-
Notifications
You must be signed in to change notification settings - Fork 215
/
TrustManagerFactoryFactory.java
181 lines (161 loc) · 7.52 KB
/
TrustManagerFactoryFactory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.connectivity.service.messaging.internal.ssl;
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathBuilder;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.X509CertSelector;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.TrustManagerFactory;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.json.JsonPointer;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
/**
* Creates {@link javax.net.ssl.TrustManagerFactory}s from different sources.
*/
public final class TrustManagerFactoryFactory {
private static final String PKIX = "PKIX";
private static final KeyStore DEFAULT_CA_KEYSTORE = loadDefaultCAKeystore();
private static final String CERTIFICATE_LABEL = "CERTIFICATE";
private static final CertificateFactory X509_CERTIFICATE_FACTORY;
private final KeyStoreFactory keyStoreFactory;
private final ExceptionMapper exceptionMapper;
static {
try {
X509_CERTIFICATE_FACTORY = CertificateFactory.getInstance("X.509");
} catch (final CertificateException e) {
throw new Error("FATAL: failed to load X.509 certificate factory", e);
}
}
TrustManagerFactoryFactory(final ExceptionMapper exceptionMapper) {
this.exceptionMapper = checkNotNull(exceptionMapper, "exceptionMapper");
keyStoreFactory = new KeyStoreFactory(exceptionMapper);
}
public static TrustManagerFactoryFactory getInstance(final DittoHeaders dittoHeaders) {
return new TrustManagerFactoryFactory(ExceptionMapper.forTrustedCertificates(dittoHeaders));
}
public static TrustManagerFactoryFactory getInstance(final ExceptionMapper exceptionMapper) {
return new TrustManagerFactoryFactory(exceptionMapper);
}
public TrustManagerFactory newTrustManagerFactory(@Nullable final String trustedCertificates,
final boolean checkRevocation) {
return handleExceptions(() -> createTrustManagerFactory(trustedCertificates, checkRevocation));
}
public TrustManagerFactory newTrustManagerFactory(final Connection connection, final boolean checkRevocation) {
final String trustedCertificates = connection.getTrustedCertificates().orElse(null);
final TrustManagerFactory factory = newTrustManagerFactory(trustedCertificates, checkRevocation);
if (connection.isValidateCertificates()) {
return factory;
} else {
return AcceptAnyTrustManager.factory(factory);
}
}
public TrustManagerFactory newInsecureTrustManagerFactory() {
return InsecureTrustManagerFactory.INSTANCE;
}
private TrustManagerFactory createTrustManagerFactory(
@Nullable final String trustedCertificates,
final boolean checkForRevocation)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException,
InvalidAlgorithmParameterException {
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PKIX);
if (trustedCertificates != null) {
final KeyStore keystore = keyStoreFactory.newKeystore();
final Collection<? extends Certificate> caCerts;
final byte[] caCertsPem = trustedCertificates.getBytes(StandardCharsets.US_ASCII);
caCerts = X509_CERTIFICATE_FACTORY.generateCertificates(new ByteArrayInputStream(caCertsPem));
long cnt = 0;
for (final Certificate caCert : caCerts) {
keystore.setCertificateEntry("ca-" + cnt++, caCert);
}
trustManagerFactory.init(keystore);
} else {
// standard CAs; add revocation check
final PKIXBuilderParameters parameters =
new PKIXBuilderParameters(DEFAULT_CA_KEYSTORE, new X509CertSelector());
if (checkForRevocation) {
parameters.addCertPathChecker(
(PKIXCertPathChecker) CertPathBuilder.getInstance(PKIX).getRevocationChecker());
} else {
parameters.addCertPathChecker(NoRevocationChecker.getInstance());
}
trustManagerFactory.init(new CertPathTrustManagerParameters(parameters));
}
return trustManagerFactory;
}
private static KeyStore loadDefaultCAKeystore() {
try {
final String javaHome = System.getProperty("java.home");
final String cacerts = javaHome + "/lib/security/cacerts";
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (final FileInputStream cacertsStream = new FileInputStream(cacerts)) {
keystore.load(cacertsStream, "changeit".toCharArray());
}
return keystore;
} catch (final KeyStoreException e) {
throw new Error("FATAL: Cannot create default CA keystore");
} catch (final IOException e) {
throw new Error("FATAL: Cannot read default CA keystore");
} catch (final NoSuchAlgorithmException | CertificateException e) {
throw new Error("FATAL: Cannot load default CA keystore");
}
}
/**
* Handles common ssl exceptions and maps them to Ditto exceptions.
*
* @param supplier the supplier that may throw an exception
* @param <T> the result type
* @return the result if no exception occurred
*/
private <T> T handleExceptions(final ThrowingSupplier<T> supplier) {
try {
return supplier.get();
} catch (final CertificateException e) {
final JsonPointer errorLocation = Connection.JsonFields.TRUSTED_CERTIFICATES.getPointer();
throw exceptionMapper.badFormat(errorLocation, CERTIFICATE_LABEL, "DER")
.cause(e)
.build();
} catch (final KeyStoreException e) {
throw exceptionMapper.fatalError("Engine failed to configure trusted CA certificates")
.cause(e)
.build();
} catch (final NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw exceptionMapper.fatalError("Failed to start TLS engine")
.cause(e)
.build();
}
}
@FunctionalInterface
public interface ThrowingSupplier<T> {
/**
* @return the result.
*/
T get() throws CertificateException, KeyStoreException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException;
}
}