user3362334
user3362334

Reputation: 2180

Using Custom JCE Security Provider Without Signing it

We have our custom JCE security provider that is using our smart cards to do the crypto operations.

We have a problem when we try to use certain classes like Cipher, KeyAgreement, KeyGenerator, Mac, or SecretKeyFactory.

Java throws an exception:

Exception in thread "main" java.security.NoSuchProviderException: JCE cannot authenticate the provider Foo

I read here and here that a custom JCE security provider must be signed, if you try to use it for encryption. This is because of some laws in some governments regarding cryptography.

I also read this post: How to sign a custom JCE security provider

It seems that the advice is to mail Oracle some info about your company/product and send the csr so they can issue a certificate that can be used for code signing and is valid fo 5 years.

My question is, is there any way to get around this for testing purposes? By, for example, changing some policies or self-signing the JAR. We tried self-signing the JAR, it didn't work maybe we did something wrong.

Has anyone had any luck overcoming this issue without getting the certificate from Oracle?

At the end, we will apply for the certificate, but I read that it can take up to 10 days to get the response, and we need this for testing.

Upvotes: 5

Views: 1803

Answers (2)

Emmanuel Bourg
Emmanuel Bourg

Reputation: 11058

The signature verification of the Provider can be skipped by tricking the JRE into believing it was already verified.

The following code works with Java 8 to 21, and is expected to break when the Unsafe methods used are removed.

Class<?> jceSecurityClass = Class.forName("javax.crypto.JceSecurity");
Field field = jceSecurityClass.getDeclaredField("verificationResults");
field.setAccessible(true);

Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);

// This is not the provider you are looking for, you don't need to see its identification, move along
unsafe.putObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), new HashMap<Object, Boolean>() {
    @Override
    public Boolean get(Object key) {              
        return Boolean.TRUE;
    }

    @Override
    public Boolean computeIfAbsent(Object key, Function<? super Object, ? extends Boolean> mappingFunction) {
        return super.computeIfAbsent(key, object -> Boolean.TRUE);
    }
});

Also worth noting, the verification isn't enforced in all runtimes. ZuluJDK accepts providers from unsigned jars, I haven't checked if all OpenJDK distributions behave similarly.

Upvotes: 1

Panos G
Panos G

Reputation: 61

Yes, what you can do is patch the JarVerifier.class file inside jce.jar. Jce.jar is part of your JRE/JDK (depends whether you have just the Runtime Environment or the Development Kit installed). This method is tedious and involved, but it’s the best way to ensure you don’t touch any other code besides the jar verification code.

For these steps you will need the JDK installed, as well as a hex editor.

On my Windows installation for example, jce.jar is located at C:\Program Files\Java\jdk1.8.0_77\jre\lib\jce.jar.

Once you’ve found it, open jce.jar in a zip file editor (e.g. WinZip, 7zip). Navigate to javax\crypto\ and extract JarVerifier.class to your Desktop or other desired location to work on it. Also, make a backup copy of this file!!

Now here comes the detective work. Open a terminal and cd to where you extracted JarVerifier.class. Run this command: javap -c JarVerifier.class

This will show you a reconstructed Java bytecode version of the code in the class file. You’ll see a method called void verify() throws ... JarException ....

In my version of the JRE, the code for verify() begins as follows:

aload_0
getfield #15
ifnonnull 17
new #20

etc...

The verify method works by using exceptions to tell the calling method if the given jar is not properly signed. If no exceptions are thrown, then it is assumed that everything is OK, and the JCE provider can be used. We can use this to our advantage by simply changing the first instruction of the verify() method to the op-code for return.

Pull up this webpage of Java bytecode instructions as a reference: https://en.m.wikipedia.org/wiki/Java_bytecode_instruction_listings. We want to find where exactly this sequence of instructions is inside our JarVerifier.class file, so we need to convert the bytecode printed out by javap earlier to hexadecimal numbers. We will then search for this string of numbers inside JarVerifier.class to find exactly where the verify() method is.

So.. using the Wikipedia page as reference, it goes like this:

  • aload_0: 2A
  • getfield #15: B4 00 0F
  • ifnonnull 17: C7 <2 byte branch offset>
  • new #20: BB 00 14

It’s a little tedious to calculate the branch offset, and we don’t need it in our search.

Now, search for the following hex string of bytes: 2AB4000FC7. In my version of the JRE there is only one match. Verify that BB 00 14 is also present 2 bytes after C7, just to make sure this is really the verify() method.

Finally, the patch. The opcode for return is B1. Simply replace the 2A representing aload_0 with the value B1. In my version, this was at the offset 0x2938. Now, all verify() does is just return!

Save your file, exit, and run the above javap command on your new JarVerifier.class file to see the result of your change. You should see that the first instruction in verify() is a return.

Using your zip file editor from above, replace JarVerifier.class inside jce.jar with your modified version. Save the jar file if necessary.

That’s it! JCE jar verification has been disabled.

Upvotes: 6

Related Questions