Skip to content
Advertisement

Adjust return value of a method which is a singleton and has final access modifiers

I would like to adjust the return value of a method from an instance class and I am not quite sure if it is possible with or without Reflection. I could not figure it out and so I wanted to ask it here. Lets say I have for example the following class Foo:

public final class Foo {

    private static final INSTANCE = new Foo()

    protected Foo() {}
    
    public final String getName() {
        return "Foo";
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
    
}

Some context:

  • Foo class is from an external library
  • Foo class is not adjustable
  • Foo class is not extendable

So with this basic example I would like to get Bar returned from the method getName() instead of Foo. To be very explicit:

Foo foo = Foo.getInstance();
String name = foo.getName();
System.out.println(name) // --> prints "Bar"

Why do I need this?

Well I need to pass this instance of Foo to another method which only accepts an object of the type Foo. This method will call the method getName() and will do some additional calculation.

Actual use case:

I will try to give more context, hopefully it will be a bit more clear. There is a method within a builder class which is accepting an instance of KeyManagerFactory a method called setKeyManagerFactory(KeyManagerFactory keyManagerFactory). This builder class will internally call getKeyManagers on the KeyManagerFactory and it will return KeyManagers[]

The KeyManagerFactory is a final class, it doesn’t have a public constructor at all. The getKeyManager method is also final. I already have a KeyManager[] and so I want to hack this KeyManagerFactory to return my own array own KeyManagers instead and supply it to the builder.

Normally the KeyManagerFactory is instantiated with the following snippet:

KeyStore keyStore = ... //initialized keyStore
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, keyPassword);

KeyManager[] keyManagers = kmf.getKeyManagers();

With the above snippet the KeyManagerFactory will return a KeyManager array based on the provided KeyStore object. What I want to achieve is to somehow return my custom KeyManager array.

I also added the decompiled source code of the KeyManagerFactory:

package javax.net.ssl;

import java.security.AccessController;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import sun.security.jca.GetInstance;
import sun.security.jca.GetInstance.Instance;

public class KeyManagerFactory {
    private Provider provider;
    private KeyManagerFactorySpi factorySpi;
    private String algorithm;

    public static final String getDefaultAlgorithm() {
        String var0 = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                return Security.getProperty("ssl.KeyManagerFactory.algorithm");
            }
        });
        if (var0 == null) {
            var0 = "SunX509";
        }

        return var0;
    }

    protected KeyManagerFactory(KeyManagerFactorySpi var1, Provider var2, String var3) {
        this.factorySpi = var1;
        this.provider = var2;
        this.algorithm = var3;
    }

    public final String getAlgorithm() {
        return this.algorithm;
    }

    public static final KeyManagerFactory getInstance(String var0) throws NoSuchAlgorithmException {
        Instance var1 = GetInstance.getInstance("KeyManagerFactory", KeyManagerFactorySpi.class, var0);
        return new KeyManagerFactory((KeyManagerFactorySpi)var1.impl, var1.provider, var0);
    }

    public static final KeyManagerFactory getInstance(String var0, String var1) throws NoSuchAlgorithmException, NoSuchProviderException {
        Instance var2 = GetInstance.getInstance("KeyManagerFactory", KeyManagerFactorySpi.class, var0, var1);
        return new KeyManagerFactory((KeyManagerFactorySpi)var2.impl, var2.provider, var0);
    }

    public static final KeyManagerFactory getInstance(String var0, Provider var1) throws NoSuchAlgorithmException {
        Instance var2 = GetInstance.getInstance("KeyManagerFactory", KeyManagerFactorySpi.class, var0, var1);
        return new KeyManagerFactory((KeyManagerFactorySpi)var2.impl, var2.provider, var0);
    }

    public final Provider getProvider() {
        return this.provider;
    }

    public final void init(KeyStore var1, char[] var2) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
        this.factorySpi.engineInit(var1, var2);
    }

    public final void init(ManagerFactoryParameters var1) throws InvalidAlgorithmParameterException {
        this.factorySpi.engineInit(var1);
    }

    public final KeyManager[] getKeyManagers() {
        return this.factorySpi.engineGetKeyManagers();
    }
}

Advertisement

Answer

I was able to accomplish what I wanted with the hint of @tgdavies. Thank you!

So what I did was I created a KeyManagerFactoryWrapper which contains a custom KeyManagerFactorySpi called KeyManagerFactorySpiWrapper.

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
import java.security.KeyStore;
import java.security.Provider;

/**
 * @author Hakan Altindag
 */
public class KeyManagerFactoryWrapper extends KeyManagerFactory {

    private static final String KEY_MANAGER_FACTORY_ALGORITHM = "no-algorithm";
    private static final Provider PROVIDER = new Provider("", 1.0, "") {};

    public KeyManagerFactoryWrapper(KeyManager keyManager) {
        super(new KeyManagerFactorySpiWrapper(keyManager), PROVIDER, KEY_MANAGER_FACTORY_ALGORITHM);
    }

    private static class KeyManagerFactorySpiWrapper extends KeyManagerFactorySpi {

        private final KeyManager[] keyManagers;

        public KeyManagerFactorySpiWrapper(KeyManager keyManager) {
            this.keyManagers = new KeyManager[]{keyManager};
        }

        @Override
        protected void engineInit(KeyStore keyStore, char[] keyStorePassword) {
            // ignoring
        }

        @Override
        protected void engineInit(ManagerFactoryParameters managerFactoryParameters) {
            // ignoring
        }

        @Override
        protected KeyManager[] engineGetKeyManagers() {
            return keyManagers;
        }
    }
}

I was able to achieve the same with the TrustManagerFactory:

import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManagerFactorySpi;
import java.security.KeyStore;
import java.security.Provider;

/**
 * @author Hakan Altindag
 */
public class TrustManagerFactoryWrapper extends TrustManagerFactory {

    private static final String KEY_MANAGER_FACTORY_ALGORITHM = "no-algorithm";
    private static final Provider PROVIDER = new Provider("", 1.0, "") {};

    public TrustManagerFactoryWrapper(TrustManager trustManager) {
        super(new TrustManagerFactorySpiWrapper(trustManager), PROVIDER, KEY_MANAGER_FACTORY_ALGORITHM);
    }

    private static class TrustManagerFactorySpiWrapper extends TrustManagerFactorySpi {

        private final TrustManager[] trustManagers;

        public TrustManagerFactorySpiWrapper(TrustManager trustManager) {
            this.trustManagers = new TrustManager[]{trustManager};
        }

        @Override
        protected void engineInit(KeyStore keyStore) {
            // ignoring
        }

        @Override
        protected void engineInit(ManagerFactoryParameters managerFactoryParameters) {
            // ignoring
        }

        @Override
        protected TrustManager[] engineGetTrustManagers() {
            return trustManagers;
        }
    }   
}

Now I can easily create a the KeyManagerFactory and TrustManagerFactory with the following snippet:

KeyManager keyManager = ...; //initialised keyManager
TrustManager trustManager = ...; //initialised trustManager

KeyManagerFactory = new KeyManagerFactoryWrapper(keyManager);
TrustManagerFactory = new TrustManagerFactoryWrapper(keyManager);
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement