EternalWindows
Credentials Management / クレデンシャルの列挙

CredUIPromptForCredentialsやCredWriteによって保存されたクレデンシャルは、 CredEnumerateによって列挙することができます。

BOOL CredEnumerate(
  LPCTSTR Filter,
  DWORD Flags,
  DWORD *Count,
  PCREDENTIAL **Credentials
);

Filterは、列挙したいクレデンシャルをフィルタする文字列を指定します。 たとえば、"s*"を指定すると、sから始まるターゲット名を持つクレデンシャルが列挙されます。 フィルタをしない場合はNULLを指定します。 Flagsは、0で問題ありません。 Countは、列挙されたクレデンシャルの数を受け取る変数のアドレスを指定します。 Credentialsは、列挙されたクレデンシャルを受け取る変数のアドレスを指定します。

CREDENTIAL構造体は、次のように定義されています。

typedef struct _CREDENTIAL {
  DWORD                 Flags;
  DWORD                 Type;
  LPTSTR                TargetName;
  LPTSTR                Comment;
  FILETIME              LastWritten;
  DWORD                 CredentialBlobSize;
  LPBYTE                CredentialBlob;
  DWORD                 Persist;
  DWORD                 AttributeCount;
  PCREDENTIAL_ATTRIBUTE Attributes;
  LPTSTR                TargetAlias;
  LPTSTR                UserName;
}CREDENTIAL, *PCREDENTIAL;

Flagsは、クレデンシャルの特徴を定義する定数が格納されます。 通常は、0になります。 Typeは、クレデンシャルのタイプが格納されます。 通常は、クレデンシャルがユーザー名とパスワードを基にしていることを示すCRED_TYPE_GENERIC(1)です。 TargetNameは、クレデンシャルを識別するターゲット名が格納されます。 Commentは、クレデンシャルに対するコメント文字列が格納されます。 LastWrittenは、クレデンシャルが最後に書き込まれた時間がUTC形式で格納されます。 CredentialBlobSizeは、CredentialBlobのサイズが格納されます。 CredentialBlobは、クレデンシャルのデータが格納されます。 Persistは、クレデンシャルがどのように保存されるかを表す定数が格納されます。 CRED_PERSIST_ENTERPRISE(3)の場合は、クレデンシャルが永続的に保存されます。 AttributeCountは、Attributesの要素数が格納されます。 Attributesは、アプリケーション定義値が格納されます。 TargetAliasは、TargetNameのエイリアス名が格納されます。 UserNameは、クレデンシャルに関連するユーザー名が格納されます。

不要になったクレデンシャルは、CredDeleteで削除することができます。

BOOL CredDelete(
  LPCTSTR TargetName,
  DWORD Type,
  DWORD Flags
);

TargetNameは、削除したいクレデンシャルのターゲット名を指定します。 Typeは、削除したいクレデンシャルのタイプを指定します。 Flagsは、予約されているため0を指定します。

今回のプログラムは、各クレデンシャルのターゲット名をリストボックスに列挙します。 項目が選択された場合は、エディットコントロールにクレデンシャルの詳細を表示します。 項目を選択している状態でDelキーを押すと、そのクレデンシャルを削除することができます。

#include <windows.h>
#include <wincred.h>

#pragma comment (lib, "credui.lib")
#pragma comment (lib, "crypt32.lib")

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND        hwndListBox = NULL;
	static HWND        hwndEdit = NULL;
	static PCREDENTIAL *pCredential;
	
	switch (uMsg) {

	case WM_CREATE: {
		DWORD i, dwCount;

		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY | LBS_WANTKEYBOARDINPUT, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndEdit = CreateWindowEx(0, TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_READONLY, 0, 0, 0, 0, hwnd, (HMENU)2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);	

		CredEnumerate(NULL, 0, &dwCount, &pCredential);
		for (i = 0; i < dwCount; i++)
			SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)pCredential[i]->TargetName);

		return 0;
	}
	
	case WM_COMMAND: {
		int   nIndex;
		TCHAR szBuf[1024];
		DWORD dwSize;

		if ((HWND)lParam != hwndListBox || HIWORD(wParam) != LBN_SELCHANGE)
			return 0;
		
		nIndex = (int)SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);

		SetWindowText(hwndEdit, TEXT(""));

		wsprintf(szBuf, TEXT("Flags : %d\r\n"), pCredential[nIndex]->Flags);
		SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szBuf);
		
		wsprintf(szBuf, TEXT("Type : %d\r\n"), pCredential[nIndex]->Type);
		SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szBuf);
		
		wsprintf(szBuf, TEXT("Persist : %d\r\n"), pCredential[nIndex]->Persist);
		SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szBuf);
		
		wsprintf(szBuf, TEXT("UserName : %s\r\n"), pCredential[nIndex]->UserName);
		SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szBuf);
		
		wsprintf(szBuf, TEXT("Data : \r\n"));
		SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szBuf);

		dwSize = sizeof(szBuf);
		ZeroMemory(szBuf, sizeof(szBuf));
		CryptBinaryToString(pCredential[nIndex]->CredentialBlob, pCredential[nIndex]->CredentialBlobSize, CRYPT_STRING_HEXASCII, szBuf, &dwSize);
		SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szBuf);

		return 0;
	}

	case WM_VKEYTOITEM:
		if (LOWORD(wParam) == VK_DELETE) {
			int   nIndex;
			DWORD i, dwCount;

			if (MessageBox(hwnd, TEXT("このクレデンシャルを削除しますか"), TEXT("確認"), MB_YESNO) == IDYES) {
				nIndex = (int)SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
				CredDelete(pCredential[nIndex]->TargetName, pCredential[nIndex]->Type, 0);

				SendMessage(hwndListBox, LB_RESETCONTENT, 0, 0);
				CredFree(pCredential);
				CredEnumerate(NULL, 0, &dwCount, &pCredential);
				for (i = 0; i < dwCount; i++)
					SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)pCredential[i]->TargetName);
			}
		}
		break;

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam) / 2, HIWORD(lParam), TRUE);
		MoveWindow(hwndEdit, LOWORD(lParam) / 2, 0, LOWORD(lParam) / 2, HIWORD(lParam), TRUE);
		return 0;

	case WM_CTLCOLORSTATIC:
		return (LRESULT)GetStockObject(WHITE_BRUSH);

	case WM_DESTROY:
		if (pCredential != NULL)
			CredFree(pCredential);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

WM_CREATEではCredEnumerateを呼び出してCREDENTIAL構造体の配列を取得し、 この数だけターゲット名をリストボックスに追加しています。 コントロールの作成について少し工夫しているのは、 リストボックスのウインドウスタイルにLBS_WANTKEYBOARDINPUTを指定している点です。 これにより、キーが押された場合にWM_VKEYTOITEMが送られることになります。 また、エディットコントロールを読み取り専用にするためにES_READONLYを指定し、 このとき背景が灰色になるのをWM_CTLCOLORSTATICで防いでいます。

WM_COMMANDではリストボックスの項目が選択されたかどうかを調べ、 選択されている場合はそのクレデンシャルの情報をエディットコントロールに表示します。 基本的にはメンバの中身を直接扱っているだけですが、 CredentialBlobメンバについてはCryptBinaryToStringを呼び出して16進数形式の文字列を取得しています。 この関数を呼び出す場合は、crypt32.libへのリンクが必要になります。

WM_VKEYTOITEMのwParamの下位ワードには、押下された仮想キーコードが格納されています。 これがVK_DELETEである場合は、選択されているクレデンシャルを削除するということで、 LB_GETCURSELでインデックスを取得し、pCredential[nIndex]のメンバをCredDeleteに指定しています。


戻る