This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-crypto.git
The following commit(s) were added to refs/heads/master by this push:
new 33bf365 SLING-13280 Allow deferring registration of crypto service
until a given security provider is registered with OSGi
33bf365 is described below
commit 33bf365dd30737ced98cc41609dd6cbc41d668b3
Author: Konrad Windszus <[email protected]>
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 1019fe8..69808d5 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",