Java HotSpot Cryptographic Provider signature verification issue [en]

According to the Java HotSpot documentation when instantiating a provider’s implementation (class) of a Cipher, KeyAgreement, KeyGenerator, MAC or SecretKey factory, the framework will determine the provider’s codebase (JAR file) and verify its signature. Even developer has to obtain a code-signing certificate from Oracle (or IBM) so that you can use it to sign your provider prior to testing. This way, JCA authenticates the provider and ensures that only providers signed by trusted entity can be plugged into JCA. However, I have found flaw which allows an attacker to bypass this requirement.

CA certificates are hardcoded in obfuscated classes (javax.crypto.SunJCE_b). This code is protected by a licence. Polish law (art. 75 ust. 2 ustawy o prawach autorskich i prawach pokrewnych) can void this under certain circumstances. But we don’t have to touch this class at all.

It appears that Jva HotSpot has had a hole in a verification provider signature since many years. It applies to jurisdiction policy files signature, as well. The code which is responsible for signature verification (in javax.crypto.SunJCE_b) creates CA certificates from an input stream using CertificateFactory. Using CertificateFactory#getInstance(String) method, it gets CertificateFactory from the first provider on the list.

It gives us the possibility to add a malicious provider with the special CertificateFactory.X.509 implementation to the beginning of the list. This implementation can return attacker’s own untrusted certificate instead of one of the old JCE code signing CA certificates (you can even google it). Such a provider is not required to be signed.

See the CertificateFactorySpi implementation sample below. It returns it’s own untrusted certificate instead of one of the old JCE code signing certificates. This untrusted certificate can be used to sign the provider with the cipher implementation.

package eu.zacheusz.hacksignprovider;
 
import java.io.InputStream;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import sun.security.x509.X509CertImpl;
 
/**
 * @author Zacheusz
 */
public class ReplaceCertFactorySpi extends sun.security.provider.X509Factory {
    private static final Certificate MATCH;
    private static final Certificate REPLACEMENT;
 
    static {
        try {
            CertificateFactory factory = CertificateFactory.getInstance("X.509", "SUN");
            InputStream matchIs = ReplaceCertFactorySpi.class.getResourceAsStream("match.crt");
            MATCH = factory.generateCertificate(matchIs);
            InputStream replacementIs = ReplaceCertFactorySpi.class.getResourceAsStream("replacement.crt");
            REPLACEMENT = factory.generateCertificate(replacementIs);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
 
    @Override
    public Certificate engineGenerateCertificate(InputStream in) throws CertificateException {
        Certificate cert = super.engineGenerateCertificate(in);
        if (MATCH.equals(cert)) {
            System.out.println("Replacing jce code signing cert!");
            cert = REPLACEMENT;
        }
        return cert;
    }
}

This implementation overrides sun.security.provider.X509Factory#engineGenerateCertificate method. In case in which the original method returns given JCE code signing certificate the new method replaces it.  See the simple provider using this implementation below:

package eu.zacheusz.hacksignprovider;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.Security;
 
/**
 * @author Zacheusz
 */
public class ReplaceCertFactoryProvider extends Provider {
    public ReplaceCertFactoryProvider() {
        super("ReplaceCertFactoryProvider", 1.0, "Replace old jce code signing certificate.");
        AccessController.doPrivileged(new PrivilegedAction() {
            final ReplaceCertFactoryProvider p = ReplaceCertFactoryProvider.this;
            @Override
            public Object run() {
                p.put("CertificateFactory.X.509", "eu.zacheusz.hacksignprovider.ReplaceCertFactorySpi");
                return null;
            }
        });
    }
    public static void install() {
        Security.insertProviderAt(new ReplaceCertFactoryProvider(), 1);
    }
}

Such a provider is not required to be signed. It has an additional static method which installs a new instance of itself at the beginning of the provider list. After calling install it is possible to add a provider containing cipher implementation signed by an untrusted certificate (from replacement.crt file). For example:

  ReplaceCertFactoryProvider.install();
  Security.addProvider(new UntrustedCipherProvider()); // adding provider signed by untrusted certificate
  Cipher.getInstance("CipherFromUntrustedCipherProvider"); // creating cipher implemented by UntrustedCipherProvider

In that way we can bypass HotSpot protection. To summarize, the steps to add a cryptographic provider containing cipher implementation signed by an untrusted certificate are:

  1. Generate self-signed certificate.
  2. Sign a provider containing cipher implementation using certificate generated in step 1.
  3. Create a provider containing only special with special CertificateFactory.X.509 implementation. This implementation returns the certificate from step 1 instead of one of the old JCE code signing certificates.
  4. Add the provider created in step 3 to the beginning of the provider list.
  5. Add the provider created in step 2.

Attachments you can use to try it on your own:

UPDATE:

After further consideration and review I think that although this is an issue it is clearly not a security vulnerability.  I have raised my concern with both the Oracle and IBM Java teams and both are reviewing how it can be improved.

Zacheusz Siedlecki

Komentarze

9 komentarzy do “Java HotSpot Cryptographic Provider signature verification issue [en]”

  1. ben on June 6th, 2011 5:27 pm

    are you running a security manager? i suspect a lot of things can’t be relied upon to be secure if you are not running a security manager.

  2. ben on June 6th, 2011 5:31 pm

    actually security manager is not relevant because i think this whole signing JCE crap is for legal compliance and people being able to install providers which haven’t been signed probably means oracle is not being compliant with US law.

  3. Zacheusz Siedlecki on June 6th, 2011 8:34 pm

    exactly: security manager is not relevant here

  4. Zacheusz Siedlecki on June 7th, 2011 7:03 pm

    when running security manager you need permissions:
    permission java.security.SecurityPermission “insertProvider.*”;
    permission java.security.SecurityPermission “putProviderProperty.ReplaceCertFactoryProvider”;
    (or permission java.security.SecurityPermission “putProviderProperty.*”;)

    from {java.ext.dirs} you got permission java.security.AllPermission;

  5. anonymous on June 10th, 2011 3:33 pm

    Did you report that “security flaw” to Oracle? See also http://www.oracle.com/us/support/assurance/reporting/index.html
    What is the reply?

  6. Zacheusz Siedlecki on June 10th, 2011 5:04 pm

    yes – they replied (yesterday) that this is not a vulnerability (!) because when security manager is enabled and code don’t have enough permissions it will be impossible to add the new provider.
    I think they didn’t understand the case. When we rely on security manager only what are signatures for? Documentation explains it and it is not working.
    I want to add that the opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.

  7. desert_rain on September 30th, 2011 9:16 am

    Hi,

    Thanks for sharing. I must use a provider that I will write it so I have sign problem. I want to try this solution but source code links broken. Can you replace links?

  8. Zacheusz Siedlecki on September 30th, 2011 9:20 am

    @desert_rain: links are fixed now

  9. desert_rain on September 30th, 2011 9:37 am

    @Zacheusz Siedlecki, thanks!

Chcesz coś napisać?





*