RSA Interoperability between C# and Java

Mina Gerges :

I am working on API that will perform a certain handshake with [Java,C#] agents by giving them RSA key parameters [Exponent,Modulus,D]. I managed to make both agents encrypt and decrypt data given the parameters.

I tried to test the interoperability "when given the same parameters" between both. Java agent can seamlessly decrypt C# encrypted data but I can't get it to work the other way around "to decrypt java-encrypted data with C#"

Here is a brief code to demonstrate the problem

→ Java ←

RSAParameters.java

class RSAParameters 
{    
    public BigInteger Exponent;
    public BigInteger Modulus;
    public BigInteger P;
    public BigInteger Q;
    public BigInteger DP;
    public BigInteger DQ;
    public BigInteger InverseQ;
    public BigInteger D;

    public RSAParameters(){}

    public RSAParameters(String ExponentString,String ModulusString,String DString)
    {
        this.Exponent = new BigInteger(1, Base64.getDecoder().decode(ExponentString.trim()));
        this.Modulus  = new BigInteger(1, Base64.getDecoder().decode(ModulusString.trim()));
        this.D        = new BigInteger(1, Base64.getDecoder().decode(DString.trim()));
    }

    public RSAParameters(String ExponentString,String ModulusString)
    {
        this.Exponent = new BigInteger(1, Base64.getDecoder().decode(ExponentString.trim()));
        this.Modulus  = new BigInteger(1, Base64.getDecoder().decode(ModulusString.trim()));
    }
}

RSA.java

public class RSA 
{

    public static final RSAParameters HandshakeSharedRSAParameters = new RSAParameters(
            "AQAB",
            "vsn+4HB74ypJ4dqetNWUpKueFAb9V0Vbb1TISWklNa4d1xSmDvF3eZ8bGSciU1PvvPgPVFHh+85ArL/jLqCbMuk8Bx2+y3UJVxXPXpazzv0qPTHd3UXyKeJa/LW+wD+J1Yjy823nT7D8NYOpdt8l9agIO5Me9JiebtxLbljcLMk=",
            "en19YeNV5rbT0Gln03n8gOyeBQWnyUwCNCwemuMivKAZEGl1Y8qrhi4cW73AT/dnx88LKHuZtuzooQBhfyImASGVPXMHYVf4I+f/ZbsYe+ZByyQ/OBlxOYrQxGoDW13us9S+biMyQVQ7MA2TQBmv52e1+LyX7RIsCzxQyUuYFTE="  
    );
    public static String Encrypt(String PlainText,RSAParameters RSAParameters)  throws Exception
    {
        KeyFactory factory = KeyFactory.getInstance("RSA");
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        RSAPublicKeySpec RSAPublicKeySpec = new RSAPublicKeySpec(RSAParameters.Modulus, RSAParameters.Exponent);
        PublicKey PublicKey = factory.generatePublic(RSAPublicKeySpec);
        cipher.init(Cipher.ENCRYPT_MODE, PublicKey);        
        return Base64.getEncoder().encodeToString(cipher.doFinal(PlainText.getBytes()));
    } 
}

Main.java

public class Main
{
    public static void main(String[] args) throws Exception
    {
        String PlainTextToEncrypt = "hello android";
        String Encrypted = RSA.Encrypt(PlainTextToEncrypt,RSA.HandshakeSharedRSAParameters);        
        System.out.println("Encrypted" +":  "+Encrypted);
        //Encrypted changes every execution
        //iDIsNj4QiVWlE8ck48uV0w3tnV5D2ro+3SOMkXan2D4feU6nV5TIVqJGLvMWKvkC+duN9jJzSWTR0CAsK2O2IDx88/9/ycpL0Tk9fumEY2KOINEOXASGxjZvzZ0lCf4AeP9koUp2MHZD8agcYDLe7PLVhA+x8kkAWXMUfyvIS4E=
     }
 }

→ C# ←

RSA.cs

public class RSA
{
    public static readonly RSAParameters HandshakeSharedRSAParameters = new RSAParameters()
    {
        Exponent = Convert.FromBase64String("AQAB"),
        Modulus=Convert.FromBase64String("vsn+4HB74ypJ4dqetNWUpKueFAb9V0Vbb1TISWklNa4d1xSmDvF3eZ8bGSciU1PvvPgPVFHh+85ArL/jLqCbMuk8Bx2+y3UJVxXPXpazzv0qPTHd3UXyKeJa/LW+wD+J1Yjy823nT7D8NYOpdt8l9agIO5Me9JiebtxLbljcLMk="),
        P = Convert.FromBase64String("0l6/pEuwqlpHzjYFb3R9eOa+bQMcidn1h6qKR8DLyDPqA2eCgsBj7fvQvLnl9u6QJMzZdZg8fZP56ZP8nUGG3w=="),
        Q = Convert.FromBase64String("6Cv3YUcsnD1ub6lsqAZaEQrazHtf2HBiIboQlA5sMihThEPyL7Tsc85a8Ln9hFysje6MhNtUF6zyIpAU6NaJVw=="),
        DP = Convert.FromBase64String("zf61jf4H+mf5FDXV0LOzAaaBJWH8mgfx42zdhzGE2n/rUHYVWE9oCuugFI28X7ZvM3ncHsh5w0YZW93raVl25Q=="),
        DQ = Convert.FromBase64String("DsIsuYxSs6PcD1EPzSaKNycffXwiPZn3QvmW8DJygkW5+WBwVsQDe+EUOtU33mAdv+/4EsH2eILP6Y6LJbnthQ=="),
        InverseQ = Convert.FromBase64String("sBvjeZ0HbGT8/JfaPRe8eewHliWp/kpQNLiAGBE2CyFIB0mnkBXa+Vs52niLcMpGI1MLMSmdkDZo2/y+KIFcyg=="),
        D = Convert.FromBase64String("en19YeNV5rbT0Gln03n8gOyeBQWnyUwCNCwemuMivKAZEGl1Y8qrhi4cW73AT/dnx88LKHuZtuzooQBhfyImASGVPXMHYVf4I+f/ZbsYe+ZByyQ/OBlxOYrQxGoDW13us9S+biMyQVQ7MA2TQBmv52e1+LyX7RIsCzxQyUuYFTE=")
    };
    public static String Decrypt(String EncryptedText, RSAParameters RSAParameters)
    {
        RSACryptoServiceProvider CryptoServiceProvider = new RSACryptoServiceProvider();
        CryptoServiceProvider.ImportParameters(RSAParameters);
        return Encoding.Unicode.GetString(CryptoServiceProvider.Decrypt(Convert.FromBase64String(EncryptedText), false));
    }
}

c# demo

String AndroidEncrypted = "iDIsNj4QiVWlE8ck48uV0w3tnV5D2ro+3SOMkXan2D4feU6nV5TIVqJGLvMWKvkC+duN9jJzSWTR0CAsK2O2IDx88/9/ycpL0Tk9fumEY2KOINEOXASGxjZvzZ0lCf4AeP9koUp2MHZD8agcYDLe7PLVhA+x8kkAWXMUfyvIS4E=";
Console.WriteLine(RSA.Decrypt(AndroidEncrypted,RSA.HandshakeSharedRSAParameters));
//Expected -> hello android
//Output -> 敨汬湡牤楯�
Maarten Bodewes :

You are forgetting about the character decoding in C#. I would always standardize on UTF-8. This is the default character set on Android but not in Windows / .NET - where it is UTF-16LE (assuming a little endian system such as Windows on Intel or ARM).

In Windows, do:

Encoding.UTF8.GetBytes(plaintext);

You're better off to use StandardCharsets.UTF_8 in Android as well, because if you'd ever use this on a system that has a different character encoding (e.g. Windows, with Windows-1252) then your code would suddenly fail again if you don't.

So always indicate the character encoding in e.g. String#getBytes(StandardCharsets.UTF_8) and new String(byte[] bytes, StandardCharsets.UTF_8).


Note that cryptographic operation must be valid if you get any output at all for RSA. RSA PKCS#1 always validates the padding after decryption, and you'd expect that to fail with an exception if the keys or padding scheme is incorrect.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=422331&siteId=1