プログラムTIPS

DEMO PROGRAM ヘキサドライブでは様々な研究開発をしています。その一部を紹介。

ドングル2(2011年7月27日)

前回ご紹介した「USBをハードウェアキーとして利用する」の続きをやってみたいと思います。
今回注目するのはライセンスキー発行に繋がる部分です。

ライセンスキーの流れは普通はこんな感じでしょうか。
まずユーザーはハードウェアキー情報をライセンス管理者に送信して、
管理者はその情報を基にライセンスキーを発行してユーザーに渡します。
アプリケーションはライセンスキー情報とハードウェアキー情報を照合して、
対になるデータであることを確認したらアプリケーションを正常起動します。

今回はハードウェアキーの情報を管理者に安全に届けるための方法を考えたいと思います。
公開鍵と秘密鍵を使った暗号化というのがあります。
公開鍵で暗号化されたデータは秘密鍵だけで復号化出来るというものです。
この仕組を利用して
アプリケーションには公開鍵情報を埋め込み、
この鍵を利用してハードウェアキー情報を暗号化して、
管理者のもつ秘密鍵で復号化してライセンスキーを作成するという流れが出来そうです。

ではどのようにこの暗号化、復号化を行うかですが…
今回もAPIを利用したコードをネットで調べてC++で動作するようにしてみました。
Windows7にてVS2010のコンソールアプリとして動作確認しています。

#include 
#include 
#include 
#include 
 
void printCode(const char* str, const BYTE* code, DWORD size)
{
    printf("%s from>>\n", str);
    for(DWORD i = 0; i < size; i++){
        printf("%c", code[i]);
    }
    printf("\n<<to\n");
}
 
int main(void)
{
    // RSA用にコンテキストを有効化します
    HCRYPTPROV cryptprov;
    CryptAcquireContext(&cryptprov, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0 );
    // 鍵の自動生成
    HCRYPTKEY genKey;
    // 第三引数の上位16bitが0の場合はデフォルトのキー長が使われる
    CryptGenKey(cryptprov, CALG_RSA_KEYX, CRYPT_EXPORTABLE, &genKey);
    // 秘密鍵です
    DWORD   privateKeySize;
    CryptExportKey(genKey, 0, PRIVATEKEYBLOB, 0, NULL, &privateKeySize);
    std::auto_ptr privateKey(new BYTE[privateKeySize]);
    CryptExportKey(genKey, 0, PRIVATEKEYBLOB, 0, privateKey.get(), &privateKeySize);
    // 公開鍵です
    DWORD   publicKeySize;
    CryptExportKey(genKey, 0, PUBLICKEYBLOB, 0, NULL, &publicKeySize);
    std::auto_ptr publicKey(new BYTE[publicKeySize]);
    CryptExportKey(genKey, 0, PUBLICKEYBLOB, 0, publicKey.get(), &publicKeySize);
 
    // 作成された鍵で暗号化と復号化が出来るか試してみる
    // キーをインポートしたと想定、公開鍵を読み込んで改めてキーを作成する
    HCRYPTKEY pubImportKey;
    if(!CryptImportKey(cryptprov, publicKey.get(), publicKeySize, 0, 0, &pubImportKey)){
        return 0;
    }
    // まずは暗号化
    const char* originalCode = "hello world";
    DWORD originalSize = strlen(originalCode);
    DWORD encryptSize = originalSize;
    // 一度暗号化データのサイズを調べる
    CryptEncrypt(pubImportKey, 0, true, 0, (BYTE*)originalCode, &encryptSize, 0);
    std::auto_ptr encryptBuffer(new BYTE[encryptSize]);
    memcpy(encryptBuffer.get(), originalCode, originalSize);
    printCode("オリジナルコード", encryptBuffer.get(), originalSize);
    // 暗号化する
    if(!CryptEncrypt(pubImportKey, 0, true, 0, encryptBuffer.get(), &originalSize, encryptSize)){
        return 0;
    }
    printCode("暗号化したコード", encryptBuffer.get(), encryptSize);
 
    // 続いて復号化
    // キーをインポートしたと想定、秘密鍵を読み込んで改めてキーを作成する
    HCRYPTKEY prvImportKey;
    if(!CryptImportKey(cryptprov, privateKey.get(), privateKeySize, 0, 0, &prvImportKey)){
        return 0;
    }
    if(!CryptDecrypt(prvImportKey, 0, true, 0, encryptBuffer.get(), &encryptSize)){
        return 0;
    }
    printCode("復号化したコード", encryptBuffer.get(), encryptSize);
    // 本来は最初に鍵を作ったら何処かにとって置かないと毎回違う鍵が作られるので要注意です
 
    CryptDestroyKey(prvImportKey);
    CryptDestroyKey(pubImportKey);
    CryptDestroyKey(genKey);
    CryptReleaseContext(cryptprov, 0);
 
    return 0;

APIを使えば簡単に暗号化も出来てしまいますね。
本来はこの暗号化の仕組み自体を作ると言うのも面白いのですが、
使えるものを上手く使うことは時間節約にも繋がります。
ここで浮いた時間を本来のアプリケーションにつぎ込むとより良いものになると思います。

【免責事項】

本サイトが提供している情報に関しては、安全性等、いかなる保証もされません。 株式会社ヘキサドライブは、これらの情報をあなたが利用することによって生ずるいかなる損害に対しても一切責任を負いません。

【著作権】

本サイトが提供しているコンテンツについては、特に断りのある場合を除き、株式会社ヘキサドライブが著作権を有します。