You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2026/09/13 15:17:19 UTC

(sling-org-apache-sling-commons-crypto) branch feature/defer-registration-until-provider-is-available created (now fa1121f)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a change to branch feature/defer-registration-until-provider-is-available
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-crypto.git


      at fa1121f  SLING-13280 Allow deferring registration of crypto service until a given security provider is registered with OSGi

This branch includes the following new commits:

     new fa1121f  SLING-13280 Allow deferring registration of crypto service until a given security provider is registered with OSGi

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(sling-org-apache-sling-commons-crypto) 01/01: SLING-13280 Allow deferring registration of crypto service until a given security provider is registered with OSGi

Posted by kw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/defer-registration-until-provider-is-available
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-crypto.git

commit fa1121f10604a01e98c1ca49809093c9d1de6213
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Sun Sep 13 17:17:09 2026 +0200

    SLING-13280 Allow deferring registration of crypto service until a given
    security provider is registered with OSGi
    
    Register both custom (provided by bundle) and default (provided by JRE)
    security providers with OSGi. Allow depending on it from both
    JcaPbeCryptoService and deprecated JasyptStandardPbeStringCryptoService
---
 .../OsgiAwareSecurityProviderInstaller.java        | 67 ++++++++++++++++++----
 .../JasyptStandardPbeStringCryptoService.java      |  6 ++
 .../crypto/jca/internal/JcaPbeCryptoService.java   |  5 +-
 .../internal/JcaPbeCryptoServiceConfiguration.java |  7 +++
 4 files changed, 70 insertions(+), 15 deletions(-)

diff --git a/src/main/java/org/apache/sling/commons/crypto/internal/OsgiAwareSecurityProviderInstaller.java b/src/main/java/org/apache/sling/commons/crypto/internal/OsgiAwareSecurityProviderInstaller.java
index 6c495a5..7b37507 100644
--- a/src/main/java/org/apache/sling/commons/crypto/internal/OsgiAwareSecurityProviderInstaller.java
+++ b/src/main/java/org/apache/sling/commons/crypto/internal/OsgiAwareSecurityProviderInstaller.java
@@ -29,10 +29,13 @@ import java.security.Security;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.SynchronousBundleListener;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -42,22 +45,31 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * This DS component listens for bundle events and automatically installs or uninstalls security providers
+ * This DS component listens for bundle events and automatically adds or removes security providers with the Java Security API
  * based on the presence of a service registration file {@value #SECURITY_PROVIDER_CONFIGURATION_FILE} in the 
- * started/stopping bundle.
+ * started/stopping bundle via {@link Security#addProvider(Provider)} and {@link Security#removeProvider(String)}.
+ * In addition it also registers each provider (even the ones shipping with the JVM) as an OSGi service so that other services can defer loading 
+ * until a certain provider is available.
+ * The OSGi service registration includes a property {@value #PROVIDER_NAME_PROPERTY} with the name of the provider.
  */
 @Component(immediate = true, service= {}, name = "org.apache.sling.commons.crypto.internal.AutoRegisterSecurityProvider")
 @ServiceDescription("Apache Sling Commons Crypto – Auto Register Security Provider")
 public final class OsgiAwareSecurityProviderInstaller implements SynchronousBundleListener {
     private static final String SECURITY_PROVIDER_CONFIGURATION_FILE = "META-INF/services/java.security.Provider";
     private static final Logger LOGGER = LoggerFactory.getLogger(OsgiAwareSecurityProviderInstaller.class);
+    private static final String PROVIDER_NAME_PROPERTY = "provider.name";
+    private final Map<String, ServiceRegistration<Provider>> registeredProviders;
+    private final BundleContext bundleContext;
 
     @Activate
     public OsgiAwareSecurityProviderInstaller(BundleContext bundleContext) {
+        registeredProviders = new ConcurrentHashMap<>();
+        this.bundleContext = bundleContext;
         bundleContext.addBundleListener(this);
+        registerOrUnregisterDefaultProviders(true);
         for (Bundle bundle : bundleContext.getBundles()) {
             if (bundle.getState() == Bundle.ACTIVE) {
-                addOrRemoveProviders(true, bundle);
+                addOrRemoveCustomProviders(true, bundle);
             }
         }
     }
@@ -67,9 +79,10 @@ public final class OsgiAwareSecurityProviderInstaller implements SynchronousBund
         bundleContext.removeBundleListener(this);
         for (Bundle bundle : bundleContext.getBundles()) {
             if (bundle.getState() == Bundle.ACTIVE) {
-                addOrRemoveProviders(false, bundle);
+                addOrRemoveCustomProviders(false, bundle);
             }
         }
+        registerOrUnregisterDefaultProviders(false);
     }
 
     @Override
@@ -84,15 +97,25 @@ public final class OsgiAwareSecurityProviderInstaller implements SynchronousBund
             LOGGER.debug("Ignoring bundle event {} for bundle {}", event.getType(), bundle.getSymbolicName());
             return;
         }
-        addOrRemoveProviders(isAdd, bundle);
+        addOrRemoveCustomProviders(isAdd, bundle);
+    }
+    
+    protected void registerOrUnregisterDefaultProviders(boolean isRegister) {
+        for (Provider provider : Security.getProviders()) {
+            if (isRegister) {
+                registerProviderWithOsgi(this.bundleContext, provider);
+            } else {
+                unregisterProviderWithOsgi(provider.getName(), registeredProviders.remove(provider.getName()));
+            }
+        }
     }
 
-    protected void addOrRemoveProviders(boolean isAdd, Bundle bundle) {
+    protected void addOrRemoveCustomProviders(boolean isAdd, Bundle bundle) {
         try {
             Collection<String> classNames = collectClassNamesFromProviderConfigurationFile(bundle);
             for (String className : classNames) {
                 try {
-                    addOrRemoveProvider(isAdd, bundle, className);
+                    addOrRemoveCustomProvider(isAdd, bundle, className);
                 } catch (ClassNotFoundException e) {
                     LOGGER.error("Class {} not found in bundle {}: {}", className, bundle.getSymbolicName(), e.getMessage(), e);
                 } catch (Exception e) {
@@ -124,7 +147,7 @@ public final class OsgiAwareSecurityProviderInstaller implements SynchronousBund
         return classNames;
     }
 
-    protected void addOrRemoveProvider(boolean isAdd, Bundle bundle, String providerClassName)
+    protected void addOrRemoveCustomProvider(boolean isAdd, Bundle bundle, String providerClassName)
             throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException,
             InvocationTargetException, NoSuchMethodException, SecurityException {
         Class<?> clazz = bundle.loadClass(providerClassName);
@@ -138,11 +161,8 @@ public final class OsgiAwareSecurityProviderInstaller implements SynchronousBund
             if (position == -1) {
                 LOGGER.warn("Failed to add security provider {} (name {}) from bundle {} to the security providers list. Provider with that name already registered.", providerClassName, provider.getName(), bundle);
             }
-            // also add service registration for the provider so that other services can defer loading until the provider is available
-            Hashtable<String, String> props = new Hashtable<>();
-            props.put("provider.name", provider.getName());
-            bundle.getBundleContext().registerService(Provider.class, provider, props);
             LOGGER.info("Added security provider {} (name {}) from bundle {} to last position {}", providerClassName, provider.getName(), bundle, position);
+            registerProviderWithOsgi(bundle.getBundleContext(), provider);
         } else {
             if (Security.getProvider(provider.getName()) != null) {
                 Security.removeProvider(provider.getName());
@@ -150,6 +170,29 @@ public final class OsgiAwareSecurityProviderInstaller implements SynchronousBund
             } else {
                 LOGGER.warn("Security provider {} (name {}) not found for removal", providerClassName, provider.getName());
             }
+            unregisterProviderWithOsgi(provider.getName(), registeredProviders.remove(provider.getName()));
+        }
+    }
+
+    private void registerProviderWithOsgi(BundleContext context, Provider provider) {
+        // also add service registration for the provider so that other services can defer loading until the provider is available
+        Hashtable<String, String> props = new Hashtable<>();
+        props.put(PROVIDER_NAME_PROPERTY, provider.getName());
+        ServiceRegistration<Provider> registration = context.registerService(Provider.class, provider, props);
+        registeredProviders.put(provider.getName(), registration);
+        LOGGER.info("Registered security provider with name {} as OSGi service", provider.getName());
+    }
+
+    private void unregisterProviderWithOsgi(String name, ServiceRegistration<Provider> registration) {
+        if (registration == null) {
+            LOGGER.warn("No service registration found for security provider with name '{}' to unregister", name);
+            return;
+        }
+        try {
+            registration.unregister();
+            LOGGER.info("Unregistered security provider with name '{}' as OSGi service", name);
+        } catch (IllegalStateException e) {
+            LOGGER.warn("Service for provider with name {} is already unregistered: {}", name, e.getMessage(), e);
         }
     }
 }
diff --git a/src/main/java/org/apache/sling/commons/crypto/jasypt/internal/JasyptStandardPbeStringCryptoService.java b/src/main/java/org/apache/sling/commons/crypto/jasypt/internal/JasyptStandardPbeStringCryptoService.java
index 6776f8c..6164c53 100644
--- a/src/main/java/org/apache/sling/commons/crypto/jasypt/internal/JasyptStandardPbeStringCryptoService.java
+++ b/src/main/java/org/apache/sling/commons/crypto/jasypt/internal/JasyptStandardPbeStringCryptoService.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sling.commons.crypto.jasypt.internal;
 
+import java.security.Provider;
 import java.util.Arrays;
 import java.util.Objects;
 import java.util.Set;
@@ -70,6 +71,11 @@ public final class JasyptStandardPbeStringCryptoService implements CryptoService
     )
     private volatile SaltGenerator saltGenerator;
 
+    // only used to potentially defer loading of this service until the security provider is available, but not used directly
+    @SuppressWarnings("unused")
+    @Reference
+    private volatile Provider securityProvider;
+
     private StandardPBEStringEncryptor encryptor;
 
     private String algorithm;
diff --git a/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java b/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
index b394aa2..8946ff0 100644
--- a/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
+++ b/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
@@ -70,7 +70,6 @@ import org.slf4j.LoggerFactory;
 @Component(service = CryptoService.class)
 @Designate(ocd = JcaPbeCryptoServiceConfiguration.class, factory = true)
 @ServiceDescription("Apache Sling Commons Crypto – JCA PBE String Crypto Service")
-@SuppressWarnings({ "java:S1117", "java:S3077", "java:S6212" })
 public final class JcaPbeCryptoService implements CryptoService {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(JcaPbeCryptoService.class);
@@ -90,8 +89,8 @@ public final class JcaPbeCryptoService implements CryptoService {
 
     @Activate
     public JcaPbeCryptoService(final JcaPbeCryptoServiceConfiguration configuration, BundleContext bundleContext,
-            @Reference(name="passwordProvider") PasswordProvider passwordProvider, @Reference(name="saltProvider") SaltProvider saltProvider)
-            throws NoSuchAlgorithmException { //
+            @Reference(name="passwordProvider") PasswordProvider passwordProvider, @Reference(name="saltProvider") SaltProvider saltProvider, @Reference(name="securityProvider") Provider securityProvider)
+            throws NoSuchAlgorithmException {
         this(configuration, saltProvider.getSalt(), passwordProvider);
     }
 
diff --git a/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceConfiguration.java b/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceConfiguration.java
index 8deaf03..848956c 100644
--- a/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceConfiguration.java
+++ b/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceConfiguration.java
@@ -73,6 +73,13 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition;
     )
     String securityProviderName() default "SunJCE";
 
+    @AttributeDefinition(
+        name = "Security Provider Target",
+        description = "Filter expression to target a Security Provider (usually by name with a pattern like \"(provider.name=<securityProviderName>)\"). Useful to defer loading of the service until the custom Security Provider bundle is available and properly registered.",
+        required = false
+    )
+    String securityProvider_target();
+
     // automatically evaluated (https://docs.osgi.org/specification/osgi.cmpn/8.0.0/service.component.html#service.component-target.property)
     @AttributeDefinition(
         name = "Password Provider Target",