Skip to content

Commit 7296040

Browse files
authored
TLS Revocation config (#8425)
TLS Revocation config Signed-off-by: David Kral <[email protected]>
1 parent 083c1ba commit 7296040

File tree

18 files changed

+740
-4
lines changed

18 files changed

+740
-4
lines changed

common/tls/src/main/java/io/helidon/common/tls/ConfiguredTlsManager.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,28 @@
1818

1919
import java.io.IOException;
2020
import java.security.GeneralSecurityException;
21+
import java.security.InvalidAlgorithmParameterException;
2122
import java.security.KeyStore;
2223
import java.security.KeyStoreException;
2324
import java.security.NoSuchAlgorithmException;
2425
import java.security.NoSuchProviderException;
2526
import java.security.PrivateKey;
2627
import java.security.SecureRandom;
2728
import java.security.UnrecoverableKeyException;
29+
import java.security.cert.CertPathBuilder;
2830
import java.security.cert.Certificate;
2931
import java.security.cert.CertificateException;
32+
import java.security.cert.PKIXBuilderParameters;
33+
import java.security.cert.PKIXRevocationChecker;
34+
import java.security.cert.X509CertSelector;
3035
import java.security.cert.X509Certificate;
3136
import java.util.Base64;
37+
import java.util.HashSet;
3238
import java.util.Objects;
3339
import java.util.Optional;
40+
import java.util.Set;
3441

42+
import javax.net.ssl.CertPathTrustManagerParameters;
3543
import javax.net.ssl.KeyManager;
3644
import javax.net.ssl.KeyManagerFactory;
3745
import javax.net.ssl.SSLContext;
@@ -291,6 +299,48 @@ protected TrustManagerFactory createTmf(TlsConfig tlsConfig) {
291299
}
292300
}
293301

302+
/**
303+
* Perform initialization of the {@link TrustManagerFactory} based on the provided TLS configuration.
304+
*
305+
* @param tmf trust manager factory to be initialized
306+
* @param keyStore keystore
307+
* @param tlsConfig tls configuration
308+
*/
309+
protected void initializeTmf(TrustManagerFactory tmf, KeyStore keyStore, TlsConfig tlsConfig) {
310+
try {
311+
if (tlsConfig.revocation().isPresent()) {
312+
RevocationConfig revocationConfig = tlsConfig.revocation().get();
313+
if (revocationConfig.enabled()) {
314+
CertPathBuilder cpb = null;
315+
cpb = CertPathBuilder.getInstance("PKIX");
316+
PKIXRevocationChecker rc = (PKIXRevocationChecker) cpb.getRevocationChecker();
317+
Set<PKIXRevocationChecker.Option> options = new HashSet<>();
318+
if (revocationConfig.preferCrlOverOcsp()) {
319+
options.add(PKIXRevocationChecker.Option.PREFER_CRLS);
320+
}
321+
if (revocationConfig.checkOnlyEndEntity()) {
322+
options.add(PKIXRevocationChecker.Option.ONLY_END_ENTITY);
323+
}
324+
if (!revocationConfig.fallbackEnabled()) {
325+
options.add(PKIXRevocationChecker.Option.NO_FALLBACK);
326+
}
327+
if (revocationConfig.softFailEnabled()) {
328+
options.add(PKIXRevocationChecker.Option.SOFT_FAIL);
329+
}
330+
rc.setOptions(options);
331+
revocationConfig.ocspResponderUri().ifPresent(rc::setOcspResponder);
332+
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(keyStore, new X509CertSelector());
333+
pkixParams.addCertPathChecker(rc);
334+
tmf.init(new CertPathTrustManagerParameters(pkixParams));
335+
return;
336+
}
337+
}
338+
tmf.init(keyStore);
339+
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | KeyStoreException e) {
340+
throw new IllegalArgumentException("Failed to initialize TrustManagerFactory", e);
341+
}
342+
}
343+
294344
/**
295345
* Creates a trust all trust manager factory.
296346
*
@@ -310,7 +360,7 @@ private TrustManagerFactory initTmf(TlsConfig tlsConfig) throws KeyStoreExceptio
310360
i++;
311361
}
312362
TrustManagerFactory tmf = createTmf(tlsConfig);
313-
tmf.init(ks);
363+
initializeTmf(tmf, ks, tlsConfig);
314364
return tmf;
315365
}
316366

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.common.tls;
18+
19+
import java.net.URI;
20+
import java.util.Optional;
21+
22+
import io.helidon.builder.api.Option;
23+
import io.helidon.builder.api.Prototype;
24+
25+
/**
26+
* Certificate revocation configuration.
27+
* This configuration determines whether client certificate validation should include checking if
28+
* it is still considered valid by the certificate authority.
29+
* <br>
30+
* Types of certificate validation checks:
31+
* <ul>
32+
* <li>CRL - shortcut name for Certificate Revocation List. It is a list of certificates that have
33+
* been revoked by a certificate authority before their expiration date</li>
34+
* <li>OCSP - shortcut name for Online Certificate Status Protocol. It is a real-time protocol used
35+
* to check the status of a certificate, providing immediate verification of its validity</li>
36+
* </ul>
37+
*/
38+
@Prototype.Blueprint
39+
@Prototype.Configured
40+
interface RevocationConfigBlueprint {
41+
42+
/**
43+
* Flag indicating whether this revocation config is enabled.
44+
*
45+
* @return enabled flag
46+
*/
47+
@Option.Configured
48+
@Option.DefaultBoolean(false)
49+
boolean enabled();
50+
51+
/**
52+
* Prefer CRL over OCSP.
53+
* Default value is {@code false}. OCSP is preferred over the CRL by default.
54+
*
55+
* @return whether to prefer CRL over OCSP
56+
*/
57+
@Option.Configured
58+
@Option.DefaultBoolean(false)
59+
boolean preferCrlOverOcsp();
60+
61+
/**
62+
* Only check the revocation status of end-entity certificates.
63+
* Default value is {@code false}.
64+
*
65+
* @return whether to check only end-entity certificates
66+
*/
67+
@Option.Configured
68+
@Option.DefaultBoolean(false)
69+
boolean checkOnlyEndEntity();
70+
71+
/**
72+
* Enable fallback to the less preferred checking option.
73+
* <br>
74+
* If the primary method for revocation checking fails to verify the revocation status of a certificate
75+
* (such as using a CRL or OCSP), the checker will attempt alternative methods. This option ensures
76+
* whether revocation checking is performed strictly according to the specified method, or should fallback
77+
* to the one less preferred. OCSP is preferred over the CRL by default.
78+
*
79+
* @return whether to allow fallback to the less preferred checking option
80+
*/
81+
@Option.Configured
82+
@Option.DefaultBoolean(true)
83+
boolean fallbackEnabled();
84+
85+
/**
86+
* Allow revocation check to succeed if the revocation status cannot be
87+
* determined for one of the following reasons:
88+
* <ul>
89+
* <li>The CRL or OCSP response cannot be obtained because of a
90+
* network error.
91+
* <li>The OCSP responder returns one of the following errors
92+
* specified in section 2.3 of RFC 2560: internalError or tryLater.
93+
* </ul>
94+
*
95+
* @return whether soft fail is enabled
96+
*/
97+
@Option.Configured
98+
@Option.DefaultBoolean(false)
99+
boolean softFailEnabled();
100+
101+
/**
102+
* The URI that identifies the location of the OCSP responder. This
103+
* overrides the {@code ocsp.responderURL} security property and any
104+
* responder specified in a certificate's Authority Information Access
105+
* Extension, as defined in RFC 5280.
106+
*
107+
* @return OCSP responder URI
108+
*/
109+
@Option.Configured
110+
Optional<URI> ocspResponderUri();
111+
112+
}

common/tls/src/main/java/io/helidon/common/tls/TlsConfigBlueprint.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,12 @@ static List<X509Certificate> createTrust(Keys config) {
291291
@Option.Configured
292292
Optional<String> internalKeystoreProvider();
293293

294+
/**
295+
* Certificate revocation check configuration.
296+
*
297+
* @return certificate revocation configuration
298+
*/
299+
@Option.Configured
300+
Optional<RevocationConfig> revocation();
301+
294302
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
///////////////////////////////////////////////////////////////////////////////
2+
3+
Copyright (c) 2024 Oracle and/or its affiliates.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
17+
///////////////////////////////////////////////////////////////////////////////
18+
19+
ifndef::rootdir[:rootdir: {docdir}/..]
20+
:description: Configuration of io.helidon.common.tls.RevocationConfig
21+
:keywords: helidon, config, io.helidon.common.tls.RevocationConfig
22+
:basic-table-intro: The table below lists the configuration keys that configure io.helidon.common.tls.RevocationConfig
23+
include::{rootdir}/includes/attributes.adoc[]
24+
25+
= RevocationConfig (common.tls) Configuration
26+
27+
// tag::config[]
28+
29+
30+
Type: link:{javadoc-base-url}/io.helidon.common.tls/io/helidon/common/tls/RevocationConfig.html[io.helidon.common.tls.RevocationConfig]
31+
32+
33+
34+
35+
== Configuration options
36+
37+
38+
39+
.Optional configuration options
40+
[cols="3,3a,2,5a"]
41+
42+
|===
43+
|key |type |default value |description
44+
45+
|`check-only-end-entity` |boolean |`false` |Only check the revocation status of end-entity certificates.
46+
Default value is `false`.
47+
48+
@return whether to check only end-entity certificates
49+
|`enabled` |boolean |`false` |Flag indicating whether this revocation config is enabled.
50+
51+
@return enabled flag
52+
|`fallback-enabled` |boolean |`true` |Enable fallback to the less preferred checking option.
53+
54+
@return whether to allow fallback to the less preferred checking option
55+
|`ocsp-responder-uri` |URI |{nbsp} |The URI that identifies the location of the OCSP responder. This
56+
overrides the `ocsp.responderURL` security property and any
57+
responder specified in a certificate's Authority Information Access
58+
Extension, as defined in RFC 5280.
59+
60+
@return OCSP responder URI
61+
|`prefer-crl-over-ocsp` |boolean |`false` |Prefer CRL over OCSP.
62+
Default value is `false`.
63+
64+
@return whether to prefer CRL over OCSP
65+
|`soft-fail-enabled` |boolean |`false` |Allow revocation check to succeed if the revocation status cannot be
66+
determined for one of the following reasons:
67+
68+
- The CRL or OCSP response cannot be obtained because of a
69+
network error.
70+
71+
- The OCSP responder returns one of the following errors
72+
specified in section 2.3 of RFC 2560: internalError or tryLater.
73+
74+
@return whether soft fail is enabled
75+
76+
|===
77+
78+
// end::config[]

docs/src/main/asciidoc/config/io_helidon_common_tls_Tls.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///////////////////////////////////////////////////////////////////////////////
22

3-
Copyright (c) 2023 Oracle and/or its affiliates.
3+
Copyright (c) 2024 Oracle and/or its affiliates.
44

55
Licensed under the Apache License, Version 2.0 (the "License");
66
you may not use this file except in compliance with the License.
@@ -85,6 +85,9 @@ Type: link:{javadoc-base-url}/io.helidon.common.tls/io/helidon/common/tls/Tls.ht
8585
|`provider` |string |{nbsp} |Use explicit provider to obtain an instance of javax.net.ssl.SSLContext.
8686
8787
@return provider to use, defaults to none (only #protocol() is used by default)
88+
|`revocation` |xref:{rootdir}/config/io_helidon_common_tls_RevocationConfig.adoc[RevocationConfig] |{nbsp} |Certificate revocation check configuration.
89+
90+
@return certificate revocation configuration
8891
|`secure-random-algorithm` |string |{nbsp} |Algorithm to use when creating a new secure random.
8992
9093
@return algorithm to use, by default uses java.security.SecureRandom constructor

etc/copyright-exclude.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jaxb.index
3737
.bin
3838
.vm
3939
.sql
40+
.crl
4041
src/main/proto/
4142
src/test/resources/keystore/
4243
etc/javadoc/

integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -170,7 +170,7 @@ boolean loadContext(boolean initialLoad) {
170170
tmf = createTmf(tlsConfig);
171171
KeyStore keyStore = internalKeystore(tlsConfig);
172172
keyStore.setCertificateEntry("trust-ca", ca);
173-
tmf.init(keyStore);
173+
initializeTmf(tmf, keyStore, tlsConfig);
174174
}
175175

176176
Optional<X509KeyManager> keyManager = Arrays.stream(kmf.getKeyManagers())

tests/integration/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<module>security</module>
6464
<module>vault</module>
6565
<module>zipkin-mp-2.2</module>
66+
<module>tls-revocation-config</module>
6667
</modules>
6768

6869
<dependencyManagement>

0 commit comments

Comments
 (0)