jasypt-spring-boot
jasypt-spring-boot copied to clipboard
Add GraalVM Native Image Compatibility
There is now a big initiative to make Spring compatible with GraalVM Native Images, which provides massive performance and resource improvements over the traditional JVM.
I've got my application compiling to a native image now, but it fails at runtime because of this library:
2020-11-27 15:03:05,312 ERROR [main] o.s.boot.SpringApplication - Application run failed
org.springframework.aop.framework.AopConfigException: Can't create an ObjenesisCglibAopProxy since it is unsupported in native images
at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:26)
at org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy(ProxyCreatorSupport.java:105)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:97)
at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.proxyPropertySource(EncryptablePropertySourceConverter.java:71)
at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.instantiatePropertySource(EncryptablePropertySourceConverter.java:78)
at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.convertPropertySource(EncryptablePropertySourceConverter.java:45)
at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.makeEncryptable(EncryptablePropertySourceConverter.java:37)
at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.lambda$convertPropertySources$1(EncryptablePropertySourceConverter.java:27)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.concurrent.CopyOnWriteArrayList$COWIterator.forEachRemaining(CopyOnWriteArrayList.java:1206)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.convertPropertySources(EncryptablePropertySourceConverter.java:28)
at com.ulisesbocchio.jasyptspringboot.configuration.EnableEncryptablePropertiesBeanFactoryPostProcessor.postProcessBeanFactory(EnableEncryptablePropertiesBeanFactoryPostProcessor.java:51)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:291)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:175)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at com.morg.service.app.HeyItsMyFakeApplication.main(HeyItsMyFakeApplication.java:10)
This is the offending function: https://github.com/ulisesbocchio/jasypt-spring-boot/blob/master/jasypt-spring-boot/src/main/java/com/ulisesbocchio/jasyptspringboot/EncryptablePropertySourceConverter.java#L77
Judging by the comments, it looks like this section has been problematic before, but the fallback that has been introduced causes the incompatibility with Graal Native Images.
Is there an alternative to this fallback which could be created to ensure Graal compatibility? I'll also raise this issue upstream to see if the workaround used here can be avoided entirely.
Cheers!, Rich
Interesting... thanks for bringing this to my attention. I'd love a GraalVM spring boot app template to play around if you have one. The problem at the core is that some of the Spring property source types are not extendable... so I can't intercept them by wrapping them. I can add some config to disable the proxies altogether, but you'll need to be aware they wouldn't be encryptable anymore. Spring internally is accessing datasources by name/type that's why the proxies are needed. A couple alternatives to look at:
- EnvironmentPostProcessor registered through spring.factories
- Graal friendly Custom Environment that intercepts at the top level only. This would have the same limitations explained before if the property sources are being accessed directly
3.0.4 released. It includes no AOP defaults, so you can give it a try, it shouldn't try to proxy anything unless you tell it to
I have a related problem with the latest version 3.0.5
The error I am getting is not the same as above but still there is an issue.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'enableEncryptablePropertySourcesPostProcessor': Unsatisfied dependency expressed through method 'enableEncryptablePropertySourcesPostProcessor' parameter 1: Error creating bean with name 'encryptablePropertySourceConverter': Instantiation of supplied bean failed at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:351) at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:271) at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:206) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:191) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) at gr.cosmoone.admhe.config.AdmheApiApplication.main(AdmheApiApplication.java:25) at [email protected]/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:775) at org.graalvm.nativeimage.builder/com.oracle.svm.core.windows.WindowsPlatformThreads.osThreadStartRoutine(WindowsPlatformThreads.java:178) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'encryptablePropertySourceConverter': Instantiation of supplied bean failed at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1236) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1405) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325) at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:334) ... 25 common frames omitted Caused by: java.lang.IllegalArgumentException: Invalid jasypt.encryptor.skip-property-sources: Class org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource not found at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.getPropertiesClass(EncryptablePropertySourceConverter.java:77) at [email protected]/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at [email protected]/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992) at [email protected]/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at [email protected]/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at [email protected]/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) at [email protected]/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at [email protected]/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.defaultSkipPropertySourceClasses(EncryptablePropertySourceConverter.java:59) at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.<init>(EncryptablePropertySourceConverter.java:53) at com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration.encryptablePropertySourceConverter(EncryptablePropertyResolverConfiguration.java:64) at com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration__BeanDefinitions.lambda$getEncryptablePropertySourceConverterInstanceSupplier$0(EncryptablePropertyResolverConfiguration__BeanDefinitions.java:41) at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:208) at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:59) at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:47) at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:220) at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:208) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) ... 37 common frames omitted Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource at [email protected]/java.lang.Class.forName(DynamicHub.java:1132) at [email protected]/java.lang.Class.forName(DynamicHub.java:1105) at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.getPropertiesClass(EncryptablePropertySourceConverter.java:71) ... 56 common frames omitted
I have a related problem with the latest version 3.0.5 The error I am getting is not the same as above but still there is an issue.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'enableEncryptablePropertySourcesPostProcessor': Unsatisfied dependency expressed through method 'enableEncryptablePropertySourcesPostProcessor' parameter 1: Error creating bean with name 'encryptablePropertySourceConverter': Instantiation of supplied bean failed at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:351) at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:271) at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:206) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:191) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) at gr.cosmoone.admhe.config.AdmheApiApplication.main(AdmheApiApplication.java:25) at [email protected]/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:775) at org.graalvm.nativeimage.builder/com.oracle.svm.core.windows.WindowsPlatformThreads.osThreadStartRoutine(WindowsPlatformThreads.java:178) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'encryptablePropertySourceConverter': Instantiation of supplied bean failed at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1236) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1405) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325) at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:334) ... 25 common frames omitted Caused by: java.lang.IllegalArgumentException: Invalid jasypt.encryptor.skip-property-sources: Class org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource not found at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.getPropertiesClass(EncryptablePropertySourceConverter.java:77) at [email protected]/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at [email protected]/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992) at [email protected]/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at [email protected]/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at [email protected]/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) at [email protected]/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at [email protected]/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.defaultSkipPropertySourceClasses(EncryptablePropertySourceConverter.java:59) at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.<init>(EncryptablePropertySourceConverter.java:53) at com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration.encryptablePropertySourceConverter(EncryptablePropertyResolverConfiguration.java:64) at com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration__BeanDefinitions.lambda$getEncryptablePropertySourceConverterInstanceSupplier$0(EncryptablePropertyResolverConfiguration__BeanDefinitions.java:41) at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:208) at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:59) at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:47) at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:220) at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:208) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) ... 37 common frames omitted Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource at [email protected]/java.lang.Class.forName(DynamicHub.java:1132) at [email protected]/java.lang.Class.forName(DynamicHub.java:1105) at com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.getPropertiesClass(EncryptablePropertySourceConverter.java:71) ... 56 common frames omitted
I ran into this as well. I'll see if I can supply the proper reflection hint to fix and I'll circle back.
This should at least get you past your error.. but my team and I only use Jasypt for local development (to prevent checking credentials into VCS), so we don't actually let it decrypt anything while deployed.
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
@Configuration
@ImportRuntimeHints(JasyptHints.class)
class JasyptHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
hints.reflection()
.registerType(Class.forName("org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"))
.registerType(Class.forName("org.springframework.core.env.PropertySource$StubPropertySource"));
} catch (ClassNotFoundException e) {
throw new RuntimeHintsException(e);
}
}
}
Let me know if you run into additional errors and I'll take another look.
That exception I'm throwing just extends RuntimeException. You'll want to create your own.
I use the class from @matthenry87 and the error is fixed. But starting the app now fails with a different error message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'bla.passwd' to java.lang.String:
Reason: org.jasypt.exceptions.EncryptionInitializationException: Cannot find a valid UNICODE normalizer: neither java.text.Normalizer nor com.ibm.icu.text.Normalizer have been found at the classpath. If you are using a version of the JDK older than JavaSE 6, you should include the icu4j library in your classpath.
Without Spring Native I don't have errors like this. And of course I'm using JDK 17. ;-) Does anyone have any ideas? Thanks a lot.
Interesting, is there something special about the value of your password that would require a Unicode normalizer?
Also - I highly advise against unnecessarily abbreviating things (even in general). You run the risk of your sensitive properties not being properly masked/sanitized by things like the actuator configProps endpoint for example.
It is likely that you just need a hint for that normalizer class if/when it's being loaded dynamically. Do you happen to know how to run the GraalVM tracing agent?
@benocms Here is the dynamic class loading and reflective calls you need to add hints for: https://github.com/jasypt/jasypt/blob/d384f9a755af2938bc142f7575365bee42ba5f22/jasypt/src/main/java/org/jasypt/normalization/Normalizer.java#L148
@matthenry87 :
Interesting, is there something special about the value of your password that would require a Unicode normalizer?
Not in my opinion. All characters between the brackets "ENC(...)" are only ASCII characters. I cannot find any Unicode characters.
Also - I highly advise against unnecessarily abbreviating things (even in general). You run the risk of your sensitive properties not being properly masked/sanitized by things like the actuator configProps endpoint for example.
I'm sorry, I didn't understand that. Can you give me a little more information about what you mean?
It is likely that you just need a hint for that normalizer class if/when it's being loaded dynamically. Do you happen to know how to run the GraalVM tracing agent?
Unfortunately no.
@matthenry87
@benocms Here is the dynamic class loading and reflective calls you need to add hints for: https://github.com/jasypt/jasypt/blob/d384f9a755af2938bc142f7575365bee42ba5f22/jasypt/src/main/java/org/jasypt/normalization/Normalizer.java#L148
I added the class 'org.jasypt.normalization.Normalizer' to the 'JasyptHints' class above. But this is probably not correct ;-) Anyway, the error remains with it.
hints.reflection()
.registerType(Class.forName("org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"))
.registerType(Class.forName("org.springframework.core.env.PropertySource$StubPropertySource"))
.registerType(Class.forName("org.jasypt.normalization.Normalizer"));
All the classes you need to register for runtime hints are
- java.text.Normalizer.class
- java.text.Normalizer.Form.class
- org.jasypt.salt.RandomSaltGenerator.class
- org.jasypt.iv.RandomIvGenerator.class
- org.jasypt.normalization.Normalizer.class
- Class.forName("org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource")
- Class.forName("org.springframework.core.env.PropertySource$StubPropertySource")
@paoyuan Thank you for your reply. I registered for runtime hints as following:
@Configuration
@EnableEncryptableProperties
@ImportRuntimeHints(JasyptConfig.class)
public class JasyptConfig implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
hints.reflection()
.registerType(java.text.Normalizer.class)
.registerType(java.text.Normalizer.Form.class)
.registerType(org.jasypt.salt.RandomSaltGenerator.class)
.registerType(org.jasypt.iv.RandomIvGenerator.class)
.registerType(org.jasypt.normalization.Normalizer.class)
.registerType(Class.forName("org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"))
.registerType(Class.forName("org.springframework.core.env.PropertySource$StubPropertySource"))
;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
and then build with Graal successfully, but the native exe startup failed.
Description:
Failed to bind properties under 'spring.datasource.password' to java.lang.String:
Reason: java.lang.IllegalStateException: either 'jasypt.encryptor.password', one of ['jasypt.encryptor.private-key-string', 'jasypt.encryptor.private-key-location'] for asymmetric encryption, or one of ['jasypt.encryptor.gcm-secret-key-string', 'jasypt.encryptor.gcm-secret-key-location', 'jasypt.encryptor.gcm-secret-key-password'] for AES/GCM encryption must be provided for Password-based or Asymmetric encryption
Action:
Update your application's configuration
How to resolve this error?
Dependency:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
application.yml:
jasypt:
encryptor:
bootstrap: true
password: qwerty
algorithm: PBEWithMD5AndDES
provider-name: SunJCE
string-output-type: base64
spring:
datasource:
...... something else
username: root
password: ENC(vAacMNOrKT5OV0pq4qk/5AyJfDMad0vBeEz8H75/F+45T3ulGUyQ2w==)
@yuan2006 add the following code will possibly resolve your problem.
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(jasyptPass);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
@paoyuan ths. Your runtime hints works both by JVM and native when the app run on another machine. I prepare to redo my env.
Unfortunately using the Algorithm PBEWITHHMACSHA512ANDAES_256 and all the reflection hints provided above I still get:
Caused by: org.jasypt.exceptions.EncryptionInitializationException: Cannot find a valid UNICODE normalizer: java.text.Normalizer has been found at the classpath, but has an incompatible signature for its 'normalize' method.
for the record I needed to add the following to get normalization working:
try {
Class<?> javaTextNormalizerClass = java.text.Normalizer.class;
Class<?> javaTextNormalizerFormClass = java.text.Normalizer.Form.class;
final Method javaTextNormalizerMethod =
javaTextNormalizerClass.getMethod(
"normalize", new Class[]{ CharSequence.class, javaTextNormalizerFormClass });
final Field javaTextNormalizerFormNFCConstantField = javaTextNormalizerFormClass.getField("NFC");
hints.reflection()
.registerType(java.text.Normalizer.class)
.registerMethod(javaTextNormalizerMethod, ExecutableMode.INTROSPECT)
.registerField(javaTextNormalizerFormNFCConstantField)
.registerType(java.text.Normalizer.Form.class)
.registerType(org.jasypt.salt.RandomSaltGenerator.class)
.registerType(org.jasypt.iv.RandomIvGenerator.class)
.registerType(org.jasypt.normalization.Normalizer.class)
.registerType(Class.forName("org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"))
.registerType(Class.forName("org.springframework.core.env.PropertySource$StubPropertySource"))
;
} catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
For spring-boot application,my summary is as follows: (1) application.yml
jasypt:
encryptor:
password: xxxxxx
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
(2) reflect-config.json
[
{
"name": "org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"methods": [
{
"name": "<init>",
"parameterTypes": [ ]
}
]
},
{
"name": "org.springframework.core.env.PropertySource$StubPropertySource",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"methods": [
{
"name": "<init>",
"parameterTypes": [ ]
}
]
},
{
"name": "org.jasypt.iv.RandomIvGenerator",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "org.jasypt.iv.NoIvGenerator",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "org.jasypt.salt.RandomSaltGenerator",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "org.jasypt.iv.RandomIvGenerator",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "org.jasypt.normalization.Normalizer",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "java.text.Normalizer",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "java.text.Normalizer$Form",
"allDeclaredFields": true,
"allDeclaredConstructors": true,
"allDeclaredMethods": true
}
]
(3)JasyptConfig.java
import lombok.extern.slf4j.Slf4j;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class JasyptConfig {
@Value("${jasypt.encryptor.password}")
private String password;
@Value("${jasypt.encryptor.algorithm}")
private String algorithm;
@Value("${jasypt.encryptor.iv-generator-classname}")
private String classname;
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor() {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm(algorithm);
config.setIvGeneratorClassName(classname);
encryptor.setConfig(config);
return encryptor;
}
}