我有一个客户端/服务器设置,包括使用OpenSSL
用C ++编写的服务器和使用Aes/RSACryptoServiceProvider
用C#编写的客户端。我在两侧生成一个RSA密钥对,并向每一侧发送公钥。然后,当我准备发送消息时,我生成一个Aes密钥/ iv并用此加密消息,然后加密Aes密钥(以及iv也是?我已经尝试加密它而不加密它,但是两者都给了我同样的错误,我会稍微提一下)用收件人的公钥,然后发送Aes加密密钥,iv和加密邮件。但是,当我尝试从客户端发送到服务器时,在使用EVP_OpenInit
时,我收到的OpenSSL错误读取“大于mod len的数据”。
以下是我在C#中生成数据的方法:
var keyPair = new RSACryptoServiceProvider(2048); //server uses 2048 too (added on Edit)
var aes = new AesCryptoServiceProvider();
//OpenSSL uses the EVP_CIPHER* EVP_aes_256_cbc()
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.KeySize = 256; //bits
aes.GenerateKey();
aes.GenerateIV();
var message = Encoding.Default.GetBytes("test data");
var eMessage = new byte[4096];
using (var stream = new MemoryStream(eMessage))
{
var encryptor = aes.CreateEncryptor();
using (var cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
{
await cryptoStream.WriteAsync(message, 0, message.Length);
}
}
string eMessageString = null;
for (int i = 0; i < eMessage.Length; i++)
{
if (eMessage[i] == '\0')
{
eMessageString = Convert.ToBase64String(eMessage, 0, i-1);
}
}
var eKey = Convert.ToBase64String(keyPair.Encrypt(aes.Key, false));
var eIV = Convert.ToBase64String(aes.IV); //may not need to encrypt
我知道我的C ++实现正常工作,因为OpenSSL正确读入客户端公钥,并且当使用通过{{1}生成的不同密钥时,我可以使用EVP_Seal
/ EVP_Open
函数加密/解密数据在服务器上。所以,我不确定是什么导致了这个错误,但我想我有个主意。当我将数据发送到服务器时,是否可以对密钥/ iv /加密消息进行编码?或者可能是OpenSSL
和C#之间的实施差异?或者也许是我完全没有抓到的东西?
编辑:以下是我使用OpenSSL
。
EVP_OpenInit
答案 0 :(得分:2)
Encoding.Default.GetString
无法工作。 IV,包装密钥和密文都是二进制的,并且(和它一样)无法区分随机。这意味着编码可能会出错,因为并非所有字节都映射到字符。这意味着信息丢失。请尝试使用base 64编码。
您的IV,包装密钥和密文也应该可以相互区分。然而,这并不难,因为IV具有底层密码的块大小(AES / CBC为16字节),包装密钥具有相同的模数字节(或RSA密钥大小),以及密文,井,剩下的就是。换句话说,你可以简单地将它们连接起来。
所以你的预感是正确的。
答案 1 :(得分:1)
RSA 4096 w / OAEP只能加密446个字节的数据(see 7.1 of RSA RFC 2437),而RSA 2048 w / OAEP只能加密245个字节(对于IV和对称,仍然应该有足够的空间容纳16 + 32个字节键)。我没有看到您为RSA提供程序设置密钥长度的任何地方,因此可能由于某种原因失败加密AES密钥。
您是否至少可以提供服务器代码抛出异常的行?您在EVP_OpenInit
中为eki
参数(对称密钥长度)提供了什么?您是否在尝试使用服务器上的RSA解密对称密钥之前执行Base64解码?
并且为了记录,您不需要在传输之前加密IV,但是它没有负面影响(除了计算成本)。
<强>更新强>
调试加密问题以减少每个语句中的步骤数时总是有用,这样您就可以找到错误发生的位置。我建议将客户端代码的最后几条语句分解为单独的步骤并逐步完成(即在单独的行上进行RSA加密和Base64编码)。
所以你现在可以比较客户端和服务器上的以下值,它们是逐字节的(不是0x00
等等)?
Reference | Client | Server
------------------------------------------------------------------
A | keyPair.Encrypt(aes.Key, false) | ek
Base64E(A) | eKey | ??
len(A) | len(A) | len(ek)
您在另一条评论中提到您在客户端和服务器上比较了Base64解码的加密密钥的十六进制编码值,它是否相同?你可以尝试使用客户端&amp;服务器加密和解密任意明文消息(&lt;&lt;&lt; 245字节,以确保OAEP或PKCS#1 v1.5填充不超过245字节)与该密钥对,以确保一切正确?
我对C#实现并不是特别熟悉 - 在客户端复制EVP_SealInit
时还需要做些什么吗?
答案 2 :(得分:0)
我的问题在于如何在客户端上编码消息,密钥和iv并在服务器上解码。客户端将它们编码为base64,因此必须在服务器上对其进行解码,以便OpenSSL理解它们(OpenSSL使用原始字节而不是编码方案)。
编辑:C#实现和OpenSSL实现之间似乎也存在冲突。 C#Aes实现似乎与OpenSSL完全不匹配。在OpenSSL中解密时,我得到正确的纯文本,但是后面有一堆垃圾数据,而content.js:53 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
at main (content.js:53)
at content.js:9
会导致错误,说“解密不好”。如果有人知道解决方法,请告诉我!我尝试过openssl.net API,但如果我尝试使用BIO,则会抛出错误。