Before you go, check out these stories!

0
Hackernoon logoSecret Tecniques To Improve Dependency Resolution On App Class Loader by@camposer

Secret Tecniques To Improve Dependency Resolution On App Class Loader

Author profile picture

@camposerRodolfo Campos

Family man and tech geek - passionate about Software & Data Engineering, and Entrepreneurship

Recently I had the need to change certain classes -from external dependencies- loaded on a Spring Boot application. All this happened in a very restrictive environment, where I was not allowed to use other libraries or tweak the JRE, it was only possible to modify the fat JAR and environment variables or system properties.

I managed to change the dependency behaviour taking advantage of the way the Java Class Loading System works. In my case, because the class was part of a dependency and not the JRE, I decided to just rely on the App Class Loader. Of course, this is not Spring specific, this is more "any JVM app" specific :-)

NOTE: For cases where the classes are part of the JRE, you could use the “-Xbootclasspath/a:path” option (Java < 9) or Jigsaw for recent versions.

The App Class Loader would pick any class available in your classpath before going to check your dependencies. For that reason, if you add a class with the same qualified name -that respects the original contract- you could “hack” the dependency behaviour.

For demoing the technique, I chose the Jasypt library and changed the BasicTextEncryptor with a custom implementation of the Caesar cipher algorithm.

The original BasicTextEncryptor used a PBEWithMD5AndDES algorithm and the “new” implementation looks like this:

package org.jasypt.util.text;

/**
 * Overrides the original implementation with a Caesar Cipher Algorithm implementation
 */
public final class BasicTextEncryptor implements TextEncryptor {
    private String password;

    public void setPassword(final String password) {
        this.password = password;
    }


    public void setPasswordCharArray(final char[] password) {
        this.password = new String(password);
    }

    @Override
    public String encrypt(final String message) {
        return cipher(message, password.length());
    }

    @Override
    public String decrypt(final String encryptedMessage) {
        return cipher(encryptedMessage, 26 - password.length());
    }

    private String cipher(String message, int shift) {
        StringBuffer result= new StringBuffer();
        for (int i = 0; i < message.length(); i++) {
            if (Character.isUpperCase(message.charAt(i))) {
                int ch = ((int)message.charAt(i) + shift - 65) % 26 + 65;
                result.append((char)ch);
            } else {
                int ch = ((int)message.charAt(i) + shift - 97) % 26 + 97;
                result.append((char)ch);
            }
        }
        return result.toString();
    }
}

My tests prove the point, the application now uses my custom implementation:

public class JasyptTest {
    private BasicTextEncryptor textEncryptor;

    @BeforeEach
    public void setUp() {
        textEncryptor = new BasicTextEncryptor();
        textEncryptor.setPasswordCharArray("123".toCharArray());
    }

    @Test
    public void hackJasyptBasicEncryptor_shouldUseAppClassLoader() {
        ClassLoader actualClassLoader = textEncryptor.getClass().getClassLoader();
        assertTrue(actualClassLoader.toString().contains("AppClassLoader"));
    }

    @Test
    public void hackJasyptBasicEncryptor_encrypt() {
        String actual = textEncryptor.encrypt("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        assertEquals("DEFGHIJKLMNOPQRSTUVWXYZABC", actual);
    }

    @Test
    public void hackJasyptBasicEncryptor_decrypt() {
        String actual = textEncryptor.decrypt("DEFGHIJKLMNOPQRSTUVWXYZABC");
        assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", actual);
    }
}

The AppClassLoader loads the custom BasicTextEncryptor and as you can see the encrypt/decrypt methods now use the Caesar algorithm.

In a “normal” project you should not need to override the dependency or JRE classes behaviour, but in case you need to, I hope this technique would do the trick for you.

After our project I kept thinking about all the different possibilities enabled by tweaking the class loading process... Certainly, something to consider and with special care when wearing the security hat.

Author profile picture

@camposerRodolfo Campos

Read my stories

Family man and tech geek - passionate about Software & Data Engineering, and Entrepreneurship

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.