aes256 结果在 C# (Windows) 和 C++ 中有所不同; (Ubuntu)实施
以下是在 C(Windows 和 C++(Ubuntu 使用 libcrypto++)上使用 cbc 和 pkcs7 填充(和密码)加密 aes256 的代码。加密结果不一样。为什么?
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
public static class AESEncryption
{
public static string Encrypt(byte[] PlainTextBytes, byte[] KeyBytes, string InitialVector)
{
try
{
byte[] InitialVectorBytes = Encoding.UTF8.GetBytes(InitialVector);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.CBC;
// SymmetricKey.Padding = PaddingMode.PKCS7;
ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
MemoryStream MemStream = new MemoryStream();
CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
CryptoStream.FlushFinalBlock();
byte[] CipherTextBytes = MemStream.ToArray();
MemStream.Close();
CryptoStream.Close();
//return ByteToHexConversion(CipherTextBytes);
return Convert.ToBase64String(CipherTextBytes);
}
catch (Exception a)
{
throw a;
}
}
}
namespace aes
{ class Program
{
static void Main(string[] args)
{
string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"), Encoding.ASCII.GetBytes("My Password"), "0000000000000000");
Console.WriteLine(FinalValue);
}
}
}
C++:
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <cassert>
#include <stdlib.h>
#include <openssl/evp.h>
#include <sstream>
#include "base64.h"
int main()
{
std::string result;
std::stringstream out;
// ctx holds the state of the encryption algorithm so that it doesn't
// reset back to its initial state while encrypting more than 1 block.
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
std::string keyy="My Password";// in char key[] My Password is written in bytes
unsigned char key[] = {0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x4d,0x79, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77,
0x6f, 0x72, 0x64};
unsigned char iv[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
assert(sizeof(key) == 32); // AES256 key size
assert(sizeof(iv) == 16); // IV is always the AES block size
// If data isn't a multiple of 16, the default behavior is to pad with
// n bytes of value n, where n is the number of padding bytes required
// to make data a multiple of the block size. This is PKCS7 padding.
// The output then will be a multiple of the block size.
std::string plain("My Text");
std::vector<unsigned char> encrypted;
size_t max_output_len = plain.length() + (plain.length() % 16) + 16;
encrypted.resize(max_output_len);
// Enc is 1 to encrypt, 0 to decrypt, or -1 (see documentation).
EVP_CipherInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv, 1);
// EVP_CipherUpdate can encrypt all your data at once, or you can do
// small chunks at a time.
int actual_size = 0;
EVP_CipherUpdate(&ctx,
&encrypted[0], &actual_size,
reinterpret_cast<unsigned char *>(&plain[0]), plain.size());
// EVP_CipherFinal_ex is what applies the padding. If your data is
// a multiple of the block size, you'll get an extra AES block filled
// with nothing but padding.
int final_size;
EVP_CipherFinal_ex(&ctx, &encrypted[actual_size], &final_size);
actual_size += final_size;
encrypted.resize(actual_size);
for( size_t index = 0; index < encrypted.size(); ++index )
{
std::cout << std::hex << std::setw(2) << std::setfill('0') <<
static_cast<unsigned int>(encrypted[index]);
//std:: cout<< "val: "<< static_cast<unsigned int>(encrypted[index]) << std::endl;
out<< std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(encrypted[index]);
}
result = out.str();
std::cout <<"\n"<< result<< "\n";
EVP_CIPHER_CTX_cleanup(&ctx);
//
std::cout<<"decript..\n";
return 0;
}
Here are the codes for aes256 with cbc and pkcs7 padding (and a password) encryption on C (Windows and C++ (Ubuntu using libcrypto++). The encryption result is not the same. Why?
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
public static class AESEncryption
{
public static string Encrypt(byte[] PlainTextBytes, byte[] KeyBytes, string InitialVector)
{
try
{
byte[] InitialVectorBytes = Encoding.UTF8.GetBytes(InitialVector);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.CBC;
// SymmetricKey.Padding = PaddingMode.PKCS7;
ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
MemoryStream MemStream = new MemoryStream();
CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
CryptoStream.FlushFinalBlock();
byte[] CipherTextBytes = MemStream.ToArray();
MemStream.Close();
CryptoStream.Close();
//return ByteToHexConversion(CipherTextBytes);
return Convert.ToBase64String(CipherTextBytes);
}
catch (Exception a)
{
throw a;
}
}
}
namespace aes
{ class Program
{
static void Main(string[] args)
{
string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"), Encoding.ASCII.GetBytes("My Password"), "0000000000000000");
Console.WriteLine(FinalValue);
}
}
}
C++:
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <cassert>
#include <stdlib.h>
#include <openssl/evp.h>
#include <sstream>
#include "base64.h"
int main()
{
std::string result;
std::stringstream out;
// ctx holds the state of the encryption algorithm so that it doesn't
// reset back to its initial state while encrypting more than 1 block.
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
std::string keyy="My Password";// in char key[] My Password is written in bytes
unsigned char key[] = {0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x4d,0x79, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77,
0x6f, 0x72, 0x64};
unsigned char iv[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
assert(sizeof(key) == 32); // AES256 key size
assert(sizeof(iv) == 16); // IV is always the AES block size
// If data isn't a multiple of 16, the default behavior is to pad with
// n bytes of value n, where n is the number of padding bytes required
// to make data a multiple of the block size. This is PKCS7 padding.
// The output then will be a multiple of the block size.
std::string plain("My Text");
std::vector<unsigned char> encrypted;
size_t max_output_len = plain.length() + (plain.length() % 16) + 16;
encrypted.resize(max_output_len);
// Enc is 1 to encrypt, 0 to decrypt, or -1 (see documentation).
EVP_CipherInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv, 1);
// EVP_CipherUpdate can encrypt all your data at once, or you can do
// small chunks at a time.
int actual_size = 0;
EVP_CipherUpdate(&ctx,
&encrypted[0], &actual_size,
reinterpret_cast<unsigned char *>(&plain[0]), plain.size());
// EVP_CipherFinal_ex is what applies the padding. If your data is
// a multiple of the block size, you'll get an extra AES block filled
// with nothing but padding.
int final_size;
EVP_CipherFinal_ex(&ctx, &encrypted[actual_size], &final_size);
actual_size += final_size;
encrypted.resize(actual_size);
for( size_t index = 0; index < encrypted.size(); ++index )
{
std::cout << std::hex << std::setw(2) << std::setfill('0') <<
static_cast<unsigned int>(encrypted[index]);
//std:: cout<< "val: "<< static_cast<unsigned int>(encrypted[index]) << std::endl;
out<< std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(encrypted[index]);
}
result = out.str();
std::cout <<"\n"<< result<< "\n";
EVP_CIPHER_CTX_cleanup(&ctx);
//
std::cout<<"decript..\n";
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
c# 中的 IV 是包含“0”而不是“\0”的字符串,而 c++ 中的 IV 确实包含“\0”,“0”和“\0”的 ascii 值不同。
将以下行替换
为
我认为应该可以解决的问题。
20110111
尝试将
Encoding.ASCII.GetBytes("My Password")
替换为new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x4d、0x79、0x20、0x50、0x61、0x73、0x73、0x77、0x6f、0x72、 0x64}
在你的 C# 代码中应该输出不同的结果
your IV in c# is a string containing '0' and not '\0' and your IV in c++ does contain '\0' the ascii value of '0' and '\0' are different.
replace the following line
with
that should do the trick I think.
20110111
try replacing
Encoding.ASCII.GetBytes("My Password")
withnew byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d,0x79, 0x20, 0x50, 0x61, 0x73, 0x73, 0x77,0x6f, 0x72, 0x64}
in your c# code should yell different result
扩展 dvhh 的答案:
无论如何,您可能不应该使用零 IV。 IV 可用于避免相同的明文(或具有相同前缀的明文)被攻击者识别为相同。 IV 本身可以是明文;所以你可以随机生成 IV 并将其添加到输出中以允许解密。
要在 .NET 中获得安全的随机 IV,例如:
顺便说一句,C# 中的通常做法是在变量或参数名称的开头使用小写字母 - 如果您遵循,您将使您的代码对其他人更具可读性套装。
Expanding on dvhh's answer:
You probably should not use a zero IV anyhow. An IV can be useful to avoid identical plaintexts (or plaintexts with identical prefixes) from being recognizably identical to an attacker. It's OK for the IV itself to be plaintext; so you could just randomly generate the IV and prepend it to the output to permit decryption.
To get a secure random IV in .NET, so something like:
BTW, the usual practice in C# is to use a lowercase letter at the start of a variable or parameter name - you'll make your code more readable to others if you follow suit.
当我在 PHP 和 .NET Web 服务之间进行加密时,我遇到了同样的问题。
我在 github 上创建了一个示例项目来展示 PHP 和 NET 之间 Rijndael 加密的工作示例:
https://github.com/dchymko/.NET--PHP-加密
I came across this same issue when I was working on encryption between PHP and a .NET web service.
I created a sample project on github to show a working example of Rijndael encryption between PHP and NET.:
https://github.com/dchymko/.NET--PHP-encryption
如果您想使用 IV 作为值为 0x00 的 16 字节数组,则将 C# 代码更改
为
您还可以更改函数以省略第三个参数
If you want to use IV as 16 bytes array of value 0x00, then change the C# code
to
You can also change the function to ommit the 3rd parameter