ハッシュを利用したパスワード付き暗号化は次の手順で行うことができます。
と、こんな感じでできます。キーを作るのは簡単ですし、キーを使って 暗号化したり復号するのも難しくはないです。1.CryptAcquireContext関数でデフォルトのプロバイダのハンドルを取得 2.CryptCreateHash関数でハッシュオブジェクトを作る 3.CryptHashData関数でパスワードをハッシュする 4.CryptDeriveKey関数でパスワードのハッシュを元にサイファーセッション・キーを作る 5.このキーを使ってCryptEncrypt関数で暗号化する(または復号する) 6.後始末
hProvは、使用するプロバイダのハンドルです。BOOL WINAPI CryptCreateHash( HCRYPTPROV hProv, // in ALG_ID Algid, // in HCRYPTKEY hKey, // in DWORD dwFlags, // in HCRYPTHASH *phHash // out );
Algidは、ハッシュ・アルゴリズムの識別IDです。サンプルではCALG_MD5を使っています。
hKeyは、ハッシュ・アルゴリズムがHMACとかMACアルゴリズムのようにかぎ付きハッシュの場合 指定します。鍵なしアルゴリズムの場合は0を指定します。
dwFlagsは、予約済みで0です。
新しいハッシュ・オブジェクトのハンドルのアドレスです。
成功したらTRUE, 失敗したらFALSEが返ります。
ハッシュ・オブジェクトにデータを供給します。BOOL WINAPI CryptHashData( HCRYPTHASH hHash, // in BYTE *pbData, // in DWORD cbData, // in DWORD dwFlags // in );
hHashは、ハッシュ・オブジェクトのハンドルです。
*pbDataは、ハッシュされるデータのアドレスです。
cbDataは、ハッシュされるデータのバイト数です。
dwFlagsはマイクロソフトのベースプロバイダに無視されます。
成功したらTRUE, 失敗したらFALSEが返されます。
ベースデータに由来した暗号キーを生成します。この関数は 同じベースデータから生成されたすべてのキーが同一であることを保証します。 ベースデータにはパスワードや他のユーザーデータが使えます。BOOL WINAPI CryptDeriveKey( HCRYPTPROV hProv, // in ALG_ID Algid, // in HCRYPTHASH hBaseData, // in DWORD dwFlags, // in HCRYPTKEY *phKey // in/out );
hProvは、プロバイダのハンドルです。
Algidは、生成されるキーのアルゴリズムの識別子です。
hBaseDataは、ハッシュ・オブジェクトのハンドルです。
dwFlagsは、生成されるキーのタイプの識別子です。 通常はCRYPT_EXPORTABLEを指定します。
phKeyは、新しく生成されるキー・ハンドルのアドレスです。
成功すればTRUE, 失敗したときはFALSEが返されます。
hHashで示されるハッシュ・オブジェクトを破棄します。BOOL WINAPI CryptDestroyHash( HCRYPTHASH hHash // in );
では、プログラムを見てみましょう。BOOL WINAPI CryptDestroyKey( HCRYPTKEY hKey // in );
普通のメニューとダイアログボックスのリソース・スクリプトですが、 ダイアログの2つのスタティックコントロールに何も表示していないことに注意 してください。このダイアログは暗号化と復号の時使うので場合に応じて スタティックコントロールの表示内容を変えるためです。// hash01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "暗号(&C)" BEGIN MENUITEM "暗号化(&H)", IDM_HASH MENUITEM "復号(&D)", IDM_DECRYPT END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYFNAME DIALOG DISCARDABLE 0, 0, 213, 89 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,91,7,115,15,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,91,29,115,15,ES_AUTOHSCROLL EDITTEXT IDC_EDIT3,91,49,115,15,ES_PASSWORD | ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,7,68,50,14 PUSHBUTTON "キャンセル",IDCANCEL,156,68,50,14 LTEXT "",IDC_STATIC1,7,13,73,8 LTEXT "",IDC_STATIC2,7,33,73,8 LTEXT "パスワード",IDC_STATIC3,7,51,73,8 END
暗号化の場合はszOrgに暗号化するファイル名、szDestに暗号化後の ファイル名が入ります。復号する場合はszOrgに暗号化されたファイル名、 szDestに復号後のファイル名が入ります。// hash01.cpp #define _WIN32_WINNT 0x0500 #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include <wincrypt.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyFNameProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MyHash(HWND); BOOL MyDecrypt(HWND); char szClassName[] = "hash01"; //ウィンドウクラス HINSTANCE hInst; char szDest[MAX_PATH]; char szOrg[MAX_PATH]; CHAR szPassword[256]; BOOL bEncrypt; //暗号化のためのダイアログかどうか
いつもとほとんど変わりませんが、親ウインドウのサイズを少し小さめにしてあります。int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかる暗号化", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 220, //幅 130, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
親ウィンドウのプロシージャです。 メニューからIDM_HASHが選択されたらMyHash関数を呼んで暗号化します。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_HASH: MyHash(hWnd); break; case IDM_DECRYPT: MyDecrypt(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
メニューからIDM_DECRYPTが選択されたらMyDecrypt関数を呼んで復号します。
ファイル名とパスワードを入力するダイアログを出す前に、グローバル変数の bEncryptをTRUEにして、これから出すダイアログは暗号化のためのもので あることをダイアログのプロシージャに知らせます。BOOL MyHash(HWND hWnd) { HCRYPTPROV hProv = 0; HCRYPTKEY hKey = 0; HCRYPTHASH hHash = 0; char szBuffer[1024 + 64]; DWORD dwLength, dwRead, dwWrite; HANDLE hOrg, hDest; BOOL bEnd = FALSE; int id; bEncrypt = TRUE;//暗号化のためのダイアログ id = DialogBox(hInst, "MYFNAME", hWnd, (DLGPROC)MyFNameProc); if (id == IDCANCEL) return FALSE; if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) { MessageBox(hWnd, "CryptAcquire Error", "Error", MB_OK); return FALSE; } if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { MessageBox(hWnd, "CryptCreateHash Error", "Error", MB_OK); CryptReleaseContext(hProv, 0); return FALSE; } dwLength = strlen(szPassword); if (!CryptHashData(hHash, (BYTE *)szPassword, dwLength, 0)) { MessageBox(hWnd, "CryptHashData Error", "Error", MB_OK); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); return FALSE; } if (!CryptDeriveKey(hProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hKey)) { MessageBox(hWnd, "CryptDeriveKey Error", "Error", MB_OK); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); return FALSE; } hOrg = CreateFile(szOrg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hOrg == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "元のファイルをオープンできません", "Error", MB_OK); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); return FALSE; } hDest = CreateFile(szDest, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hOrg == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "暗号化ファイルをオープンできません", "Error", MB_OK); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); return FALSE; } while (1) { ReadFile(hOrg, szBuffer, 1024, &dwRead, NULL); if (dwRead < 1024) bEnd = TRUE; else bEnd = FALSE; CryptEncrypt(hKey, 0, bEnd, 0, (BYTE *)szBuffer, &dwRead, sizeof(szBuffer)); WriteFile(hDest, szBuffer, dwRead, &dwWrite, NULL); if (bEnd) break; } CloseHandle(hOrg); CloseHandle(hDest); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); MessageBox(hWnd, "暗号化完了しました", "完了", MB_OK); return TRUE; }
その後、CryptDeriveKey関数でhKeyを取得するまでは、最初の説明の通りの手順です。 その後は、元になるファイルと暗号化後のファイルをオープンして、 ReadFile関数で元ファイルを読んで、CryptEncrypt関数で暗号化してWriteFileで 暗号化後ファイルに書き込みます。
ダイアログを出す前にグローバル変数のbEncryptをFALSEにして、 これから出すダイアログが復号のためのものであることをダイアログの プロシージャに知らせます。BOOL MyDecrypt(HWND hWnd) { HCRYPTPROV hProv = 0; HCRYPTKEY hKey = 0; HCRYPTHASH hHash = 0; char szBuffer[1024 + 64]; DWORD dwLength, dwRead, dwWrite; HANDLE hOrg, hDest; BOOL bEnd = FALSE; int id; bEncrypt = FALSE; id = DialogBox(hInst, "MYFNAME", hWnd, (DLGPROC)MyFNameProc); if (id == IDCANCEL) return FALSE; if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) { MessageBox(hWnd, "CryptAcquireContext Error", "Error", MB_OK); return FALSE; } if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { MessageBox(hWnd, "CryptCreateHash Error", "Error", MB_OK); CryptReleaseContext(hProv, 0); return FALSE; } dwLength = strlen(szPassword); if (!CryptHashData(hHash, (BYTE *)szPassword, dwLength, 0)) { MessageBox(hWnd, "CryptHashData Error", "Error", MB_OK); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); return FALSE; } if (!CryptDeriveKey(hProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hKey)) { MessageBox(hWnd, "CryptDeriveKey Error", "Error", MB_OK); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); return FALSE; } hOrg = CreateFile(szOrg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hOrg == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "元のファイルをオープンできません", "Error", MB_OK); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); return FALSE; } hDest = CreateFile(szDest, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hOrg == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "復号化ファイルをオープンできません", "Error", MB_OK); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); return FALSE; } while (1) { ReadFile(hOrg, szBuffer, 1024, &dwRead, NULL); if (dwRead < 1024) bEnd = TRUE; else bEnd = FALSE; CryptDecrypt(hKey, 0, bEnd, 0, (BYTE *)szBuffer, &dwRead); WriteFile(hDest, szBuffer, dwRead, &dwWrite, NULL); if (bEnd) break; } CloseHandle(hOrg); CloseHandle(hDest); CryptDestroyHash(hHash); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); MessageBox(hWnd, "復号完了しました", "完了", MB_OK); return TRUE; }
後の手順は暗号化の時とほとんど同じです。
ダイアログのプロシージャです。LRESULT CALLBACK MyFNameProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit1, hEdit2, hEdit3; HWND hStatic1, hStatic2; switch (msg) { case WM_INITDIALOG: hEdit1 = GetDlgItem(hDlg, IDC_EDIT1); hEdit2 = GetDlgItem(hDlg, IDC_EDIT2); hEdit3 = GetDlgItem(hDlg, IDC_EDIT3); hStatic1 = GetDlgItem(hDlg, IDC_STATIC1); hStatic2 = GetDlgItem(hDlg, IDC_STATIC2); if (bEncrypt) { SetWindowText(hDlg, "ファイルの暗号化"); SetWindowText(hStatic1, "元になるファイル名"); SetWindowText(hStatic2, "暗号化後のファイル名"); } else { SetWindowText(hDlg, "ファイルの復号"); SetWindowText(hStatic1, "暗号化されたファイル名"); SetWindowText(hStatic2, "復号後のファイル名"); } return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hEdit1, szOrg, sizeof(szOrg)); Edit_GetText(hEdit2, szDest, sizeof(szDest)); Edit_GetText(hEdit3, szPassword, sizeof(szPassword)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }
WM_INITDIALOGメッセージが来たらエディットコントロールとスタティックコントロールの ハンドルを取得しておきます。そして、グローバル変数bEncryptの値によって スタティックコントロールやダイアログのタイトルバーに表示する文字列を変えます。
OKボタンが押されたらファイル名やパスワードをグローバル変数に格納して、ダイアログを 閉じます。
このプログラムではテキストファイル以外のファイルも暗号化できます。 このプログラムを持っていて、かつパスワードを知っている人以外は 暗号化されたファイルを解読するのはほとんど不可能です。(特殊な人にとっては 容易かもしれません・・・)
また、暗号化するときパスワードを入力しなくても暗号化されます。 この場合復号するときはパスワードを入力しなければ復号できます。
ファイル名を入力するときいちいちパスも入力するのは面倒です。 コモンダイアログを使ってこの手間を省くように改良してみてください。
Update 26/Apr/2000 By Y.Kumei