2011年07月27日

ドングル2

お久しぶりです。
珍しく前回からネタが続く、good sun晴れこと山口です。

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

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

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

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

#include <windows.h>
#include <wincrypt.h>
#include <memory>
#include <stdio.h>

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<BYTE> privateKey(new BYTE[privateKeySize]);
CryptExportKey(genKey, 0, PRIVATEKEYBLOB, 0, privateKey.get(), &privateKeySize);
// 公開鍵です
DWORD publicKeySize;
CryptExportKey(genKey, 0, PUBLICKEYBLOB, 0, NULL, &publicKeySize);
std::auto_ptr<BYTE> 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<BYTE> 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を使えば簡単に暗号化も出来てしまいますね。
本来はこの暗号化の仕組み自体を作ると言うのも面白いのですが、
使えるものを上手く使うことは時間節約にも繋がります。
ここで浮いた時間を本来のアプリケーションにつぎ込むとより良いものになると思いますパンチ
次回も続く予定ですが、少し違った方向性で行きたいと思います。
ではまた手(パー)

posted by 管理人 at 21:20 | プログラミング