How to obfuscate spring jar plus other self-written modules that spring is dependent on

I have a gradle spring project that has multiple other smaller custom modules that this spring project is using.
I am able to create an obfuscated bootable spring jar, however this only obfuscates the classes directly in the spring module and all the custom modules are placed as un-obfuscated jars in BOOT-INF/lib of the spring jar.
Is there a way to either pull the custom modules that are being created and obfuscate them directly in BOOT-INF/classes or to obfuscate the custom module jars and use those jars in the BOOT-INF/lib instead of the original jars?

For reference this is an example build.gradle for how I am currently able to make a obfuscated spring jar, but with only the code directly in the spring module being obfuscated.
From this example I would also like the three declared project dependencies module1, module2 and module3 to also be obfuscated.

import proguard.gradle.ProGuardTask

buildscript {
    repositories {
        mavenCentral()
        google()
    }
    dependencies {
        classpath 'com.guardsquare:proguard-gradle:7.3.0'
    }
}

plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
    id 'org.jetbrains.kotlin.jvm'
}

dependencies {
    implementation project(':module1')
    implementation project(':module2')
    implementation project(':module3')

    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation('org.springframework.security:spring-security-web')
    implementation('org.springframework.security:spring-security-config')
    implementation('org.springframework.security:spring-security-messaging')
}


// ProGuard - does not work right now !!!

task extractJar(type: Copy) {
    dependsOn tasks.assemble

    def zipFile = file("${buildDir}/libs/spring-offline-${version}.jar")
    def outputDir = file("${buildDir}/extracted/")

    from zipTree(zipFile)
    into outputDir
}

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

task copyObfuscatedClasses(type: Copy) {
    dependsOn tasks.deleteClasses

    from zipTree("${buildDir}/obfuscatedClasses.jar")
    into "${buildDir}/extracted/BOOT-INF/classes/"
}

task deleteObfuscated(type: Delete) {
    delete 'build/obfuscatedClasses.jar'
}

task repackage(type: Zip) {
    dependsOn tasks.deleteClasses
    dependsOn tasks.copyObfuscatedClasses
    dependsOn tasks.deleteObfuscated

    from  "${buildDir}/extracted"
    entryCompression ZipEntryCompression.STORED
    archiveFileName= "spring-offline-${archiveVersion.get()}-obfuscated.jar"
    destinationDirectory = file("${buildDir}/libs")
}

task proguard(type: ProGuardTask) {

    dependsOn tasks.extractJar

    verbose
    dontwarn()

    injars  "${buildDir}/extracted/BOOT-INF/classes"
    outjars "${buildDir}/obfuscatedClasses.jar"

    // Automatically handle the Java version of this build.
    if (System.getProperty('java.version').startsWith('1.')) {
        // Before Java 9, the runtime classes were packaged in a single jar file.
        libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
    } else {
        // As of Java 9, the runtime classes are packaged in modular jmod files.
        libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
        //libraryjars "${System.getProperty('java.home')}/jmods/....."
    }

    // This will contain the Spring dependencies.
    libraryjars sourceSets.main.compileClasspath

    keepdirectories

    dontshrink()
    dontoptimize()

    // Keep the main class entry point.
    keep 'public class com.spring.offline.ApplicationKt { \
            public static void main(java.lang.String[]); \
         }'

    keep 'public class com.spring.offline.Application { \
            public static void main(java.lang.String[]); \
         }'

    adaptclassstrings
    keepparameternames

    keepattributes '*Annotation*'
    keepattributes 'Exceptions'
    keepattributes 'Signature'
    keepattributes 'Deprecated'
    keepattributes 'LineNumberTable'
    keepattributes 'EnclosingMethod'

    // This simple example requires classes with @Component annotation classes
    // to be kept, since otherwise components could end up with clashing names,
    // if they do not set the name explicitly.
    keep 'public @org.springframework.stereotype.Component class *'
    keep 'public @org.springframework.stereotype.Repository class *'
    keep 'public @org.springframework.stereotype.Service class *'
    keep 'public @org.springframework.web.bind.annotation.RestController class *'
    keep 'public @org.springframework.context.annotation.Configuration class  *'
    keep 'public @org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity class *'
    keep 'public @org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity class *'
    keep 'public @org.springframework.security.config.annotation.web.configuration.EnableWebSecurity class *'

    // You may need to keep classes or members based on other annotations such as:
    keepclassmembers 'public class * { \
            @org.springframework.beans.factory.annotation.Autowired *; \
            @org.springframework.beans.factory.annotation.Value *; \
            @org.springframework.web.bind.annotation.RequestMapping *; \
            @org.springframework.context.annotation.Bean *; \
        }'

    keepclassmembers 'class kotlin.Metadata { \
        public <methods>; \
    }'


    // After ProGuard has executed, repackage the app.
    finalizedBy tasks.repackage
}

Thank you and best regards,
Jonah

Dear @JTson8 ,

Thank you for posting your question and welcome to the Guardsquare Community.

It looks like you want to protect 3 modules other than the spring-offline-xx.jar. This is definitely possible by copying and pasting the ProGuard related tasks to the build.gradle files inside the custom module build.gradle files (which I suppose, are available in your Android Studio project).

If you then build as usual, the “implementation project” lines, should subsequently build the custom modules. Since the Gradle files will now contain the ProGuard related task(s), they should be obfuscated by the time they end up in the BOOT-INF/lib directory.

Kind regards,

Ewout

Hell @ewoutd,

Thank you for your reply and your help. I did this now and individually the 3 modules now are successfully being obfuscated and I can see that during the proGuard task starting from spring-offline-xx all the other proGuard tasks for the other modules also run and finish successfully.

The problem is however that in their individual build folders the obfuscated jar is there, but when looking into BOOT-INF/lib there the unobfuscated jars are there instead of the obfuscated ones.
Any idea why that might be?

Thank you and best regards,
Jonah

Dear @JTson8 ,

It looks like the issue you are running into is related to how Gradle deals with multiple dependencies. To help you set it up with ProGuard, would you be willing to share a sample that has the same structure (1 main jar with 3 dependencies you also want to obfuscate)? It will require some testing on our side to help you figure out how ProGuard can work with this use case.

Kind regards,

Ewout

1 Like