2626
2727import static com .oracle .svm .core .snippets .KnownIntrinsics .readCallerStackPointer ;
2828
29- import java .lang .ref .ReferenceQueue ;
3029import java .lang .reflect .Constructor ;
3130import java .net .URL ;
3231import java .security .AccessControlContext ;
7069import com .oracle .svm .core .util .VMError ;
7170import com .oracle .svm .util .ReflectionUtil ;
7271
72+ import jdk .graal .compiler .core .common .SuppressFBWarnings ;
73+ import jdk .graal .compiler .serviceprovider .JavaVersionUtil ;
7374import sun .security .util .SecurityConstants ;
7475
7576/*
@@ -358,6 +359,7 @@ final class Target_javax_crypto_JceSecurity {
358359 // value == PROVIDER_VERIFIED is successfully verified
359360 // value is failure cause Exception in error case
360361 @ Alias //
362+ @ RecomputeFieldValue (kind = RecomputeFieldValue .Kind .Reset ) //
361363 private static Map <Object , Object > verificationResults ;
362364
363365 @ Alias //
@@ -368,16 +370,11 @@ final class Target_javax_crypto_JceSecurity {
368370 @ RecomputeFieldValue (kind = RecomputeFieldValue .Kind .FromAlias ) //
369371 private static Map <Class <?>, URL > codeBaseCacheRef = new WeakHashMap <>();
370372
371- @ Alias //
372- @ TargetElement //
373- private static ReferenceQueue <Object > queue ;
374-
375373 @ Substitute
376374 static Exception getVerificationResult (Provider p ) {
377375 /* Start code block copied from original method. */
378376 /* The verification results map key is an identity wrapper object. */
379- Object key = new Target_javax_crypto_JceSecurity_WeakIdentityWrapper (p , queue );
380- Object o = verificationResults .get (key );
377+ Object o = SecurityProvidersSupport .singleton ().getSecurityProviderVerificationResult (p .getName ());
381378 if (o == PROVIDER_VERIFIED ) {
382379 return null ;
383380 } else if (o != null ) {
@@ -395,15 +392,6 @@ static Exception getVerificationResult(Provider p) {
395392 }
396393}
397394
398- @ TargetClass (className = "javax.crypto.JceSecurity" , innerClass = "WeakIdentityWrapper" )
399- @ SuppressWarnings ({"unused" })
400- final class Target_javax_crypto_JceSecurity_WeakIdentityWrapper {
401-
402- @ Alias //
403- Target_javax_crypto_JceSecurity_WeakIdentityWrapper (Provider obj , ReferenceQueue <Object > queue ) {
404- }
405- }
406-
407395class JceSecurityAccessor {
408396 private static volatile SecureRandom RANDOM ;
409397
@@ -577,19 +565,120 @@ final class Target_sun_security_jca_ProviderConfig {
577565 @ Alias //
578566 private String provName ;
579567
568+ @ Alias //
569+ private static sun .security .util .Debug debug ;
570+
571+ @ Alias //
572+ private Provider provider ;
573+
574+ @ Alias //
575+ private boolean isLoading ;
576+
577+ @ Alias //
578+ private int tries ;
579+
580+ @ Alias
581+ private native Provider doLoadProvider ();
582+
583+ @ Alias
584+ private native boolean shouldLoad ();
585+
580586 /**
581- * All security providers used in a native-image must be registered during image build time. At
582- * runtime, we shouldn't have a call to doLoadProvider. However, this method is still reachable
583- * at runtime, and transitively includes other types in the image, among which is
584- * sun.security.jca.ProviderConfig.ProviderLoader. This class contains a static field with a
585- * cache of providers loaded during the image build. The contents of this cache can vary even
586- * when building the same image due to the way services are loaded on Java 11. This cache can
587- * increase the final image size substantially (if it contains, for example,
588- * {@code org.jcp.xml.dsig.internal.dom.XMLDSigRI}.
587+ * The `entrypoint` for allocating security providers at runtime. The implementation is copied
588+ * from the JDK with a small tweak to filter out providers that are neither user-requested nor
589+ * reachable via a security service.
589590 */
590591 @ Substitute
591- private Provider doLoadProvider () {
592- throw VMError .unsupportedFeature ("Cannot load new security provider at runtime: " + provName + "." );
592+ @ SuppressWarnings ("fallthrough" )
593+ @ SuppressFBWarnings (value = "DC_DOUBLECHECK" , justification = "This double-check is implemented correctly and is intentional." )
594+ Provider getProvider () {
595+ // volatile variable load
596+ Provider p = provider ;
597+ if (p != null ) {
598+ return p ;
599+ }
600+ // DCL
601+ synchronized (this ) {
602+ p = provider ;
603+ if (p != null ) {
604+ return p ;
605+ }
606+ if (!shouldLoad ()) {
607+ return null ;
608+ }
609+
610+ // Create providers which are in java.base directly
611+ SecurityProvidersSupport support = SecurityProvidersSupport .singleton ();
612+ switch (provName ) {
613+ case "SUN" , "sun.security.provider.Sun" : {
614+ p = support .isSecurityProviderExpected ("SUN" , "sun.security.provider.Sun" ) ? new sun .security .provider .Sun () : null ;
615+ break ;
616+ }
617+ case "SunRsaSign" , "sun.security.rsa.SunRsaSign" : {
618+ p = support .isSecurityProviderExpected ("SunRsaSign" , "sun.security.rsa.SunRsaSign" ) ? new sun .security .rsa .SunRsaSign () : null ;
619+ break ;
620+ }
621+ case "SunJCE" , "com.sun.crypto.provider.SunJCE" : {
622+ p = support .isSecurityProviderExpected ("SunJCE" , "com.sun.crypto.provider.SunJCE" ) ? new com .sun .crypto .provider .SunJCE () : null ;
623+ break ;
624+ }
625+ case "SunJSSE" : {
626+ p = support .isSecurityProviderExpected ("SunJSSE" , "sun.security.ssl.SunJSSE" ) ? new sun .security .ssl .SunJSSE () : null ;
627+ break ;
628+ }
629+ case "Apple" , "apple.security.AppleProvider" : {
630+ // need to use reflection since this class only exists on MacOsx
631+ try {
632+ Class <?> c = Class .forName ("apple.security.AppleProvider" );
633+ if (Provider .class .isAssignableFrom (c )) {
634+ @ SuppressWarnings ("deprecation" )
635+ Object newInstance = c .newInstance ();
636+ p = (Provider ) newInstance ;
637+ }
638+ } catch (Exception ex ) {
639+ if (debug != null ) {
640+ debug .println ("Error loading provider Apple" );
641+ ex .printStackTrace ();
642+ }
643+ }
644+ break ;
645+ }
646+ case "SunEC" : {
647+ if (JavaVersionUtil .JAVA_SPEC > 21 ) {
648+ // Constructor inside method and then allocate. ModuleSupport to open.
649+ p = support .isSecurityProviderExpected ("SunEC" , "sun.security.ec.SunEC" ) ? support .allocateSunECProvider () : null ;
650+ break ;
651+ }
652+ /*
653+ * On older JDK versions, SunEC was part of the `jdk.crypto.ec` module and was
654+ * allocated via the service loading mechanism, so this fallthrough is
655+ * intentional. On newer JDK versions, SunEC is part of `java.base` and is
656+ * allocated directly.
657+ */
658+ }
659+ // fall through
660+ default : {
661+ if (isLoading ) {
662+ // because this method is synchronized, this can only
663+ // happen if there is recursion.
664+ if (debug != null ) {
665+ debug .println ("Recursion loading provider: " + this );
666+ new Exception ("Call trace" ).printStackTrace ();
667+ }
668+ return null ;
669+ }
670+ try {
671+ isLoading = true ;
672+ tries ++;
673+ p = doLoadProvider ();
674+ } finally {
675+ isLoading = false ;
676+ }
677+ }
678+ }
679+ provider = p ;
680+ }
681+ return p ;
593682 }
594683}
595684
0 commit comments