以下是在C上使用cbc和pkcs7填充(和密码)加密的aes256的代码(Windows和C ++(使用libcrypto ++的Ubuntu)。加密结果不一样。为什么?
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;
}
答案 0 :(得分:8)
你在c#中的IV是一个包含'0'而不是'\ 0'的字符串,你在c ++中的IV确实包含'\ 0',ascii值为'0'和'\ 0'是不同的。
替换以下行
string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"), Encoding.ASCII.GetBytes("My Password"), "0000000000000000");
与
string FinalValue = AESEncryption.Encrypt( Encoding.ASCII.GetBytes("My Text"), Encoding.ASCII.GetBytes("My Password"), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
这应该是我认为的伎俩。
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#代码中应该大喊不同的结果
答案 1 :(得分:3)
扩展dvhh的回答:
无论如何,你可能不应该使用零IV。 IV可以用于避免相同的明文(或具有相同前缀的明文)与攻击者可识别地相同。 IV本身可以是明文;所以你可以随机生成IV并将其添加到输出中以允许解密。
要在.NET中获得安全的随机IV,请执行以下操作:
byte[] initialVectorBytes = new byte[16];
using(var rng = new RNGCryptoServiceProvider())
rng.GetBytes(initialVectorBytes);
//...
using(var memStream = new MemoryStream()) {
memStream.Write(IV,0,16); //to permit decryption later.
//...
}
顺便说一句,C#中通常的做法是在变量或参数名称的开头使用小写字母 - 如果你效仿,你将使你的代码对其他人更具可读性。
答案 2 :(得分:1)
当我在PHP和.NET Web服务之间进行加密时,我遇到了同样的问题。
我在github上创建了一个示例项目,以显示PHP和NET之间Rijndael加密的工作示例: https://github.com/dchymko/.NET--PHP-encryption
答案 3 :(得分:0)
如果要将IV用作值为0x00的16字节数组,则更改C#代码
byte[] InitialVectorBytes = Encoding.UTF8.GetBytes(InitialVector);
到
byte[] InitialVectorBytes = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
您也可以更改功能以省略第3个参数