CGLIB: signer information does not match signer information of other classes
Today I’m going to describe the solution for the issue SPR-12833. The issue description says about a subset of problems which you’ll get using spring in a signed environment. The good thing about it, you need to sign jars of your application in limited cases. One of those cases is you need to distribute your application to clients by Java Web Start. And the root cause of this problem is the fact that built-in CGLIB doesn’t work properly in a signed environment. If you start your app from IDE all your classes aren’t signed, and this is a problem for CGLIB. There is a class
org.springframework.cglib.core.ReflectUtils and it uses
PROTECTION_DOMAIN which was retrieved from spring framework’s jar (in our case, all classes in this jar are signed), while all classes in our project aren’t signed until we pack it, sign, and distribute the application. Default classloader tries to compare certificates from spring framework’s classes with empty certificates list from project’s classes, here we get
SecurityException. This issue was solved in original CGLIB’s repository on Jul 18, 2014, but wasn’t merged into spring framework.
If you try to use proxies without any tricks, most likely you will get something like this:
Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237) at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:317) at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:137) at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:109) at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:400) ... 39 more Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384) at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219) ... 44 more Caused by: java.lang.SecurityException: class "com.dimafeng.test.MyTest$Config$$EnhancerBySpringCGLIB$$aec0d6cc"'s signer information does not match signer information of other classes in the same package at java.lang.ClassLoader.checkCerts(ClassLoader.java:895) at java.lang.ClassLoader.preDefineClass(ClassLoader.java:665) at java.lang.ClassLoader.defineClass(ClassLoader.java:758) ... 50 more
Spring framework uses CGLIB proxy generation for 2 operations:
- Java-based configuration using
- AOP proxies without interface or proxies which are marked with
This class of problems can be solved really easy. The first comment in SPR-12833 says that if you use
@Component-based annotations instead of
@Configuration, spring won’t generate CGLIB proxy.
This simple test creates
ApplicationContext from the Java-based configuration in
Config. If you start it test will pass.
The quickest solution of this issue is a usage of a custom class loader. Spring framework allows to use your own instance of the class loader for beans when you create your
ApplicationContext. It may look like this:
It’s important to pass
false as a second parameter - we don’t need to refresh context before a new class loader will be set. Now we can implement our
Here we have a little bit magic. That’s why you don’t need to include this code in production. This works well for tests and dev environment when you use a default class loader. Probably, this trick won’t work in next versions of java, if
ClassLoader structure is changed.
What we’ve done here. First of all, it’s important to set a parent class loader, it should be the class loader which is used during application start. In this case, classes will be validated in our class loader but will be stored in the parent. Then, we override value of
package2certs field - we should set map-mock instead. If you look at
ClassLoader’s sources, you’ll see:
package2certs always returns
null we’ll never get
SecurityException and it’s good in our case.