Skip to content

Commit e98ae39

Browse files
committed
[GR-32212] Add support for runtime configuration of default TrustStore.
PullRequest: graal/9502
2 parents c6fd250 + 32fcac3 commit e98ae39

File tree

3 files changed

+226
-26
lines changed

3 files changed

+226
-26
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
layout: docs
3+
toc_group: native-image
4+
link_title: Certificate Management in Native Image
5+
permalink: /reference-manual/native-image/CertificateManagement/
6+
---
7+
# Certificate Management in Native Image
8+
9+
Native-image provides multiple ways to specify the certificate file used to
10+
define the default TrustStore. In the following sections we describe the
11+
available buildtime and runtime options. Note the default behavior for
12+
native-image is to capture and use the default TrustStore from the buildtime
13+
host environment.
14+
15+
## Buildtime Options
16+
17+
During the image building process, native-image captures the host environment's
18+
default TrustStore and embeds it into the native image. This TrustStore is
19+
by default created from the root certificate file provided within the JDK, but
20+
can be changed to use a different certificate file by setting the buildtime
21+
system property "javax.net.ssl.trustStore" (see [Properties](Properties.md) for
22+
how to do so).
23+
24+
Since the contents of the buildtime certificate file is embedded into the image
25+
executable, the file itself does not need to present in the target environment.
26+
27+
## Runtime Options
28+
29+
The certificate file can also be changed dynamically at runtime via setting
30+
the "javax.net.ssl.trustStore\*" system properties.
31+
32+
If any of the following system properties are set during image execution,
33+
native-image also requires "javax.net.ssl.trustStore" to be set and for it
34+
to point to an accessible certificate file:
35+
- javax.net.ssl.trustStore
36+
- javax.net.ssl.trustStoreType
37+
- javax.net.ssl.trustStoreProvider
38+
- javax.net.ssl.trustStorePassword
39+
40+
If any of these properties are set and "javax.net.ssl.trustStore" does not point
41+
to an accessible file, then an UnsupportedFeatureError will be thrown.
42+
43+
Note that this behavior is different than OpenJDK. When the
44+
"javax.net.ssl.trustStore" system property is unset/invalid, OpenJDK will
45+
fallback to using a certificate file shipped within the JDK; however, such
46+
files will not be present alongside the image executable and hence cannot be
47+
used as a fallback.
48+
49+
During the execution, it also possible to dynamically change the
50+
"javax.net.ssl.trustStore\*" properties and for the default TrustStore to be
51+
updated accordingly.
52+
53+
Finally, whenever all of the "javax.net.ssl.trustStore\*" system properties
54+
listed above are unset, the default TrustStore will be the one captured during
55+
buildtime, as described in the [prior section](#buildtime-options).
56+
57+
## Untrusted Certificates
58+
59+
During the image building process, a list of untrusted certificates is loaded
60+
from the file <java.home>/lib/security/blacklisted.certs. This file is used
61+
when validating certificates at both buildtime and runtime. In other words,
62+
when a new certificate file is specified at runtime via setting the
63+
"javax.net.ssl.trustStore\*" system properties, the new certificates will still
64+
be checked against the <java.home>/lib/security/blacklisted.certs loaded at
65+
image buildtime.

docs/security/security-guide.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ This can either result in sensitive data ending up in the snapshot or fixing ini
109109

110110
Developers can request static initializers that process sensitive information to be instead executed at runtime by either specifying the `--initialize-at-run-time` CLI parameter when building a native image, or making use of the `RuntimeClassInitialization` API.
111111

112+
Native-image provides multiple ways to specify the certificate file used to
113+
define the default TrustStore. While the default behavior for native-image is
114+
to capture and use the default TrustStore from the buildtime host environment,
115+
this can be changed at runtime by setting the "javax.net.ssl.trustStore\*"
116+
system properties. Please see the
117+
[documentation](/reference-manual/native-image/CertificateManagement/) for more
118+
details.
119+
120+
112121
In addition, developers can run the native image builder in a dedicated environment, such as a container, that does not contain any sensitive information in the first place.
113122

114123
### Serialization in Native Image

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_sun_security_ssl_TrustStoreManager.java

Lines changed: 152 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27+
import java.io.File;
2728
import java.security.KeyStore;
2829
import java.security.cert.X509Certificate;
2930
import java.util.Set;
@@ -32,25 +33,34 @@
3233
import org.graalvm.nativeimage.hosted.Feature;
3334
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
3435

36+
import com.oracle.svm.core.annotate.Alias;
3537
import com.oracle.svm.core.annotate.AutomaticFeature;
3638
import com.oracle.svm.core.annotate.Delete;
39+
import com.oracle.svm.core.annotate.RecomputeFieldValue;
3740
import com.oracle.svm.core.annotate.Substitute;
3841
import com.oracle.svm.core.annotate.TargetClass;
3942
import com.oracle.svm.core.util.VMError;
4043
import com.oracle.svm.util.ReflectionUtil;
4144

45+
// Checkstyle: stop
46+
import sun.security.ssl.SSLLogger;
47+
// Checkstyle: resume
48+
4249
/**
43-
* Native image uses the principle of "immutable security" for the root certificates: They are fixed
44-
* at image build time based on the the certificate configuration used for the image generator. This
45-
* avoids shipping a `cacerts` file or requiring to set a system property to set up root
46-
* certificates that are provided by the OS where the image runs.
50+
* Root certificates in native image are fixed/embedded into the image, at image build time, based
51+
* on the certificate configuration used for the image generator. This avoids the need to ship a
52+
* `cacerts` file alongside the image executable.
4753
*
48-
* As a consequence, system properties such as `javax.net.ssl.trustStore` do not have an effect at
49-
* run time. They need to be provided at image build time.
54+
* <p>
55+
* Users are also allowed to override the embedded root certificate at run time by setting the
56+
* `javax.net.ssl.trustStore*` system properties. For more details about both buildtime and runtime
57+
* certificate management, please refer to <a href=
58+
* "https://www.graalvm.org/reference-manual/native-image/CertificateManagement/">CertificateManagement.md</a>.
5059
*
51-
* The implementation "freezes" the return values of TrustStoreManager managers by invoking them at
52-
* image build time (using reflection because the class is non-public) and returning the frozen
53-
* values using a substitution.
60+
* <p>
61+
* For embedding the build time root certificates, the implementation "freezes" the return values of
62+
* TrustStoreManager managers by invoking them at image build time (using reflection because the
63+
* class is non-public) and returning the frozen values using a substitution.
5464
*/
5565
@AutomaticFeature
5666
final class TrustStoreManagerFeature implements Feature {
@@ -73,48 +83,164 @@ public void afterRegistration(AfterRegistrationAccess access) {
7383
/*
7484
* The class initializer of UntrustedCertificates loads the file
7585
* lib/security/blacklisted.certs, so this class must be initialized at image build time.
76-
* This is the default anyway for code JDK classes, but since this this class is relevant
77-
* for security we spell it out explicitly.
86+
* This is the default anyway for code JDK classes, but since this class is relevant for
87+
* security we spell it out explicitly.
88+
*
89+
* Note when a runtime certificate file is specified, we still honor/use the build time
90+
* lib/security/blacklisted.certs file
7891
*/
7992
RuntimeClassInitialization.initializeAtBuildTime(sun.security.util.UntrustedCertificates.class);
80-
RuntimeClassInitialization.initializeAtBuildTime(org.jcp.xml.dsig.internal.dom.XMLDSigRI.class);
8193
}
8294
}
8395

8496
final class TrustStoreManagerSupport {
85-
final Set<X509Certificate> trustedCerts;
86-
final KeyStore trustedKeyStore;
8797

88-
TrustStoreManagerSupport(Set<X509Certificate> trustedCerts, KeyStore trustedKeyStore) {
89-
this.trustedCerts = trustedCerts;
90-
this.trustedKeyStore = trustedKeyStore;
98+
final Set<X509Certificate> buildtimeTrustedCerts;
99+
final KeyStore buildtimeTrustedKeyStore;
100+
101+
TrustStoreManagerSupport(Set<X509Certificate> buildtimeTrustedCerts, KeyStore buildtimeTrustedKeyStore) {
102+
this.buildtimeTrustedCerts = buildtimeTrustedCerts;
103+
this.buildtimeTrustedKeyStore = buildtimeTrustedKeyStore;
91104
}
105+
106+
/**
107+
* This method creates a TrustStoreDescriptor if any of the "javax.net.ssl.trustStore*"
108+
* properties are set, or otherwise returns null.
109+
*/
110+
static Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor getRuntimeTrustStoreDescriptor() {
111+
/* First read current system properties. */
112+
String storePropName = System.getProperty("javax.net.ssl.trustStore");
113+
String storePropType = System.getProperty("javax.net.ssl.trustStoreType");
114+
String storePropProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
115+
String storePropPassword = System.getProperty("javax.net.ssl.trustStorePassword");
116+
117+
/*
118+
* Check if any of the properties are set. If not, then should not attempt to create a trust
119+
* store descriptor.
120+
*/
121+
if (storePropName == null && storePropType == null && storePropProvider == null && storePropPassword == null) {
122+
return null;
123+
}
124+
125+
if (storePropName == null) {
126+
throw VMError.unsupportedFeature(
127+
"System property javax.net.ssl.trustStore must be also set if any of javax.net.ssl.trustStore(Type|Provider|Password) are set." +
128+
"See https://www.graalvm.org/reference-manual/native-image/CertificateManagement#runtime-options for more details about runtime certificate management.");
129+
}
130+
131+
/* Setting remaining properties to defaults if unset. */
132+
if (storePropType == null) {
133+
storePropType = KeyStore.getDefaultType();
134+
}
135+
if (storePropProvider == null) {
136+
storePropProvider = "";
137+
}
138+
if (storePropPassword == null) {
139+
storePropPassword = "";
140+
}
141+
142+
/* Creating TrustStoreDescriptor. */
143+
Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor descriptor = createTrustStoreDescriptor(storePropName, storePropType, storePropProvider,
144+
storePropPassword);
145+
146+
/*
147+
* Checking if TrustStoreDescriptor was able to find a valid trust store.
148+
*/
149+
if (descriptor == null) {
150+
throw VMError.unsupportedFeature("Inaccessible trust store: " + storePropName +
151+
" See https://www.graalvm.org/reference-manual/native-image/CertificateManagement#runtime-options for more details about runtime certificate management.");
152+
}
153+
154+
return descriptor;
155+
}
156+
157+
/**
158+
* Creates a new TrustStoreDescriptor object.
159+
*
160+
* @return A new TrustStoreDescriptor or {@code null} if an appropriate descriptor for the
161+
* provided parameters could not be found.
162+
*/
163+
private static Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor createTrustStoreDescriptor(String storePropName, String storePropType, String storePropProvider,
164+
String storePropPassword) {
165+
/* This code is largely taken from the JDK. */
166+
String temporaryName = "";
167+
File temporaryFile = null;
168+
long temporaryTime = 0L;
169+
if (!"NONE".equals(storePropName)) {
170+
File f = new File(storePropName);
171+
if (f.isFile() && f.canRead()) {
172+
temporaryName = storePropName;
173+
temporaryFile = f;
174+
temporaryTime = f.lastModified();
175+
} else {
176+
// The file is inaccessible.
177+
if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
178+
SSLLogger.fine("Inaccessible trust store: " + storePropName);
179+
}
180+
181+
return null;
182+
}
183+
} else {
184+
temporaryName = storePropName;
185+
}
186+
187+
return new Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor(
188+
temporaryName, storePropType, storePropProvider,
189+
storePropPassword, temporaryFile, temporaryTime);
190+
}
191+
92192
}
93193

94194
@TargetClass(className = TrustStoreManagerFeature.TRUST_STORE_MANAGER_CLASS_NAME)
95195
final class Target_sun_security_ssl_TrustStoreManager {
196+
/*
197+
* This singleton object caches the last retrieved trusted KeyStore and set of trusted
198+
* certificates.
199+
*/
200+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClassName = TrustStoreManagerFeature.TRUST_STORE_MANAGER_CLASS_NAME +
201+
"$TrustAnchorManager") private static Target_sun_security_ssl_TrustStoreManager_TrustAnchorManager tam;
96202

97203
@Substitute
98204
private static Set<X509Certificate> getTrustedCerts() throws Exception {
99-
return ImageSingletons.lookup(TrustStoreManagerSupport.class).trustedCerts;
205+
Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor runtimeDescriptor = TrustStoreManagerSupport.getRuntimeTrustStoreDescriptor();
206+
if (runtimeDescriptor == null) {
207+
return ImageSingletons.lookup(TrustStoreManagerSupport.class).buildtimeTrustedCerts;
208+
}
209+
return tam.getTrustedCerts(runtimeDescriptor);
100210
}
101211

102212
@Substitute
103213
private static KeyStore getTrustedKeyStore() throws Exception {
104-
return ImageSingletons.lookup(TrustStoreManagerSupport.class).trustedKeyStore;
214+
Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor runtimeDescriptor = TrustStoreManagerSupport.getRuntimeTrustStoreDescriptor();
215+
if (runtimeDescriptor == null) {
216+
return ImageSingletons.lookup(TrustStoreManagerSupport.class).buildtimeTrustedKeyStore;
217+
}
218+
return tam.getKeyStore(runtimeDescriptor);
105219
}
106220
}
107221

108-
/*
109-
* The internal classes to describe and load root certificates must not be reachable at run time.
110-
*/
111-
112-
@Delete
113222
@TargetClass(className = TrustStoreManagerFeature.TRUST_STORE_MANAGER_CLASS_NAME, innerClass = "TrustStoreDescriptor")
114223
final class Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor {
224+
@Delete private static String defaultStorePath;
225+
@Delete private static String defaultStore;
226+
@Delete private static String jsseDefaultStore;
227+
228+
@Delete
229+
static native Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor createInstance();
230+
231+
@Alias
232+
@SuppressWarnings("unused")
233+
Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor(String storeName, String storeType, String storeProvider, String storePassword, File storeFile, long lastModified) {
234+
throw VMError.shouldNotReachHere("This is an alias to the original constructor in the target class, so this code is unreachable");
235+
}
115236
}
116237

117-
@Delete
118238
@TargetClass(className = TrustStoreManagerFeature.TRUST_STORE_MANAGER_CLASS_NAME, innerClass = "TrustAnchorManager")
119239
final class Target_sun_security_ssl_TrustStoreManager_TrustAnchorManager {
240+
241+
@Alias
242+
native Set<X509Certificate> getTrustedCerts(Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor descriptor) throws Exception;
243+
244+
@Alias
245+
native KeyStore getKeyStore(Target_sun_security_ssl_TrustStoreManager_TrustStoreDescriptor descriptor) throws Exception;
120246
}

0 commit comments

Comments
 (0)