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/*
@@ -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-
408396class 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
0 commit comments