C++ 文字列の暗号化/復号化

Win32API の Cryptography Functions を利用

コード

// EncryptDecryptCR4.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

#pragma comment( lib, "Crypt32.lib" )	// CryptBinaryToStringA(), CryptStringToBinaryA()
#include <windows.h>
#include <wincrypt.h>
#include <stdlib.h>	// malloc()
#include <assert.h>
#include <conio.h>	// _getch()

#define MY_ENCRYPT_ALGORITHM		CALG_RC4
#define MY_ENCRYPT_KEYLENGTH		128

// 暗号化/復号化 の後処理
static
void PostCryptString( HCRYPTPROV* phCryptProv,
	HCRYPTKEY* phCryptKey )
{
	if( *phCryptKey )
	{
		CryptDestroyKey( *phCryptKey );
		*phCryptKey = NULL;
	}
	if( *phCryptProv )
	{
		CryptReleaseContext( *phCryptProv, 0 );
		*phCryptProv = NULL;
	}
}

// 暗号化/復号化 の前処理
static
bool PreCryptString( const char* pszKey,
	HCRYPTPROV* phCryptProv,
	HCRYPTKEY* phCryptKey )
{
	// アウトプットの初期化
	*phCryptProv = NULL;
	*phCryptKey = NULL;

	// インプットのチェック
	if( NULL == pszKey )
	{
		return false;
	}
	DWORD dwSizeKey = (DWORD)( strlen( pszKey ) * sizeof( char ) );
	if( 0 == dwSizeKey )
	{
		return false;
	}

	// 暗号化プロバイダの取得
	// 暗号化プロバイダとして、「Microsoft Enhanced RSA and AES Cryptographic Provider」を指定
	// 暗号化プロバイダとして、「PROV_RSA_AES」を指定
	long errorcode = CryptAcquireContext(phCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0);
	if (!errorcode && GetLastError() == NTE_BAD_KEYSET) {
		// キーコンテナが存在しないのでCRYPT_NEWKEYSETフラグを利用して再呼び出し(https://docs.microsoft.com/ja-jp/windows/desktop/api/wincrypt/nf-wincrypt-cryptacquirecontexta)
		errorcode = CryptAcquireContext(phCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_NEWKEYSET);
	}
	if (!errorcode)
	{
		CString mess;
		mess.Format(_T("errorcode = %d"), GetLastError());
		AfxMessageBox(mess);
		goto LABEL_ERROR;
	}

	// ハッシュオブジェクトの作成
	HCRYPTHASH hCryptHash = NULL;
	if( !CryptCreateHash( *phCryptProv, CALG_MD5, 0, 0, &hCryptHash ) )
	{
		goto LABEL_ERROR;
	}

	// ハッシュオブジェクトに、ハッシュ値を求める元となるデータの追加
	if( !CryptHashData( hCryptHash, (BYTE*)pszKey, dwSizeKey, 0 ) )
	{
		goto LABEL_ERROR;
	}

	// ハッシュオブジェクトから暗号キーの取得
	// 第4引数は、下位16ビットをゼロにし、上位16ビットでキーのビット数を指定する。
	if( !CryptDeriveKey( *phCryptProv, MY_ENCRYPT_ALGORITHM, hCryptHash, MY_ENCRYPT_KEYLENGTH << 16, phCryptKey ) )
	{
		goto LABEL_ERROR;
	}

	if( hCryptHash )
	{
		CryptDestroyHash( hCryptHash );
	}
	return true;

LABEL_ERROR:
	if( hCryptHash )
	{
		CryptDestroyHash( hCryptHash );
	}
	PostCryptString( phCryptProv, phCryptKey );
	return false;
}

// 文字列の暗号化
bool EncryptString( const char* pszSource,
	const char* pszKey,
	BYTE** ppbyteDest,
	DWORD* pdwSizeDest )
{
	// アウトプットの初期化
	*ppbyteDest = NULL;
	*pdwSizeDest = 0;

	// インプットのチェック
	if( NULL == pszSource
		|| NULL == pszKey )
	{
		return false;
	}

	// 変数
	HCRYPTPROV hCryptProv = NULL;
	HCRYPTKEY hCryptKey = NULL;

	// 暗号化/復号化 の前処理
	if( !PreCryptString( pszKey, &hCryptProv, &hCryptKey ) )
	{
		return false;
	}

	// バッファー確保
	DWORD dwSizeSource = (DWORD)( strlen( pszSource ) * sizeof( char ) );
	if( 0 == dwSizeSource )
	{
		goto LABEL_ERROR;
	}
	DWORD dwSizeBuffer = dwSizeSource + 100;	// バッファーサイズはソースよりも多少(16以上)多く領域を確保する。
	BYTE* pbyteBuffer = (BYTE*)malloc( dwSizeBuffer );
	memset( pbyteBuffer, 0x00, dwSizeBuffer );	// ゼロ埋め。
	memcpy( pbyteBuffer, pszSource, dwSizeSource );	// 暗号化する文字列のバイト配列化

	// 暗号化
	DWORD dwSize = dwSizeSource;
	if( !CryptEncrypt( hCryptKey, 0, TRUE, 0, pbyteBuffer, &dwSize, dwSizeBuffer ) )
	{
		goto LABEL_ERROR;
	}

	// アウトプットに代入
	*ppbyteDest = pbyteBuffer;
	*pdwSizeDest = dwSize;	// dwSizeは、CryptEncrypt()で、暗号化結果データサイズになっている。

	PostCryptString( &hCryptProv, &hCryptKey );
	return true;

LABEL_ERROR:
	if( pbyteBuffer ) { free( pbyteBuffer ); }
	PostCryptString( &hCryptProv, &hCryptKey );
	return false;
}

// 文字列の復号化
bool DecryptString( const BYTE* pbyteSource,
	const DWORD dwSizeSource,
	const char* pszKey,
	char** ppszDest )
{
	// アウトプットの初期化
	*ppszDest = NULL;

	// インプットのチェック
	if( NULL == pbyteSource
		|| NULL == pszKey )
	{
		return false;
	}

	// 変数
	HCRYPTPROV hCryptProv = NULL;
	HCRYPTKEY hCryptKey = NULL;

	// 暗号化/復号化 の前処理
	if( !PreCryptString( pszKey, &hCryptProv, &hCryptKey ) )
	{
		return false;
	}

	// バッファー確保
	if( 0 == dwSizeSource )
	{
		goto LABEL_ERROR;
	}
	DWORD dwSizeBuffer = dwSizeSource + 100;	// バッファーサイズはソースよりも多少(16以上)多く領域を確保する。
	char* pszBuffer = (char*)malloc( dwSizeBuffer );
	memset( pszBuffer, 0x00, dwSizeBuffer );	// ゼロ埋め。
	memcpy( pszBuffer, pbyteSource, dwSizeSource );

	// 復号化
	DWORD dwSize = dwSizeSource;
	if( !CryptDecrypt( hCryptKey, 0, TRUE, 0, (BYTE*)pszBuffer, &dwSize ) )
	{
		goto LABEL_ERROR;
	}

	// 文字列の終端に「\0」を付加
	pszBuffer[dwSize / sizeof( char )] = '\0';	// dwSizeは、CryptEncrypt()で、復号化結果データサイズになっている。

	// アウトプットに代入
	*ppszDest = pszBuffer;

	PostCryptString( &hCryptProv, &hCryptKey );
	return true;

LABEL_ERROR:
	if( pszBuffer ) { free( pszBuffer ); }
	PostCryptString( &hCryptProv, &hCryptKey );
	return false;
}

// バイト配列を16進数文字列に変換
char* ByteArray2HexString( BYTE* pbyteSource, DWORD dwSizeSource )
{
	DWORD dwSizeBuffer = 0;
	CryptBinaryToStringA( pbyteSource, dwSizeSource, CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF, NULL, &dwSizeBuffer );
	char* pszString = (char*)malloc( dwSizeBuffer );	// 終端文字列分多く確保
	CryptBinaryToStringA( pbyteSource, dwSizeSource, CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF, pszString, &dwSizeBuffer );
	return pszString;
}

// 16進数文字列をバイト配列に変換
void HexString2ByteArray( char* pszHexString, BYTE** ppbyteDest, DWORD* pdwSizeDest )
{
	DWORD dwSizeBuffer = 0;
	CryptStringToBinaryA( pszHexString, strlen( pszHexString ), CRYPT_STRING_HEXRAW, NULL, &dwSizeBuffer, 0, 0 );
	BYTE* pbyteBuffer = (BYTE*)malloc( dwSizeBuffer );
	CryptStringToBinaryA( pszHexString, strlen( pszHexString ), CRYPT_STRING_HEXRAW, pbyteBuffer, &dwSizeBuffer, 0, 0 );
	*ppbyteDest = pbyteBuffer;
	*pdwSizeDest = dwSizeBuffer;
}

int main()
{
	// 変数
	char	szString[] = "abcdあいうえアイウエ亜異迂絵↑↓→←㊤㊦㊨㊧";
	char	szKey[] = "hiramine.com";

	BYTE* pbyteEncrypted = NULL;
	DWORD dwSizeEncrypted = 0;
	char* pszDecrypted = NULL;

	printf( "暗号化前文字列 : %s\n", szString );

	// 文字列の暗号化
	if( !EncryptString( szString, szKey, &pbyteEncrypted, &dwSizeEncrypted ) )
	{
		assert( !"文字列の暗号化に失敗" );
		return -1;
	}
	assert( pbyteEncrypted );

	// バイト配列を16進数文字列に変換
	char* pszEncrypted = ByteArray2HexString( pbyteEncrypted, dwSizeEncrypted );
	printf( "暗号化後文字列 : %s\n", pszEncrypted );

	// 16進数文字列をバイト配列に変換
	BYTE* pbyteEncrypted2 = NULL;
	DWORD dwSizeEncrypted2 = 0;
	HexString2ByteArray( pszEncrypted, &pbyteEncrypted2, &dwSizeEncrypted2 );

	// 暗号化されたデータの復号化
	if( !DecryptString( pbyteEncrypted2, dwSizeEncrypted2, szKey, &pszDecrypted ) )
	{
		free( pbyteEncrypted );
		assert( !"暗号化されたデータの復号化に失敗" );
		return -1;
	}
	assert( pszDecrypted );
	free( pbyteEncrypted );
	free( pszEncrypted );
	free( pbyteEncrypted2 );
	printf( "復号化後文字列 : %s\n", pszDecrypted );

	if( 0 == strcmp( szString, pszDecrypted ) )
	{
		printf( "暗号化前文字列と復号化後文字列は、一致!\n" );
	}
	else
	{
		printf( "暗号化前文字列と復号化後文字列は、不一致!\n" );
	}

	free( pszDecrypted );

	_getch();

	return 0;
}