Crypto APIによる暗号化

Crypto APIによる暗号化は、ちょっとした用途で暗号化強度がさほど必要でない場合に便利だ。
今回は文字列を暗号化・復号化するルーチンのサンプルを提示する。
もっと強度が必要な場合には、Cryptography API: Next Generationを用いるとよいようだ。
こちらについては、「主筆」さんのHPにある記事がわかりやすい。
http://www.syuhitu.org/other/cng/cng.html


まず、カプセル化。なお以下のコードではSHA-256などは用いることができない。

#include <wincrypt.h>
class TMyCrypt
{
  private:
    HCRYPTPROV hProv;
    HCRYPTHASH hHash;
    HCRYPTKEY  hKey;
    bool is_init();
  public:
    TMyCrypt() : hProv(0), hHash(0), hKey(0) {};
    ~TMyCrypt() { finalize(); }
    bool init( const TCHAR *pass, ALG_ID hash_alg = CALG_SHA1,
                                  ALG_ID enc_alg  = CALG_RC4,
                                  DWORD keyLen    = 0x00800000 );

    void finalize();
    bool encrypt( BYTE  *ret, const TCHAR *src, DWORD &dwLen );
    bool decrypt( TCHAR *ret, const BYTE  *src, DWORD &dwLen );
};
//----------------------------------------------------------------
bool TMyCrypt::is_init()
{
  return (hKey)? true : false;
}
//----------------------------------------------------------------
bool TMyCrypt::init( const TCHAR *pass, ALG_ID hash, 
                                      ALG_ID enc, DWORD dwKeyLen )
{
  finalize(); // 二重突入対策

  if( !CryptAcquireContext( &hProv, NULL, MS_ENHANCED_PROV, 
                              PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) )
    return false;

  if( CryptCreateHash( hProv, hash, 0, 0, &hHash ) )
  {
    DWORD dwLen = ::lstrlen(pass)*sizeof(TCHAR);
    if( CryptHashData( hHash, reinterpret_cast<const BYTE*>(pass),
                                                        dwLen, 0 ) )
      return CryptDeriveKey( hProv, enc, hHash, dwKeyLen, &hKey );
  }

  finalize();
  return false;
}
//----------------------------------------------------------------
void TMyCrypt::finalize()
{
  if( hKey )
  {
    CryptDestroyKey( hKey );
    hKey = 0;
  }
  if( hHash )
  {
    CryptDestroyHash( hHash );
    hHash = 0;
  }
  if( hProv )
  {
    CryptReleaseContext( hProv, 0 );
    hProv = 0;
  }
}
//----------------------------------------------------------------
bool TMyCrypt::encrypt( BYTE *ret, const TCHAR *src, DWORD &dwLen )
{
  if( !is_init() )
    return false;

  dwLen = ::lstrlen(src)*sizeof(TCHAR);
  memcpy( ret, src, dwLen );

  bool bRet = CryptEncrypt( hKey, 0, true, 0, ret, &dwLen, dwLen );

  return bRet;
}
//----------------------------------------------------------------
bool TMyCrypt::decrypt( TCHAR *ret, const BYTE *src, DWORD &dwLen )
{
  if( !is_init() )
    return false;

  memcpy( ret, src, dwLen );

  bool bRet = CryptDecrypt( hKey, 0, true, 0, 
                            reinterpret_cast<BYTE*>(ret), &dwLen );

  return bRet;
}


使用例は、以下のとおり。Crypto自体はバイナリの配列を暗号化するためencryptの返却値はBYTEだ。
バッファサイズについては十分配慮する必要がある。例では手抜きをしている。
今回の例では、カプセル化でパスワードは初期化時に渡しているが、この辺のさじ加減は好み次第だろう。

void test()
{
  TCHAR *src  = T_"TESTWORD";
  TCHAR *pass = T_"DEBUG2";

  TMyCrypt crypt;
  if( !crypt.init( pass ) )
    // crypt.init( pass, CALG_SHA1, CALG_RC4, 0x00800000 )の省略形
    return;

  BYTE  buf[100] = {0};
  TCHAR ret[100] = {0};
  DWORD dwLen;
  if( crypt.encrypt( buf, src, dwLen ) )
  {
    //・・・
    if( crypt.decrypt( ret, buf, dwLen ) )
    {
      //・・・・
    }
  }

  crypt.finalize();
}

コードでTCHARを用いているので、文字列がcharの場合でもwchar_tの場合でも同じように動作する。
今回の例で選択可能なハッシュ鍵生成アルゴリズムは、認証の不要なハッシュアルゴリズムだ。