AES256で暗号化された文字を復号化。
RFC2898 PBKDF2の仕様を満たす関数がMSのCryptAPIに含まれていないが、
ネット上で代わりを見つけたので試してみた。


暗号化はC#

static void Main(string[] args)
{
string password= "password"; // パスワード


byte[] salt8byte = { 0x78, 0x57, 0x8E, 0x5A, 0x5D, 0x63, 0xCB, 0x06}; // 暗号と復号で同じにする必要があるが、パスワードではない。

//PasswordDeriveBytes pwdgen = new PasswordDeriveBytes(password, null);// PBKDF1仕様、メンテモードらしい
//byte[] iv2 = new byte[8];
//byte[] key2 = pwdgen.CryptDeriveKey("AES", "SHA1", 0, iv2); // ERROR: AESはサポートしていない

var pwdgen = new System.Security.Cryptography.Rfc2898DeriveBytes(password, salt8byte); // PBKDF2仕様
pwdgen.IterationCount = 2048;

byte[] key = pwdgen.GetBytes(32); // 共有キー
byte[] iv = pwdgen.GetBytes(16); // 初期化ベクタ

AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); // AES256

var encryptor = aes.CreateEncryptor( key, iv );

// 暗号化する文字をbyte配列へ変換。UNICODなのでC++ではWCHARとなる。
// Encoding.UTF8.GetBytesするとC++でUTF8の処理が必要になり面倒。
byte[] input = Encoding.Unicode.GetBytes("hello world! 漢字も暗号化");

using (MemoryStream msEncrypt = new MemoryStream())
{
using(CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(input, 0, input.Length);
csEncrypt.FlushFinalBlock();
}

string output = Convert.ToBase64String( msEncrypt.ToArray() );

// 3F+zAv4ChcViJEWzHcAp8QB4//cZf0DeJQVQVLB7N/tsc++iggk2pwK2jjGn9K2Y
System.Diagnostics.Debug.WriteLine( output );
}
}

3F+zAv4ChcViJEWzHcAp8QB4//cZf0DeJQVQVLB7N/tsc++iggk2pwK2jjGn9K2Yの文字を解読する。


// 復号化はC++
int _tmain(int argc, _TCHAR* argv[])
{
CStringA data = "3F+zAv4ChcViJEWzHcAp8QB4//cZf0DeJQVQVLB7N/tsc++iggk2pwK2jjGn9K2Y"; // 暗号化されたデータ、BASE64エンコードされてる

CStringA password = "password";

// 暗号化されたデータをBASE64でデコードしBYTE配列へ
int len = ATL::Base64DecodeGetRequiredLength( data.GetLength());
BYTE* buf = new BYTE[len];
ATL::Base64Decode( data, data.GetLength(), buf, &len );

// パスワードからkey,ivのbyte配列へ変換、変換アルゴリズムはRfc2898 PBKDF2
BYTE pbDerivedKey[32+16];
BYTE salt8byte[8] = { 0x78, 0x57, 0x8E, 0x5A, 0x5D, 0x63, 0xCB, 0x06}; // 暗号と復号で同じにする必要があるが、パスワードではない。

PBKDF2(sha1Prf, (LPBYTE)(LPCSTR)password, password.GetLength(), salt8byte, 8, 2048, pbDerivedKey, 32+16); // http://www.idrix.fr/Root/Samples/pbkdf2.cpp

BYTE key[32]; // 32:AES256仕様のキー
BYTE iv[16]; // 16:AES256仕様初期化ベクター

CopyMemory( key, pbDerivedKey, 32 );
CopyMemory( iv, pbDerivedKey+32, 16 );

HCRYPTPROV hProv = NULL;

BOOL bl = CryptAcquireContext(&hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0); // AES仕様
ATLASSERT( bl );

HCRYPTKEY hkey = NULL;

struct KeyBLOB{
BLOBHEADER hdr;
DWORD cbKeySize;
BYTE rgbKeyData[32]; // 32:AES_256のキー長
};

KeyBLOB kb;

kb.hdr.bType = PLAINTEXTKEYBLOB;
kb.hdr.bVersion = CUR_BLOB_VERSION;
kb.hdr.reserved = 0;
kb.hdr.aiKeyAlg = CALG_AES_256; // AES_256

kb.cbKeySize = 32;

CopyMemory( kb.rgbKeyData, key, 32 );

bl = CryptImportKey(hProv,(BYTE*)&kb, sizeof(kb),0,0,&hkey); // キー作成
ATLASSERT( bl );

bl = CryptSetKeyParam(hkey, KP_IV, iv, 0); // 初期化ベクター設定
ATLASSERT( bl );

DWORD dwCount = len;

bl = CryptDecrypt(hkey,0, true, 0, buf, &dwCount);
ATLASSERT( bl );

WCHAR* str = (WCHAR*)buf; // Encoding.Unicode.GetBytesなので、WCHARへ
str[dwCount/2] = 0; // dwCountはbyteでの位置なので、WCHARでは半分

CString output = (LPCWSTR)str;

ATLASSERT( output == L"hello world! 漢字も暗号化" );

delete [] buf;
CryptDestroyKey( hkey );
CryptReleaseContext(hProv, 0);
}

ちなみに、パスワードという物はソース内に記述してはならない。逆アセンブルで流出の恐れが
あるから。また、公開鍵を除いてネット上でのやりとりも禁止である。
ソルトやPBKDF2の回転数はソース内に堂々と記述するため、パスワードではない。