9
votes

How to setup proguard obfuscation with spring boot 2 and gradle build?

Hello. Trying to setup code obfuscation of Spring Boot app with its gradle plugin and Proguard gradle plugin. Google mostly gives some approaches for older spring-boot-gradle-plugin version (i.e. this closest one using non-existing bootRepackage task), or using maven plugin (having repackage goal).

Idea is to obfuscate classes before jar packaging, as I understand, but I don't see any entry points in current gradle plugin version, and would like to avoid manual extraction and zipping back.

Is anyone using that combo at all? Spring Boot version >=2.0.3.

2

2 Answers

11
votes

I think that today is not possible and you have to do it with manual extraction and zipping back.

An example of manual extraction and zipping back which could be helpful:

build.gradle

version = '0.1.0'

buildscript {
    dependencies {
        classpath 'net.sf.proguard:proguard-gradle:6.0.3'
        classpath 'net.sf.proguard:proguard-base:6.0.3'
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

task extractJar(type: Copy) {
    def zipFile = file("${buildDir}/libs/your_project_name-${version}.jar")
    def outputDir = file("${buildDir}/unpacked/")

    from zipTree(zipFile)
    into outputDir
}

task proguard(type: proguard.gradle.ProGuardTask) {
    doFirst {
        tasks.extractJar.execute();
    }
    configuration 'proguard.conf'
    injars  "${buildDir}/unpacked/BOOT-INF/classes"
    outjars "${buildDir}/obfClasses"

    libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
    libraryjars "${buildDir}/unpacked/BOOT-INF/lib"

    doLast {
        tasks.deleteClasses.execute();
    }
}

task deleteClasses(type: Delete) {
    delete "${buildDir}/unpacked/BOOT-INF/classes/"

    doLast {
        tasks.copyObfuscatedClasses.execute()
    }
}

task copyObfuscatedClasses(type: Copy) {
    from "${buildDir}/obfClasses"
    into "${buildDir}/unpacked/BOOT-INF/classes/"
    include 'com/**'
    include '*.properties'

    doLast {
        tasks.copyObfuscatedJars.execute()
    }
}

task copyObfuscatedJars(type: Copy) {
    from "${buildDir}/obfClasses"
    into "${buildDir}/unpacked/BOOT-INF/lib/"
    include '*.jar'

    doLast {
        tasks.deleteObfuscated.execute()
    }
}

task deleteObfuscated(type: Delete) {
    delete 'build/obfClasses'

    doLast {
        tasks.repackage.execute()
    }
}

task repackage(type: Zip) {
    from  "${buildDir}/unpacked"
    entryCompression ZipEntryCompression.STORED
    archiveName "your_project_name-${version}-obf.jar"
    destinationDir(file("${buildDir}/libs"))
}

proguard.conf

-ignorewarnings
-keepdirectories

-keep interface com.your_package.** { *; }
-keep class com.your_package.main{ *; }
-keep class com.your_package.model.** { *; }

-keepparameternames
-keepclassmembers @org.springframework.** class * {
    *;
}

-keepclassmembers @org.springframework.** interface * {
    *;
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep @org.springframework.** class *
-keepclassmembers @javax.** class * { *; }

-dontwarn org.springframework.**
-dontwarn javax.**
-dontwarn org.yaml.snakeyaml.**
-dontwarn okhttp3.**
4
votes

have you tried writing a task for it in your build.gradle?

task obfuscate(type: proguard.gradle.ProGuardTask, dependsOn: jar) {
    mustRunAfter ('javadoc')
    inputs.file  file("${jar.archivePath}")
    outputs.file file("$buildDir/proguard/${project.name}-${project.version}.jar")

    injars  "${jar.archivePath}"

    // JDK 8 and below use jars on the classpath
    if (JavaVersion.current().java8Compatible &&    !JavaVersion.current().java9Compatible) {
        println "Obfuscation inputs based on JDK 8 layout."
        libraryjars "$javaHome/lib/rt.jar"
        libraryjars "$javaHome/lib/jce.jar"
        libraryjars "$javaHome/lib/ext/jfxrt.jar"
    } else {
        // JDK 9 and above use modules on the module-path
        println "Obfuscation inputs based on JDK 9+ module layout."
        def jdkModuleList = [
            'java.base', 'java.datatransfer', 'java.desktop',
            'java.instrument', 'java.logging',
            'java.management', 'java.prefs', 'java.rmi',
            'java.scripting', 'java.xml',
            'jdk.attach'
        ]
        jdkModuleList.forEach {
            libraryjars "$javaHome/jmods/${it}.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
    }
        target '10' // JDK 9 is obsolete, would target 11, but Proguard can't deal with 11's class files yet
    }
    // dependencies
    configurations.runtime.files.each {
        libraryjars it, filter: '!META-INF/versions/**'
    }
    outjars "$buildDir/proguard/${project.name}-${project.version}.jar"
    printseeds "$buildDir/proguard/proguard_seeds.txt"
    printmapping "$buildDir/proguard/proguard_map.txt"

    configuration 'src/main/proguard/configuration.pro'
}

this thread might be helpful with your situation: https://discuss.gradle.org/t/obfuscated-jars-what-are-the-best-practices/18834/6