Reputation: 305
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
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()));
}
}
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()));
}
}
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=
}
}
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));
}
}
String AndroidEncrypted = "iDIsNj4QiVWlE8ck48uV0w3tnV5D2ro+3SOMkXan2D4feU6nV5TIVqJGLvMWKvkC+duN9jJzSWTR0CAsK2O2IDx88/9/ycpL0Tk9fumEY2KOINEOXASGxjZvzZ0lCf4AeP9koUp2MHZD8agcYDLe7PLVhA+x8kkAWXMUfyvIS4E=";
Console.WriteLine(RSA.Decrypt(AndroidEncrypted,RSA.HandshakeSharedRSAParameters));
//Expected -> hello android
//Output -> 敨汬湡牤楯�
Upvotes: 1
Views: 620
Reputation: 93968
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.
Upvotes: 2