为什么 Java 中的 RSA 加密会产生与 C# 不同长度的密文
Why is RSA encryption in Java producing different length ciphertext than C#
我正在 AES
使用随机生成的密钥加密一些文本,然后 RSA
使用私钥加密该密钥,以便我可以将其上传到数据库。
RSA
密钥是使用 Java
中的 KeyPairGenerator
生成的,并保存为文件。使用 File.ReadAllBytes()
.
读取密钥
当我在 Java 中执行此操作时,一切正常,加密密钥始终为 172 bytes
,但当我在 C#
中执行此操作时,加密密钥始终为 844 bytes
.我很确定文本已使用 AES
正确加密,但 RSA
加密出了点问题。
我检查了 Java 和 C# 中的密钥大小,它们总是匹配的。从字面上看,我能看到的唯一区别是 RSA 加密的密文长度,这使得数据无法使用。我相信它与填充有关,但我不知道如何解决它。
Java
public String encryptText(String msg, PrivateKey key)
throws NoSuchAlgorithmException, NoSuchPaddingException,
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException {
KeyGenerator generator;
this.cipher.init(Cipher.ENCRYPT_MODE, key); //cipher is initialized earlier with this.cipher = Cipher.getInstance("RSA");
try {
generator = KeyGenerator.getInstance(AES);
generator.init(128); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Cipher aesCipher = Cipher.getInstance(AES);
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
String encText = Base64.getEncoder().encodeToString(aesCipher.doFinal(msg.getBytes("UTF-8")));
String encKey = Base64.getEncoder().encodeToString(cipher.doFinal(secKey.getEncoded()));
return "(" + encText + ")" + encKey;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
C#
public String EncryptText(byte[] privateKeyBytes, string msg)
{
try
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
RSAKeyInfo.Modulus = privateKeyBytes;
RSA.ImportParameters(RSAKeyInfo);
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
byte[] keyGenerated = aes.Key;
string keyStr = Convert.ToBase64String(keyGenerated);
byte[] keyArr = Convert.FromBase64String(keyStr);
byte[] KeyArrBytes16Value = new byte[16];
Array.Copy(keyArr, KeyArrBytes16Value, 16);
aes.Key = KeyArrBytes16Value;
ICryptoTransform encrypto = aes.CreateEncryptor();
byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
string encText = Convert.ToBase64String(CipherText);
string encKey = Convert.ToBase64String(RSA.Encrypt(aes.Key, true));
return "(" + encText + ")" + encKey;
}
catch (CryptographicException e)
{
Console.WriteLine("FAILED: " + e.Message);
}
return null;
}
更新
感谢 Henno 指出问题出在我阅读密钥的方式上。我最终使用 Bouncy Castle 在 C# 中处理 RSA 加密。我还更改了 java 代码以使用 public 密钥而不是私钥进行加密。
新 C#
public String EncryptText(byte[] keyBytes, string msg)
{
try
{
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
byte[] keyGenerated = aes.Key;
string keyStr = Convert.ToBase64String(keyGenerated);
byte[] keyArr = Convert.FromBase64String(keyStr);
byte[] KeyArrBytes16Value = new byte[16];
Array.Copy(keyArr, KeyArrBytes16Value, 16);
aes.Key = KeyArrBytes16Value;
ICryptoTransform encrypto = aes.CreateEncryptor();
byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
string encText = Convert.ToBase64String(CipherText);
string encKey = Convert.ToBase64String(rsa.Encrypt(aes.Key, false));
return "(" + encText + ")" + encKey;
}
catch (CryptographicException e)
{
Console.WriteLine("FAILED: " + e.Message);
}
return null;
}
似乎出了问题的是您在 C# 中读取了保存的 "private key file",大概是在变量 privateKeyBytes
中(但您的代码不完整,所以我猜)然后执行RSAKeyInfo.Modulus = privateKeyBytes
,这很奇怪并且在密码学上令人难以置信。您还应该根据您读入的字节在 C# 中实例化某种 RSA class,我认为这就是您在 C# 代码开头(前四行)尝试做的事情。我认为应该有另一个 API,在文档中环顾四周:
RSA.ImportParameters(RSAKeyInfo)
然后可能从这些字节设置 RSAKeyInfo,但这不是模数。读取的字节应该是 PKCS1 格式或类似的格式,可能是 base64 编码在文件中,或者是原始格式等。您必须查看 Java 用于将完整密钥导出到磁盘的格式。
您使用从文件中读取的原始字节作为模数,这肯定会带来麻烦并给出一个无效且太大的 "key"。
我正在 AES
使用随机生成的密钥加密一些文本,然后 RSA
使用私钥加密该密钥,以便我可以将其上传到数据库。
RSA
密钥是使用 Java
中的 KeyPairGenerator
生成的,并保存为文件。使用 File.ReadAllBytes()
.
当我在 Java 中执行此操作时,一切正常,加密密钥始终为 172 bytes
,但当我在 C#
中执行此操作时,加密密钥始终为 844 bytes
.我很确定文本已使用 AES
正确加密,但 RSA
加密出了点问题。
我检查了 Java 和 C# 中的密钥大小,它们总是匹配的。从字面上看,我能看到的唯一区别是 RSA 加密的密文长度,这使得数据无法使用。我相信它与填充有关,但我不知道如何解决它。
Java
public String encryptText(String msg, PrivateKey key)
throws NoSuchAlgorithmException, NoSuchPaddingException,
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException {
KeyGenerator generator;
this.cipher.init(Cipher.ENCRYPT_MODE, key); //cipher is initialized earlier with this.cipher = Cipher.getInstance("RSA");
try {
generator = KeyGenerator.getInstance(AES);
generator.init(128); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Cipher aesCipher = Cipher.getInstance(AES);
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
String encText = Base64.getEncoder().encodeToString(aesCipher.doFinal(msg.getBytes("UTF-8")));
String encKey = Base64.getEncoder().encodeToString(cipher.doFinal(secKey.getEncoded()));
return "(" + encText + ")" + encKey;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
C#
public String EncryptText(byte[] privateKeyBytes, string msg)
{
try
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
RSAKeyInfo.Modulus = privateKeyBytes;
RSA.ImportParameters(RSAKeyInfo);
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
byte[] keyGenerated = aes.Key;
string keyStr = Convert.ToBase64String(keyGenerated);
byte[] keyArr = Convert.FromBase64String(keyStr);
byte[] KeyArrBytes16Value = new byte[16];
Array.Copy(keyArr, KeyArrBytes16Value, 16);
aes.Key = KeyArrBytes16Value;
ICryptoTransform encrypto = aes.CreateEncryptor();
byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
string encText = Convert.ToBase64String(CipherText);
string encKey = Convert.ToBase64String(RSA.Encrypt(aes.Key, true));
return "(" + encText + ")" + encKey;
}
catch (CryptographicException e)
{
Console.WriteLine("FAILED: " + e.Message);
}
return null;
}
更新
感谢 Henno 指出问题出在我阅读密钥的方式上。我最终使用 Bouncy Castle 在 C# 中处理 RSA 加密。我还更改了 java 代码以使用 public 密钥而不是私钥进行加密。
新 C#
public String EncryptText(byte[] keyBytes, string msg)
{
try
{
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
byte[] keyGenerated = aes.Key;
string keyStr = Convert.ToBase64String(keyGenerated);
byte[] keyArr = Convert.FromBase64String(keyStr);
byte[] KeyArrBytes16Value = new byte[16];
Array.Copy(keyArr, KeyArrBytes16Value, 16);
aes.Key = KeyArrBytes16Value;
ICryptoTransform encrypto = aes.CreateEncryptor();
byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
string encText = Convert.ToBase64String(CipherText);
string encKey = Convert.ToBase64String(rsa.Encrypt(aes.Key, false));
return "(" + encText + ")" + encKey;
}
catch (CryptographicException e)
{
Console.WriteLine("FAILED: " + e.Message);
}
return null;
}
似乎出了问题的是您在 C# 中读取了保存的 "private key file",大概是在变量 privateKeyBytes
中(但您的代码不完整,所以我猜)然后执行RSAKeyInfo.Modulus = privateKeyBytes
,这很奇怪并且在密码学上令人难以置信。您还应该根据您读入的字节在 C# 中实例化某种 RSA class,我认为这就是您在 C# 代码开头(前四行)尝试做的事情。我认为应该有另一个 API,在文档中环顾四周:
RSA.ImportParameters(RSAKeyInfo)
然后可能从这些字节设置 RSAKeyInfo,但这不是模数。读取的字节应该是 PKCS1 格式或类似的格式,可能是 base64 编码在文件中,或者是原始格式等。您必须查看 Java 用于将完整密钥导出到磁盘的格式。
您使用从文件中读取的原始字节作为模数,这肯定会带来麻烦并给出一个无效且太大的 "key"。