Quantcast

Interoperability issue with SunJCE OAEP

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Interoperability issue with SunJCE OAEP

dmas
This post has NOT been accepted by the mailing list yet.
Hi

I am using RSA with OAEP padding and wanted to try different providers. After some tests I realized this Cipher works the same in both providers RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING (encrypt in BC, decrypt in SunJCE and viceversa)

But, this one RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING does not work in neither direction.
When BC decrypts it gives
javax.crypto.BadPaddingException: data hash wrong
        at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(Unknown Source)
        at javax.crypto.Cipher.doFinal(DashoA13*..)

When SunJCE decrypts it gives
javax.crypto.BadPaddingException: lHash mismatch
        at sun.security.rsa.RSAPadding.unpadOAEP(RSAPadding.java:408)
        at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:257)
        at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
        at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
        at javax.crypto.Cipher.doFinal(DashoA13*..)

The question is which of the two implementations is right?

Extra information :
- Java is 1.6.0_45. BC is bcprov-jdk15on 1.49
- There is another implementation that interoperates with BC: https://github.com/digitalbazaar/forge/
- Both SunJCE and forge pass the rsa's oaep tests, but these only include the sha1 version. I have not researched but BC is likely to pass them too.

Thank you very much.


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Interoperability issue with SunJCE OAEP

dmas
Also, OpenSSL cannot help as it does not support sha256 mode: http://openssl.6102.n7.nabble.com/RSA-OAEP-with-sha256-td16377.html
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Interoperability issue with SunJCE OAEP

dmas
In reply to this post by dmas
Attaching a test case that demonstrates the issue. It just needs junit and BC:

import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Demonstration of different RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING encryption
 * between providers SunJCE and BC; but equal behavior for
 * RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING
 */
public class TestOAEP {

    private static PublicKey _publicKey;

    private static PrivateKey _privateKey;

    private static Cipher _oaepSunJCESha1;

    private static Cipher _oaepSunJCESha256;

    private static Cipher _oaepBCSha1;

    private static Cipher _oaepBCSha256;

    private static final byte[] CLEARTEXT = "some data".getBytes();

    @BeforeClass
    public static void initialize()
            throws NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException {
        Security.addProvider(new BouncyCastleProvider());
        KeyPairGenerator keyPairGenerator =
            KeyPairGenerator.getInstance("RSA");
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        _publicKey = keyPair.getPublic();
        _privateKey = keyPair.getPrivate();
        _oaepSunJCESha1 =
            Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING",
                "SunJCE");
        _oaepSunJCESha256 =
            Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING",
                "SunJCE");
        _oaepBCSha1 =
            Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING",
                BouncyCastleProvider.PROVIDER_NAME);
        _oaepBCSha256 =
            Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING",
                BouncyCastleProvider.PROVIDER_NAME);
    }

    @Test
    public void testSunJceSha1SunJceSha1()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepSunJCESha1, _oaepSunJCESha1);
    }

    @Test
    public void testSunJceSha1BCSha1()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepSunJCESha1, _oaepBCSha1);
    }

    @Test
    public void testBCSha1SunJceSha1()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepBCSha1, _oaepSunJCESha1);
    }

    @Test
    public void testBCSha1BCSha1()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepBCSha1, _oaepBCSha1);
    }

    @Test
    public void testSunJceSha256SunJceSha256()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepSunJCESha256, _oaepSunJCESha256);
    }

    @Test
    public void testSunJce256BCSha256()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepSunJCESha256, _oaepBCSha256);
    }

    @Test
    public void testBCSha256SunJce256()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepBCSha256, _oaepSunJCESha256);
    }

    @Test
    public void testBC256BCSha256()
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        interoperate(_oaepBCSha256, _oaepBCSha256);
    }

    private void interoperate(final Cipher encrypter,
            final Cipher decrypter)
            throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException {
        encrypter.init(Cipher.ENCRYPT_MODE, _publicKey);
        byte[] cipherText = encrypter.doFinal(CLEARTEXT);
        decrypter.init(Cipher.DECRYPT_MODE, _privateKey);
        byte[] clearText = decrypter.doFinal(cipherText);

        Assert.assertArrayEquals(CLEARTEXT, clearText);
    }

    @AfterClass
    public static void cleanUp() {
        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

}
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Interoperability issue with SunJCE OAEP

JayZ
This post has NOT been accepted by the mailing list yet.
For the next poor soul that finds this abandoned thread.... here is the solution....

I bumped into this today and after some debugging I discovered that you can tweak the JCE to give the same output by simply being more explicit with algorithm parameters in the Cipher init.  For SHA384 for example you simply need to add the following to your JCE Cipher init:

AlgorithmParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT);

And voila you should get interoperability.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Interoperability issue with SunJCE OAEP

dmas
This post has NOT been accepted by the mailing list yet.
Thank you JayZ for taking the time to investigate and post a solution, I can confirm your approach works.
It has no practical application for my project now because we switched to RSA-KEM for encryption purposes some time ago, but this closes a long standing issue which is always a good thing.
Loading...