OpenSSL错误“数据大于mod len”

时间:2017-01-04 18:24:29

标签: c# c++ encryption openssl cryptography

我有一个客户端/服务器设置,包括使用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

3 个答案:

答案 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,则会抛出错误。