Skip to content

Commit 269b9e8

Browse files
Initialize security providers at run time.
1 parent bbdf41b commit 269b9e8

File tree

8 files changed

+394
-290
lines changed

8 files changed

+394
-290
lines changed

substratevm/mx.substratevm/suite.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,12 @@
327327
"sun.reflect.generics.reflectiveObjects",
328328
"sun.reflect.generics.repository",
329329
"sun.reflect.generics.tree",
330+
"sun.security.rsa",
330331
"sun.security.jca",
331332
"sun.security.ssl",
332333
"sun.security.util",
334+
"sun.security.provider",
335+
"com.sun.crypto.provider",
333336
"sun.text.spi",
334337
"sun.util",
335338
"sun.util.locale",
Lines changed: 129 additions & 0 deletions
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

Lines changed: 115 additions & 26 deletions
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.net.URL;
3231
import java.security.AccessControlContext;
@@ -70,6 +69,8 @@
7069
import com.oracle.svm.core.util.VMError;
7170
import com.oracle.svm.util.ReflectionUtil;
7271

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

7576
/*
@@ -359,6 +360,7 @@ final class Target_javax_crypto_JceSecurity {
359360
// value == PROVIDER_VERIFIED is successfully verified
360361
// value is failure cause Exception in error case
361362
@Alias //
363+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
362364
private static Map<Object, Object> verificationResults;
363365

364366
@Alias //
@@ -369,16 +371,11 @@ final class Target_javax_crypto_JceSecurity {
369371
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) //
370372
private static Map<Class<?>, URL> codeBaseCacheRef = new WeakHashMap<>();
371373

372-
@Alias //
373-
@TargetElement //
374-
private static ReferenceQueue<Object> queue;
375-
376374
@Substitute
377375
static Exception getVerificationResult(Provider p) {
378376
/* Start code block copied from original method. */
379377
/* The verification results map key is an identity wrapper object. */
380-
Object key = new Target_javax_crypto_JceSecurity_WeakIdentityWrapper(p, queue);
381-
Object o = verificationResults.get(key);
378+
Object o = SecurityProvidersSupport.singleton().getSecurityProviderVerificationResult(p.getName());
382379
if (o == PROVIDER_VERIFIED) {
383380
return null;
384381
} else if (o != null) {
@@ -396,15 +393,6 @@ static Exception getVerificationResult(Provider p) {
396393
}
397394
}
398395

399-
@TargetClass(className = "javax.crypto.JceSecurity", innerClass = "WeakIdentityWrapper")
400-
@SuppressWarnings({"unused"})
401-
final class Target_javax_crypto_JceSecurity_WeakIdentityWrapper {
402-
403-
@Alias //
404-
Target_javax_crypto_JceSecurity_WeakIdentityWrapper(Provider obj, ReferenceQueue<Object> queue) {
405-
}
406-
}
407-
408396
class JceSecurityAccessor {
409397
private static volatile SecureRandom RANDOM;
410398

@@ -578,19 +566,120 @@ final class Target_sun_security_jca_ProviderConfig {
578566
@Alias //
579567
private String provName;
580568

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

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

Lines changed: 0 additions & 7 deletions
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)