OpenSSL 错误"data greater than mod len"

OpenSSL error "data greater than mod len"

本文关键字:mod len than data 错误 OpenSSL greater      更新时间:2023-10-16

我有一个客户端/服务器设置,它包括一个使用OpenSSL用C++编写的服务器和一个使用Aes/RSACryptoServiceProvider用C#编写的客户端。我在两边生成一个RSA密钥对,并向两边发送公钥。然后,当我准备发送消息时,我生成一个Aes密钥/iv,并用它加密消息,然后用接收方的公钥加密Aes密钥(还有iv?我试过加密和不加密,但都给了我相同的错误,我稍后会提到),然后发送Aes加密密钥、iv和加密消息。然而,当我尝试从客户端发送到服务器时,我会收到一个OpenSSL错误,在使用EVP_OpenInit时读取"大于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] == '')
{
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正确读取客户端公钥时工作的,并且当在服务器上使用通过OpenSSL生成的不同密钥时,我可以使用EVP_Seal/EVP_Open函数加密/解密数据。所以,我不确定是什么导致了这个错误,但我想我有一个主意。当我将数据发送到服务器时,会不会是密钥/iv/加密消息的编码方式?或者可能是OpenSSL和C#在实现上的差异?或者可能是我完全没有抓到的东西?

编辑:这是我如何使用EVP_OpenInit的请求代码。

BlueSOD::Encryption::DecryptionData BlueSOD::Encryption::EncryptionFactory::Decrypt2(DecryptionWork2 && work)
{
DecryptionData data;
EVP_PKEY* privateKey = work.privateKey.get();
auto encryptionKey = (unsigned char*)work.info.key.c_str();
auto encryptionIV = (unsigned char*)work.info.iv.c_str();
EVP_CIPHER_CTX_ptr cipherCtxPtr{ AcquireCipherCtx() };
EVP_CIPHER_CTX* cipher = cipherCtxPtr.get();
int status;
//ERROR HAPPENS HERE
status = EVP_OpenInit(cipher, m_Cipher, encryptionKey, work.info.key.size(), encryptionIV, privateKey);
cout << ERR_error_string(ERR_get_error(), nullptr) << endl;
CheckForError(status, "EVP_OpenInit failed.");
int bufferLength = work.cipherText.size() + EVP_MAX_BLOCK_LENGTH;
auto buffer = make_unique<unsigned char[]>(bufferLength);
auto cipherTemp = (unsigned char*)work.cipherText.c_str();
status = EVP_OpenUpdate(cipher, buffer.get(), &bufferLength, cipherTemp, work.cipherText.size());
CheckForError(status, "EVP_OpenUpdate failed.");
status = EVP_OpenFinal(cipher, buffer.get(), &bufferLength);
CheckForError(status, "EVP_OpenFinal failed.");
data.plainText = CreateSecureString(buffer.get(), bufferLength);
return move(data);
}

Encoding.Default.GetString无法工作。IV、包装密钥和密文都是二进制的,(与随机一样)无法区分。这意味着编码可能会出错,因为并非所有字节都映射到字符。这意味着信息丢失。请尝试使用base64编码。

您的IV、封装密钥和密文也应该相互区分。然而,这并不难,因为IV具有底层密码的块大小(AES/CBC为16个字节),封装的密钥具有相同的模数字节大小(或RSA密钥大小),而密文是其余的。换句话说,你还可以简单地将它们全部连接起来。

所以你的预感是对的。

RSA 4096 w/OAEP只能加密446字节的数据(请参阅RSA RFC 2437的7.1),而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解码加密密钥的十六进制编码值,结果是相同的?你可以试着只使用客户端&服务器使用该密钥对任意明文消息(<<245字节,以确保OAEP或PKCS#1v1.5填充不超过245字节)进行加密和解密,以确保一切正确?

我对C#实现不是特别熟悉——在客户端上复制EVP_SealInit还需要做些什么吗?

我的问题在于如何在客户端对消息、密钥和iv进行编码,并在服务器上进行解码。客户端用base64对它们进行编码,因此必须在服务器上对其进行解码,以便OpenSSL理解它们(OpenSSL使用原始字节而不是编码方案)。

EDIT:C#实现和OpenSSL实现之间似乎也存在冲突。C#Aes的实现似乎与OpenSSL并不完全匹配。在OpenSSL中解密时,我得到了正确的纯文本,但后面有一堆垃圾数据,EVP_OpenFinal会导致一个错误,上面写着"解密错误"。如果有人知道解决这个问题的方法,请告诉我!我尝试过openssl.net API,但如果我尝试使用BIO.,它会抛出一个错误