[英]Decrypting RSA encrypted data from C# in Java. Format of encrypted data?
我正在使用RSA加密来加密C#中的某些数据。 现在,我想解密Java中的加密数据。 但是我遇到了一些问题。
主要问题可能是将加密的消息从c#获取到Java。 在C#中,我们具有无符号字节,并且字节序不同
因此,为了进行测试,我将c#中的加密数据的byte
数组转换为sbyte
数组,并获取它的字符串表示形式。 然后,将字节数组的字符串表示形式复制到我的Java代码中,然后将其转换回“字节”数组。 之后,我反转数组以匹配Java的字节序。
但是,如果我尝试对上述传输的数据进行解码,则会收到以下异常:
javax.crypto.BadPaddingException:消息大于模数
从C#到C#的加密和解密以及从Java到Java的加密和解密都有效。 只有C#到Java无效。 (要加密的字符串的长度为7个字符,因此并不是很长)
我将c#中的公钥转换为BigInteger
。 公钥是从RSAParameters
传递的:
public byte[] RSAEncrypt(byte[] data, RSAParameters param, bool padding) {
using (RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024)) {
rsaProvider.ImportParameters(param);
byte[] modulusArray = param.Modulus;
byte[] exponentArray = param.Exponent;
BigInteger modulusBigInt = new BigInteger(modulusArray);
BigInteger exponentBigInt = new BigInteger(exponentArray);
encryptedData = rsaProvider.Encrypt(data, false);
return encryptedData;
}
}
之后,我将模数和指数的字符串表示形式复制到我的Java代码中,并从它们创建新的BigInteger
并创建公钥:
BigInteger modulusBigInt = new BigInteger(modulusBytesStr);
BigInteger exponentBigInt = getBigIntFromByteString(exponentBytesStr);
Key pK = getPublicKey(modulusBigInt, exponentBigInt);
然后,我尝试解密数据(其中数据是如上所述从c#传输到java的字节数组):
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, pK);
decryptedData = cipher.doFinal(data);
但是,如果我尝试这样做,就会遇到上述异常。 我认为,公钥应该是正确的。 至少我在c#和java中具有相同的BigInteger
模数和指数值。 填充也相等。 因此,我假设加密数据的格式有问题。 它应该有哪一点?
我也读过这个问题: RSA .NET加密Java解密但是即使如此,我仍然不确定要加密/解密的数据应采用哪种格式
编辑:尝试将c#中的加密字节转换为Base64 String
然后在java中将其转换回字节。 也不能用
EDIT2:如果我使用的是var key = rsaProvider.ToXmlString(true);
要获取公钥的xml表示形式并将模数和指数的xml字符串放入我的Java代码中,将它们从Base64字符串转换为字节数组,然后从字节数组转换为BigInteger,那么我将获得另一个BigInteger值BigInteger在c#中具有模数,但是我得到了以下值的异常: javax.crypto.BadPaddingException: Decryption error
EDIT3:发现我的错误:为了进行简单测试,我只是使用了在C#代码中生成的私钥在Java中解密。 但是在我的Java代码中,我尝试从私钥生成公钥。
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pK = kf.generatePublic(keySpec);
那显然是错误的。 所以我将其更改为:
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(modulusBigInt, exponentBigInt);
KeyFactory kf = KeyFactory.getInstance("RSA");
Key key = kf.generatePrivate(keySpec);
而且有效。 那也是为什么GregS方法起作用的原因(没有生成“ PublicKey” /“ PrivateKey”,并且解密不是使用的Java内置方法)
对于您到底要去哪里,我还不清楚。 这是一个基于您的代码对我有用的示例。 我没有Windows机器,所以我在Mono中进行了测试。
C#:
using System;
using System.Security.Cryptography;
using System.Text;
namespace RsaDotNetToJava
{
class MainClass
{
public byte[] RSAEncrypt(byte[] data, RSAParameters param, bool padding) {
using (RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024)) {
rsaProvider.ImportParameters(param);
var encryptedData = rsaProvider.Encrypt(data, false);
return encryptedData;
}
}
public static void W(string label, byte [] x) {
var b64 = Convert.ToBase64String (x);
Console.WriteLine ("{0} = {1}", label, b64);
}
public static void Main (string[] args)
{
var x = new MainClass ();
var rsa = new RSACryptoServiceProvider (1024);
var data = Encoding.ASCII.GetBytes("Hello world");
var parms = rsa.ExportParameters(true);
W ("Modulus", parms.Modulus);
W ("P", parms.P);
W ("DecryptExponent", parms.D);
W ("EncryptExponent", parms.Exponent);
var cipher = x.RSAEncrypt(data, parms, false);
W ("Cipher", cipher);
}
}
}
接下来,一些Java从命令行读取base64字符串(带“ label =”剥离),并进行一些操作。 这将java 7类javax.xml.bind.DatatypeConverter
用于其base64解析器。
import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;
public class RsaJavaToDotNet {
private static BigInteger b64ToBigInteger(String b64) {
byte [] bigEndianBytes = DatatypeConverter.parseBase64Binary(b64);
return new BigInteger(1, bigEndianBytes);
}
/**
* @param args base64 encoded .NET big-endian integer arrays
* args[0] = modulus
* args[1] = prime
* args[2] = decrypt exponent
* args[3] = encrypt exponent
* args[4] = cipher
* @throws Exception
*/
public static void main(String[] args) throws Exception {
BigInteger modulus = b64ToBigInteger(args[0]);
final int modulusByteLength = (modulus.bitLength() + 7) / 8;
BigInteger prime = b64ToBigInteger(args[1]);
BigInteger d = b64ToBigInteger(args[2]);
BigInteger e = b64ToBigInteger(args[3]);
BigInteger cipherInt = b64ToBigInteger(args[4]);
// Decrypt the cipher
BigInteger plainInt = cipherInt.modPow(d, modulus);
byte [] plain = plainInt.toByteArray();
// Verify the format and extract the message.
if (plain.length != (modulusByteLength - 1) || plain[0] != 2) {
throw new Exception("Something is wrong");
}
// Find the zero byte delimited the payload from the padding
int zeroPos = 1;
while (zeroPos < plain.length && plain[zeroPos] != 0) {
++zeroPos;
}
String plainStr = new String(plain, zeroPos + 1, plain.length - zeroPos - 1, "UTF-8");
System.out.println(plainStr);
}
}
因此,为了进行测试,我将c#中的加密数据的字节数组转换为sbyte数组,并获取它的字符串表示形式。 然后,将字节数组的字符串表示形式复制到我的Java代码中,然后将其转换回“字节”数组。 之后,我反转数组以匹配Java的字节序。
我不太明白为什么“字节序”在这里是一个问题。 Endianism是您正在运行的CPU的功能,而不是编程语言。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.