Skip to content

Commit dbecb80

Browse files
Initialize security providers at run time.
1 parent 5eb3d21 commit dbecb80

File tree

7 files changed

+379
-268
lines changed

7 files changed

+379
-268
lines changed

substratevm/mx.substratevm/suite.py

+3
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,12 @@
287287
"sun.reflect.generics.reflectiveObjects",
288288
"sun.reflect.generics.repository",
289289
"sun.reflect.generics.tree",
290+
"sun.security.rsa",
290291
"sun.security.jca",
291292
"sun.security.ssl",
292293
"sun.security.util",
294+
"sun.security.provider",
295+
"com.sun.crypto.provider",
293296
"sun.text.spi",
294297
"sun.util",
295298
"sun.util.locale",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package com.oracle.svm.core.jdk;
27+
28+
import java.lang.reflect.Constructor;
29+
import java.lang.reflect.InvocationTargetException;
30+
import java.security.Provider;
31+
import java.util.Collections;
32+
import java.util.HashMap;
33+
import java.util.HashSet;
34+
import java.util.List;
35+
import java.util.Map;
36+
import java.util.Set;
37+
38+
import org.graalvm.nativeimage.ImageSingletons;
39+
import org.graalvm.nativeimage.Platform;
40+
import org.graalvm.nativeimage.Platforms;
41+
42+
import com.oracle.svm.core.util.VMError;
43+
44+
import jdk.graal.compiler.api.replacements.Fold;
45+
46+
/**
47+
* The class that holds various build-time and runtime structures necessary for security providers.
48+
*/
49+
public final class SecurityProvidersSupport {
50+
/**
51+
* A set of providers to be loaded using the service-loading technique at runtime, but not
52+
* discoverable at build-time when processing services in the feature (see
53+
* ServiceLoaderFeature#handleServiceClassIsReachable). This occurs when the user does not
54+
* explicitly request a provider, but the provider is discovered via static analysis from a
55+
* JCA-compliant security service used by the user's code (see
56+
* SecurityServicesFeature#registerServiceReachabilityHandlers).
57+
*/
58+
@Platforms(Platform.HOSTED_ONLY.class)//
59+
private final Set<String> markedAsNotLoaded = Collections.synchronizedSet(new HashSet<>());
60+
61+
/** Set of fully qualified provider names, required for runtime resource access. */
62+
private final Set<String> userRequestedSecurityProviders = Collections.synchronizedSet(new HashSet<>());
63+
64+
/**
65+
* A map of providers, identified by their names (see {@link Provider#getName()}), and the
66+
* results of their verification (see javax.crypto.JceSecurity#getVerificationResult). This
67+
* structure is used instead of the (see javax.crypto.JceSecurity#verifyingProviders) map to
68+
* avoid keeping provider objects in the image heap.
69+
*/
70+
private final Map<String, Object> verifiedSecurityProviders = Collections.synchronizedMap(new HashMap<>());
71+
72+
private Constructor<?> sunECConstructor;
73+
74+
@Platforms(Platform.HOSTED_ONLY.class)
75+
public SecurityProvidersSupport(List<String> userRequestedSecurityProviders) {
76+
this.userRequestedSecurityProviders.addAll(userRequestedSecurityProviders);
77+
}
78+
79+
@Fold
80+
public static SecurityProvidersSupport singleton() {
81+
return ImageSingletons.lookup(SecurityProvidersSupport.class);
82+
}
83+
84+
@Platforms(Platform.HOSTED_ONLY.class)
85+
public void addVerifiedSecurityProvider(String key, Object value) {
86+
verifiedSecurityProviders.put(key, value);
87+
}
88+
89+
public Object getSecurityProviderVerificationResult(String key) {
90+
return verifiedSecurityProviders.get(key);
91+
}
92+
93+
@Platforms(Platform.HOSTED_ONLY.class)
94+
public void markSecurityProviderAsNotLoaded(String provider) {
95+
markedAsNotLoaded.add(provider);
96+
}
97+
98+
@Platforms(Platform.HOSTED_ONLY.class)
99+
public boolean isSecurityProviderNotLoaded(String provider) {
100+
return markedAsNotLoaded.contains(provider);
101+
}
102+
103+
@Platforms(Platform.HOSTED_ONLY.class)
104+
public boolean isUserRequestedSecurityProvider(String provider) {
105+
return userRequestedSecurityProviders.contains(provider);
106+
}
107+
108+
/**
109+
* Returns {@code true} if the provider, identified by either its name (e.g., SUN) or fully
110+
* qualified name (e.g., sun.security.provider.Sun), is either user-requested or reachable via a
111+
* security service.
112+
*/
113+
public boolean isSecurityProviderExpected(String providerName, String providerFQName) {
114+
return verifiedSecurityProviders.containsKey(providerName) || userRequestedSecurityProviders.contains(providerFQName);
115+
}
116+
117+
@Platforms(Platform.HOSTED_ONLY.class)
118+
public void setSunECConstructor(Constructor<?> sunECConstructor) {
119+
this.sunECConstructor = sunECConstructor;
120+
}
121+
122+
public Provider allocateSunECProvider() {
123+
try {
124+
return (Provider) sunECConstructor.newInstance();
125+
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
126+
throw VMError.shouldNotReachHere("The SunEC constructor is not present.");
127+
}
128+
}
129+
}

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

+115-26
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import static com.oracle.svm.core.snippets.KnownIntrinsics.readCallerStackPointer;
2828

29-
import java.lang.ref.ReferenceQueue;
3029
import java.lang.reflect.Constructor;
3130
import java.lang.reflect.InvocationTargetException;
3231
import java.net.URL;
@@ -69,6 +68,8 @@
6968
import com.oracle.svm.core.util.VMError;
7069
import com.oracle.svm.util.ReflectionUtil;
7170

71+
import jdk.graal.compiler.core.common.SuppressFBWarnings;
72+
import jdk.graal.compiler.serviceprovider.JavaVersionUtil;
7273
import sun.security.util.SecurityConstants;
7374

7475
/*
@@ -328,6 +329,7 @@ final class Target_javax_crypto_JceSecurity {
328329
// value == PROVIDER_VERIFIED is successfully verified
329330
// value is failure cause Exception in error case
330331
@Alias //
332+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
331333
private static Map<Object, Object> verificationResults;
332334

333335
@Alias //
@@ -338,16 +340,11 @@ final class Target_javax_crypto_JceSecurity {
338340
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) //
339341
private static Map<Class<?>, URL> codeBaseCacheRef = new WeakHashMap<>();
340342

341-
@Alias //
342-
@TargetElement //
343-
private static ReferenceQueue<Object> queue;
344-
345343
@Substitute
346344
static Exception getVerificationResult(Provider p) {
347345
/* Start code block copied from original method. */
348346
/* The verification results map key is an identity wrapper object. */
349-
Object key = new Target_javax_crypto_JceSecurity_WeakIdentityWrapper(p, queue);
350-
Object o = verificationResults.get(key);
347+
Object o = SecurityProvidersSupport.singleton().getSecurityProviderVerificationResult(p.getName());
351348
if (o == PROVIDER_VERIFIED) {
352349
return null;
353350
} else if (o != null) {
@@ -365,15 +362,6 @@ static Exception getVerificationResult(Provider p) {
365362
}
366363
}
367364

368-
@TargetClass(className = "javax.crypto.JceSecurity", innerClass = "WeakIdentityWrapper")
369-
@SuppressWarnings({"unused"})
370-
final class Target_javax_crypto_JceSecurity_WeakIdentityWrapper {
371-
372-
@Alias //
373-
Target_javax_crypto_JceSecurity_WeakIdentityWrapper(Provider obj, ReferenceQueue<Object> queue) {
374-
}
375-
}
376-
377365
class JceSecurityAccessor {
378366
private static volatile SecureRandom RANDOM;
379367

@@ -547,19 +535,120 @@ final class Target_sun_security_jca_ProviderConfig {
547535
@Alias //
548536
private String provName;
549537

538+
@Alias//
539+
private static sun.security.util.Debug debug;
540+
541+
@Alias//
542+
private Provider provider;
543+
544+
@Alias//
545+
private boolean isLoading;
546+
547+
@Alias//
548+
private int tries;
549+
550+
@Alias
551+
private native Provider doLoadProvider();
552+
553+
@Alias
554+
private native boolean shouldLoad();
555+
550556
/**
551-
* All security providers used in a native-image must be registered during image build time. At
552-
* runtime, we shouldn't have a call to doLoadProvider. However, this method is still reachable
553-
* at runtime, and transitively includes other types in the image, among which is
554-
* sun.security.jca.ProviderConfig.ProviderLoader. This class contains a static field with a
555-
* cache of providers loaded during the image build. The contents of this cache can vary even
556-
* when building the same image due to the way services are loaded on Java 11. This cache can
557-
* increase the final image size substantially (if it contains, for example,
558-
* {@code org.jcp.xml.dsig.internal.dom.XMLDSigRI}.
557+
* The `entrypoint` for allocating security providers at runtime. The implementation is copied
558+
* from the JDK with a small tweak to filter out providers that are neither user-requested nor
559+
* reachable via a security service.
559560
*/
560561
@Substitute
561-
private Provider doLoadProvider() {
562-
throw VMError.unsupportedFeature("Cannot load new security provider at runtime: " + provName + ".");
562+
@SuppressWarnings("fallthrough")
563+
@SuppressFBWarnings(value = "DC_DOUBLECHECK", justification = "This double-check is implemented correctly and is intentional.")
564+
Provider getProvider() {
565+
// volatile variable load
566+
Provider p = provider;
567+
if (p != null) {
568+
return p;
569+
}
570+
// DCL
571+
synchronized (this) {
572+
p = provider;
573+
if (p != null) {
574+
return p;
575+
}
576+
if (!shouldLoad()) {
577+
return null;
578+
}
579+
580+
// Create providers which are in java.base directly
581+
SecurityProvidersSupport support = SecurityProvidersSupport.singleton();
582+
switch (provName) {
583+
case "SUN", "sun.security.provider.Sun": {
584+
p = support.isSecurityProviderExpected("SUN", "sun.security.provider.Sun") ? new sun.security.provider.Sun() : null;
585+
break;
586+
}
587+
case "SunRsaSign", "sun.security.rsa.SunRsaSign": {
588+
p = support.isSecurityProviderExpected("SunRsaSign", "sun.security.rsa.SunRsaSign") ? new sun.security.rsa.SunRsaSign() : null;
589+
break;
590+
}
591+
case "SunJCE", "com.sun.crypto.provider.SunJCE": {
592+
p = support.isSecurityProviderExpected("SunJCE", "com.sun.crypto.provider.SunJCE") ? new com.sun.crypto.provider.SunJCE() : null;
593+
break;
594+
}
595+
case "SunJSSE": {
596+
p = support.isSecurityProviderExpected("SunJSSE", "sun.security.ssl.SunJSSE") ? new sun.security.ssl.SunJSSE() : null;
597+
break;
598+
}
599+
case "Apple", "apple.security.AppleProvider": {
600+
// need to use reflection since this class only exists on MacOsx
601+
try {
602+
Class<?> c = Class.forName("apple.security.AppleProvider");
603+
if (Provider.class.isAssignableFrom(c)) {
604+
@SuppressWarnings("deprecation")
605+
Object newInstance = c.newInstance();
606+
p = (Provider) newInstance;
607+
}
608+
} catch (Exception ex) {
609+
if (debug != null) {
610+
debug.println("Error loading provider Apple");
611+
ex.printStackTrace();
612+
}
613+
}
614+
break;
615+
}
616+
case "SunEC": {
617+
if (JavaVersionUtil.JAVA_SPEC > 21) {
618+
// Constructor inside method and then allocate. ModuleSupport to open.
619+
p = support.isSecurityProviderExpected("SunEC", "sun.security.ec.SunEC") ? support.allocateSunECProvider() : null;
620+
break;
621+
}
622+
/*
623+
* On older JDK versions, SunEC was part of the `jdk.crypto.ec` module and was
624+
* allocated via the service loading mechanism, so this fallthrough is
625+
* intentional. On newer JDK versions, SunEC is part of `java.base` and is
626+
* allocated directly.
627+
*/
628+
}
629+
// fall through
630+
default: {
631+
if (isLoading) {
632+
// because this method is synchronized, this can only
633+
// happen if there is recursion.
634+
if (debug != null) {
635+
debug.println("Recursion loading provider: " + this);
636+
new Exception("Call trace").printStackTrace();
637+
}
638+
return null;
639+
}
640+
try {
641+
isLoading = true;
642+
tries++;
643+
p = doLoadProvider();
644+
} finally {
645+
isLoading = false;
646+
}
647+
}
648+
}
649+
provider = p;
650+
}
651+
return p;
563652
}
564653
}
565654

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

-7
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
8989
*/
9090
RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
9191
rci.initializeAtBuildTime("sun.security.util.UntrustedCertificates", "Required for TrustStoreManager");
92-
/*
93-
* All security providers must be registered (and initialized) at buildtime (see
94-
* SecuritySubstitutions.java). XMLDSigRI is used for validating XML Signatures from
95-
* certificate files while generating X509Certificates.
96-
*/
97-
rci.initializeAtBuildTime("org.jcp.xml.dsig.internal.dom.XMLDSigRI", "Required for TrustStoreManager");
98-
rci.initializeAtBuildTime("org.jcp.xml.dsig.internal.dom.XMLDSigRI$ProviderService", "Required for TrustStoreManager");
9992
}
10093
}
10194

0 commit comments

Comments
 (0)