C# の Rfc2898DeriveBytes と C++ の PKCS5_PBKDF2_HMAC_SHA1 は、同じ関数 ( PBKDF2 ) であると想定されています。しかし、C# でメッセージを暗号化し、C++ で復号化しようとすると、パスワードが異なります。両側に同じ情報 (パスフレーズ、IV、ソルト) を入れています。何が起こっているかを示すために、いくつかの簡単な例をまとめました。私は間違いを犯していますか、それとも別のライブラリをどこかで使用する必要がありますか?
c# 暗号化:
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
public class RijndaelSimpleTest
{
[STAThread]
static void Main(string[] args)
{
string plainText = "Hello, World!";
string passPhrase = "testtesttesttesttesttest";
string initVector = "6543210987654321";
string saltValue = "1234567890123456";
int passwordIterations = 10000;
int keySize = 32;
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes, passwordIterations);
byte[] keyBytes = password.GetBytes(keySize);
string debugPassword = Convert.ToBase64String(keyBytes);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string encyptText = Convert.ToBase64String(cipherTextBytes);
string cipherText = initVector + saltValue + encyptText;
Console.WriteLine(String.Format("Plaintext : {0}", plainText));
Console.WriteLine(String.Format("passPhrase : {0}", passPhrase));
Console.WriteLine(String.Format("initVector : {0}", initVector));
Console.WriteLine(String.Format("saltValue : {0}", saltValue));
Console.WriteLine(String.Format("passwordIterations : {0}", passwordIterations));
Console.WriteLine(String.Format("keySize : {0}", keySize));
Console.WriteLine(String.Format("password : {0}", debugPassword));
Console.WriteLine(String.Format("EncryptedText : {0}", encyptText));
Console.WriteLine(String.Format("EncryptedText+IV+SALT : {0}", cipherText));
}
}
出力
Plaintext : Hello, World!
passPhrase : testtesttesttesttesttest
initVector : 6543210987654321
saltValue : 1234567890123456
passwordIterations : 10000
keySize : 32
password : uu1FmPoEROlTBOvilXnIHG64uS56i3f4br/RZ1d49YE=
EncryptedText : rVGIwm/WH9tw/SiN+iXw0Q==
EncryptedText+IV+SALT : 65432109876543211234567890123456rVGIwm/WH9tw/SiN+iXw0Q==
c++ 出力:
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <stdio.h>
#include <string>
#include <iostream>
const unsigned char* convertString(const std::string& s){
unsigned char * bytes = new unsigned char[s.size() + 1];
std::copy(s.begin(), s.end(), bytes);
bytes[s.size()] = '\0';
return(bytes);
}
int main(int argc, char** argv) {
const char passPhrase[] = "testtesttesttesttesttest";
std::string ExpectedPlaintext = "Hello, World!";
int passwordIterations = 10000;
int keySize = 32;
std::string EncryptedText = "65432109876543211234567890123456rVGIwm/WH9tw/SiN+iXw0Q==";
std::string ExpectedIV = "6543210987654321";
std::string ExpectedSalt = "1234567890123456";
std::string ExpectedCipherText = "rVGIwm/WH9tw/SiN+iXw0Q==";
std::string Expectedpassword = "uu1FmPoEROlTBOvilXnIHG64uS56i3f4br/RZ1d49YE=";
const unsigned char *initVector = convertString(EncryptedText.substr(0,16));
const unsigned char *saltValue = convertString(EncryptedText.substr(16,16));
const unsigned char *ciphertext = convertString(EncryptedText.substr(16+16));
unsigned char password[keySize+16+1];
if (PKCS5_PBKDF2_HMAC_SHA1(passPhrase, strlen(passPhrase),
saltValue, strlen((char*)saltValue), passwordIterations,
keySize+16, password) != 1) {
std::cout << "Could not derive password" << std::endl;
return -1;
}
password[keySize] = '\0';
int p_len = keySize;
int f_len = 0;
unsigned char *plaintext = (unsigned char*)malloc(p_len + AES_BLOCK_SIZE);
EVP_CIPHER_CTX d_ctx;
EVP_CIPHER_CTX_init(&d_ctx);
EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_cbc(), NULL, password, initVector);
EVP_DecryptInit_ex (&d_ctx, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate (&d_ctx, plaintext, &p_len, ciphertext, keySize);
EVP_DecryptFinal_ex(&d_ctx, plaintext+p_len, &f_len);
keySize = p_len + f_len;
std::cout << "EncryptedText : " << EncryptedText << std::endl;
std::cout << "passPhrase : " << passPhrase << std::endl;
std::cout << "passwordIterations: " << passwordIterations << std::endl;
std::cout << "keySize : " << keySize << std::endl;
std::cout << "ExpectedIV : " << ExpectedIV << std::endl;
std::cout << "initVector : " << initVector << std::endl;
std::cout << "ExpectedSalt : " << ExpectedSalt << std::endl;
std::cout << "saltValue : " << saltValue << std::endl;
std::cout << "ExpectedCipherText: " << ExpectedCipherText << std::endl;
std::cout << "ciphertext : " << ciphertext << std::endl;
std::cout << "Expectedpassword : " << Expectedpassword << std::endl;
std::cout << "password : " << password << std::endl;
std::cout << "ExpectedPlaintext : " << ExpectedPlaintext << std::endl;
std::cout << "plaintext : " << plaintext << std::endl;
EVP_CIPHER_CTX_cleanup(&d_ctx);
delete[] plaintext;
delete[] initVector;
delete[] saltValue;
delete[] ciphertext;
return 0;
}
どの出力:
EncryptedText : 65432109876543211234567890123456rVGIwm/WH9tw/SiN+iXw0Q==
passPhrase : testtesttesttesttesttest
passwordIterations: 10000
keySize : 16
ExpectedIV : 6543210987654321
initVector : 6543210987654321
ExpectedSalt : 1234567890123456
saltValue : 1234567890123456
ExpectedCipherText: rVGIwm/WH9tw/SiN+iXw0Q==
ciphertext : rVGIwm/WH9tw/SiN+iXw0Q==
Expectedpassword : uu1FmPoEROlTBOvilXnIHG64uS56i3f4br/RZ1d49YE=
password : ºíEúDéSëâyÈn¸¹.zwøn¿ÑgWxõ
ExpectedPlaintext : Hello, World!
plaintext : ÊÁMþ±B¯ÍN'¤ä+èû~OÂ(H¢