I have been trying all day to obfuscate the web application we are developing (we are going to allow big clients to host it on their servers) and after surfing, searching and trying a lot I am unable to reduce the number of errors I receive:
"there were 7283 unresolved references to classes or interfaces"
I started from 20K so I managed to get some improvements...
This is the pom config I am using:
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>4.8</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<configuration>
<obfuscate>true</obfuscate>
<includeDependency>true</includeDependency>
<options>
<option>-target 1.6</option>
<option>-allowaccessmodification</option>
<option><![CDATA[
-keep public class * {
public <init>(...);
public void set*(...);
public void init*(...);
public void close*(...);
public ** get*(...);
public boolean is*(...);
public boolean can*(...);
}
]]></option>
<option><![CDATA[
-keepclassmembers class * {
#javax.inject.Inject *;
#javax.inject.Named *;
#org.springframework.beans.factory.annotation.Autowired *;
#org.springframework.beans.factory.annotation.Qualifier *;
#org.springframework.beans.factory.annotation.Value *;
#org.springframework.beans.factory.annotation.Required *;
}
]]></option>
<option>-keep #org.springframework.stereotype.Service class *</option>
<option>-keep #org.springframework.stereotype.Controller class * </option>
<option>-keep #org.springframework.stereotype.Component class * </option>
<option>-keep #org.springframework.stereotype.Repository class *</option>
</options>
<inFilter>com/proton/**,!com/proton/protocol/**</inFilter>
<injar>${build.warname}.${project.packaging}</injar>
<outjar>${build.warname}-proguarded.${project.packaging}</outjar>
<outputDirectory>${project.build.directory}</outputDirectory>
<addMavenDescriptor>false</addMavenDescriptor>
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jce.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
</libs>
<proguardVersion>4.8</proguardVersion>
</configuration>
</plugin>
And a big part of the result log:
https://raw.github.com/gist/3708235/37b4cada40fa4742fd1e3ef0f24efd69fa232b28/Proguard
It seems that at least a big chunk of the errors are from spring libraries, I am sure I must work a lot with keep rules to fix some dynamic calls but I will be great if someone could help with all this errors.
I forgot I had this question open, this is the setup we are using right now:
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>4.8</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<configuration>
<obfuscate>true</obfuscate>
<includeDependency>true</includeDependency>
<options>
<option>-target 1.6</option>
<option>-dontoptimize</option>
<option>-keepdirectories</option>
<option>-renamesourcefileattribute SourceFile</option>
<option>-keepparameternames</option>
<option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
<option><![CDATA[
-keep public class com.proton.commons.** { public protected *; }
]]></option>
<!-- Here comes a lot of interface packages, public classes, etc as the last one -->
<option><![CDATA[
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
]]></option>
<option><![CDATA[
-keepclassmembers class * {
#org.springframework.beans.factory.annotation.Autowired *;
#org.springframework.beans.factory.annotation.Qualifier *;
#org.springframework.beans.factory.annotation.Value *;
#org.springframework.beans.factory.annotation.Required *;
#org.springframework.context.annotation.Bean *;
#javax.annotation.PostConstruct *;
#javax.annotation.PreDestroy *;
#org.aspectj.lang.annotation.AfterReturning *;
#org.aspectj.lang.annotation.Pointcut *;
#org.aspectj.lang.annotation.AfterThrowing *;
#org.aspectj.lang.annotation.Around *;
}
]]></option>
<option>-keep #org.springframework.stereotype.Service class *</option>
<option>-keep #org.springframework.stereotype.Controller class * </option>
<option>-keep #org.springframework.stereotype.Component class * </option>
<option>-keep #org.springframework.stereotype.Repository class *</option>
<option>-keep #org.springframework.cache.annotation.EnableCaching class *</option>
<option>-keep #org.springframework.context.annotation.Configuration class *</option>
<!-- Custom annotation -->
<option>-keep #com.proton.ks.aop.ProtonLogging class *</option>
<option>-keep #org.aspectj.lang.annotation.Aspect class *</option>
<option>-keep #java.lang.annotation.Retention class *</option>
<option>-keep #java.lang.annotation.Target class *</option>
<option>-dontwarn javax.**</option>
<option>-dontnote com.proton.ks.preferences.PreferenceFactory</option>
</options>
<addMavenDescriptor>false</addMavenDescriptor>
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jce.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
<lib>${java.home}/lib/ext/sunjce_provider.jar</lib>
</libs>
<proguardVersion>4.8</proguardVersion>
</configuration>
</plugin>
I have solved a lot of my problems using -dontskipnonpubliclibraryclasses.
Related
I have a multi module maven project runs with Spring Boot and Webflux under Netty. Si I was using proguard maven plugin to generate obfuscated jar file.
My problem is everything looks fine when I look at the logs but when I send a request to it I get 404 error.
The build section at pom.xml looks like:
<build>
<plugins>
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<version>2.5.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<proguardVersion>6.2.2</proguardVersion>
<injar>${project.build.finalName}.jar</injar>
<outjar>${project.build.finalName}.jar</outjar>
<obfuscate>true</obfuscate>
<proguardInclude>proguard.cfg</proguardInclude>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>6.2.2</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>com.dummy.test.MainClass</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
And the proguard.cfg looks like:
-dontshrink
-dontoptimize
-useuniqueclassmembernames
-adaptclassstrings
-keep public class * extends org.springframework.boot.web.support.SpringBootServletInitializer
-keep public class * extends org.springframework.boot.loader.**
-keepclasseswithmembers public class * { public static void main(java.lang.String[]);}
-keepclassmembers enum * { *; }
-keepclassmembers class * {
#org.springframework.beans.factory.annotation.Autowired *;
#org.springframework.beans.factory.annotation.Qualifier *;
#org.springframework.beans.factory.annotation.Value *;
#org.springframework.beans.factory.annotation.Required *;
#org.springframework.context.annotation.Bean *;
#org.springframework.context.annotation.Primary *;
#org.springframework.boot.context.properties.ConfigurationProperties *;
#org.springframework.boot.context.properties.EnableConfigurationProperties *;
#javax.inject.Inject *;
}
-keep #org.springframework.cache.annotation.EnableCaching class *
-keep #org.springframework.context.annotation.Configuration class *
-keep #org.springframework.boot.context.properties.ConfigurationProperties class *
-keep #org.springframework.boot.autoconfigure.SpringBootApplication class *
-keep #org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration class *
-keep #org.springframework.stereotype.Repository class *
-allowaccessmodification
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,RuntimeVisibleAnnotations
-keepdirectories org.springframework.boot.autoconfigure
-keepnames class * implements java.io.Serializable
-keepclassmembers class * {
#org.springframework.beans.factory.annotation.Autowired *;
}
## keep same
-keepclassmembernames class com.dummy.test.entity.** { *; }
## keep getters and setters for mail template
-keepclassmembers class * {
*** get*();
void set*(***);
}
It turns out, Proguard moves all the obfuscated files under BOOT-INF.
Spring boot scans the main package but it can not find your obfuscated files, because they are not in the main package. So you need to give a proper package name to Proguard to move them somewhere under the main package.
All you need to do is give a new package name like:
-repackageclasses com.foo.bar.obfuscated
I'm trying to obfuscate my code via the MAven Proguard plugin, and it works almost perfectly : when running the obfuscated jar, I get warning and errors regarding log4j. The consequence is that I have no log at all (but the program runs correctly).
Here is my pom.xml Proguard part :
<plugin>
<groupid>com.github.wvengen</groupid>
<artifactid>proguard-maven-plugin</artifactid>
<version>2.0.10</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<obfuscate>true</obfuscate>
<injar>${project.build.finalName}-jar-with-dependencies.jar</injar>
<outjar>${project.build.finalName}-small.jar</outjar>
<includedependency>true</includedependency>
<options>
<option>-keep public class my.package.MyMainClass { *; }</option>
<option>-ignorewarnings</option>
<option>-keepclassmembers class * extends java.lang.Enum { *; }</option>
<option>-keep class org.apache.logging.log4j.** { *; }</option>
<option>-keep class org.apache.log4j.** { *; }</option>
<option>-keepattributes *Annotation*</option>
<option>-keep class org.codehaus.groovy.jsr223.** { *; }</option>
<option>-adaptresourcefilenames</option>
<option>-repackageclasses</option>
<option>-overloadaggressively</option>
<option>-allowaccessmodification</option>
</options>
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jce.jar</lib>
</libs>
<archive>
<manifest>
<mainclass>MyMainClass</mainclass>
<packagename>my.package</packagename>
</manifest>
</archive>
</configuration>
<dependencies>
<dependency>
<groupid>net.sf.proguard</groupid>
<artifactid>proguard-base</artifactid>
<version>4.10</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
And when running my obfuscated jar, I get errors and warnings like this :
main WARN Found a TypeConverter [org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigIntegerConverter#14c5515] for type [void] that already exists. (this warning for all Converters)
main ERROR Unable to inject fields into builder class for plugin type class org.apache.logging.log4j.core.layout.PatternLayout, element PatternLayout.
main ERROR Unable to invoke factory method in class class org.apache.logging.log4j.core.filter.ThresholdFilter for element ThresholdFilter.
main ERROR Null object returned for ThresholdFilter in Filters.
main ERROR Unable to invoke factory method in class class org.apache.logging.log4j.core.config.LoggerConfig$RootLogger for element Root.
main ERROR Null object returned for Logger in Loggers.
main ERROR Null object returned for Root in Loggers.
main WARN No Root logger was configured, creating default ERROR-level Root logger with Console appender
any idea on why I cannot log ?
Thank you very much
is there any good example for obfuscating Spring application using proguard & Maven ? I tried adding this snippet in pom.xml. My project is compiling and running, but did not get obfuscated.
I was able to decompile my war file and view the code. Is there any setting required to obfuscate generated file ??
<!-- ProGuard obfuscation plugin -->
<plugin>
<groupId>com.github.wvengen</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<version>2.0.6</version>
<executions>
<execution>
<configuration>
<obfuscate>true</obfuscate>
<proguardVersion>5.2.1</proguardVersion>
<options>
<option>-target 1.7</option>
<option>-dontnote</option>
<option>-dontpreverify</option>
<option>-dontskipnonpubliclibraryclasses</option>
<option>-dontskipnonpubliclibraryclassmembers</option>
<option>-allowaccessmodification</option>
<option>-repackageclasses ''</option>
<option>-keep public class ${mainClass} { public static void
main(java.lang.String[]); }</option>
<option>-keepattributes
InnerClasses,Signature,LineNumberTable,*Annotation*,EnclosingMethod</option>
<option>-keepclassmembers enum * { public static **[] values();
public static ** valueOf(java.lang.String); }</option>
<option>-dontwarn org.spout.**</option>
<option>-dontwarn com.google.**</option>
</options>
<proguardInclude>${basedir}/proguard.conf</proguardInclude>
<includeDependency>false</includeDependency>
<libs>
<lib>${java.home}/lib/jce.jar</lib>
<lib>${java.home}/lib/jfxrt.jar</lib>
<lib>${java.home}/lib/rt.jar</lib>
</libs>
</configuration>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>5.2.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard</artifactId>
<version>4.4</version>
</dependency>
</dependencies>
</plugin>
I try to proguard my jar project . I need to avaible only shrinking, so this is my configurations :
<plugin>
<groupId>com.pyx4me</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<proguardVersion>4.3</proguardVersion>
<options>
<option>-dontobfuscate</option>
<option>-optimizationpasses 5</option>
<option>-dontoptimize</option>
<option>-ignorewarnings</option>
<option>-dontskipnonpubliclibraryclasses</option>
<option>-dontskipnonpubliclibraryclassmembers</option>
<option>-dontpreverify</option>
<option>-verbose</option>
<option>-dontwarn</option>
<option>-dontnote</option>
<option>-keepattributes SourceFile, SourceDir</option>
<option>-keepattributes *Annotation*</option>
<option><![CDATA[
-keep class com.myclass.document.** { *; }
-keep class com.myclass.connectivity.** { *; }
-keep class com.myclass.util.** { *; }
-keep class ch.boye.httpclientandroidlib.** { *; }
-keep class com.google.** { *;}
-keep class org.joda.time.** { *;}
-keep class org.apache.log4j.** { *;}
-keep class org.apache.commons.** { *;}
-keep class java.util.Iterator { *;}
-keepclasseswithmembernames class * { native <methods>; }
-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
]]>
</option>
</options>
<maxMemory>256m</maxMemory>
<injar>${project.build.finalName}-jar-with-dependencies.jar</injar>
<outjar>${project.build.finalName}-jar-with-dependencies.jar</outjar>
<outputDirectory>${project.build.directory}</outputDirectory>
<addMavenDescriptor>false</addMavenDescriptor>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard</artifactId>
<version>4.3</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
</plugin>
And this exception I get on start, but I left almoust all files (-keep) and still get this exception :
FATAL UNEXPECTED EXCEPTION CAUGHT in thread main.main
java.lang.IncompatibleClassChangeError: Class com.google.common.collect.Iterators$7 does not implement the requested interface java.util.Iterator
at com.google.common.collect.Iterators.getNext(Iterators.java:860)
at com.google.common.collect.Iterators.find(Iterators.java:728)
at com.google.common.collect.Iterables.find(Iterables.java:656)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
May be related to : What causes java.lang.IncompatibleClassChangeError?
Fixed by adding:
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
</libs>
I have a library Common.License which I am obfuscating with Proguard:
<plugin>
<groupId>com.pyx4me</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<obfuscate>true</obfuscate>
<options>
<option>-dontoptimize</option>
<option>-renamesourcefileattribute SourceFile</option>
<option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
<option>-keep public class * { public protected *;}</option>
<option>-keepclassmembernames class * {java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean);}</option>
<option>-keepclassmembernames class * {com.common.license.LicenseSessionStore licenseSessionStore; com.common.license.LicenseStore licenseStore;}</option>
<option>-keepclassmembers enum * {public static **[] values(); public static ** valueOf(java.lang.String);}</option>
<option>-keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve();}</option>
</options>
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
</libs>
<addMavenDescriptor>false</addMavenDescriptor>
</configuration>
</plugin>
This library has a Spring bean annotated with #Service:
#Service
public class LicenseServiceImpl implements LicenseService {
#Autowired(required = false)
LicenseSessionStore licenseSessionStore;
#Autowired(required = false)
LicenseStore licenseStore;
...
}
I use this library in a web service Company.License where I want the LicenseService to autowire:
#Component
public class BackgroundTasks {
#Autowired
ScheduledExecutorService scheduledExecutorService;
#Autowired
LicenseService licenseService;
...
}
So Company.License has a dependency on Common.License. If I obfuscate Common.License then licenseService will not autowire in BackgroundTasks. The only way I could work around this was to define licenseService explicitly as a bean:
#Bean(name = "licenseService", autowire = Autowire.BY_NAME)
public LicenseService getLicenseService() {
if (licenseService == null) {
licenseService = new LicenseServiceImpl();
}
return licenseService;
}
I should not need to explicitly declare this as a bean like this as I have already annotated the class with #Service which should be enough to make the autowiring of licenseService in BackgroundTasks Spring-magically work. But it doesn't!
What is Proguard specifically doing to make this not work? Is there anything I can do in the configuration of Proguard to make it more Spring friendly?
Grant
A big thank you to Eric Lafortune for helping point me in the correct direction here:
http://sourceforge.net/projects/proguard/forums/forum/182456/topic/2547498
Here's the working pom file addition specifying the plug-in and the options required:
<plugin>
<groupId>com.pyx4me</groupId>
<artifactId>proguard-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>proguard</goal>
</goals>
</execution>
</executions>
<configuration>
<obfuscate>true</obfuscate>
<options>
<option>-dontoptimize</option>
<option>-keepdirectories</option>
<option>-renamesourcefileattribute SourceFile</option>
<option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option>
<option>-keep public class * { public protected *;}</option>
<option>-keepclassmembernames class * {java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean);}</option>
<option>-keepclassmembernames class * {com.common.license.LicenseService licenseService; com.common.license.LicenseSessionStore licenseSessionStore;}</option>
<option>-keepclassmembers enum * {public static **[] values(); public static ** valueOf(java.lang.String);}</option>
<option>-keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve();}</option>
<option>-keep #org.springframework.beans.factory.annotation.Service class *</option>
<option>-keep #org.springframework.stereotype.Controller class *</option>
<option>-keepclassmembers class * { #org.springframework.beans.factory.annotation.Autowired *; }</option>
</options>
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
</libs>
<addMavenDescriptor>false</addMavenDescriptor>
</configuration>
</plugin>
NOTE! You need to use ProGuard 4.4, the latest version of ProGuard-Maven-Plugin (2.0.4) uses 4.3 so you need to edit:
{M2_HOME}\repository\com\pyx4me\proguard-maven-plugin\2.0.4\proguard-maven-plugin-2.0.4.pom
To have the 4.4 dependency (like this):
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard</artifactId>
<version>4.4</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Here's another way to switch to version 4.4 of proguard (which is probably a bit better):
<configuration>
<proguardVersion>4.4</proguardVersion>
<obfuscate>true</obfuscate>
<options>
...
</options>
<libs>
<lib>${java.home}/lib/rt.jar</lib>
<lib>${java.home}/lib/jsse.jar</lib>
</libs>
<addMavenDescriptor>false</addMavenDescriptor>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard</artifactId>
<version>4.4</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
I had the same issue and the -keepdirectories directive helped solve the issue for me.