如何为CredUIPromptForWindowsCredentials()预填充用户名字段
实现目标
从CredUIPromptForWindowsCredentials接口返回结果中提取明文密码用于后续校验,同时给凭据弹窗的用户名字段设置默认填充值,避免用户随意修改。
现存问题
- 使用
CREDUIWIN_GENERIC标记调用接口时,用户名字段无默认值,用户可随意修改 - 尝试替换为
CREDUIWIN_ENUMERATE_CURRENT_USER标记后,返回的密码为加密格式,无法直接读取明文 - 不清楚
pulAuthPackage参数的正确配置方式
原有实现代码
BOOL save = false; DWORD authPackage = 0; LPVOID authBuffer; ULONG authBufferSize = 0; CREDUI_INFO credUiInfo; static WCHAR username[CREDUI_MAX_USERNAME_LENGTH * sizeof(WCHAR)] = { 0 }; static WCHAR password[CREDUI_MAX_PASSWORD_LENGTH * sizeof(WCHAR)] = { 0 }; DWORD uLen = CREDUI_MAX_USERNAME_LENGTH; DWORD pLen = CREDUI_MAX_PASSWORD_LENGTH; credUiInfo.pszCaptionText = TEXT("Authentication"); credUiInfo.pszMessageText = TEXT("Please enter your Key in \"Password\"."); credUiInfo.cbSize = sizeof(credUiInfo); credUiInfo.hbmBanner = NULL; credUiInfo.hwndParent = NULL; LPVOID inBuffer = NULL; ULONG inBufferSize = 0; HRESULT rc = CredUIPromptForWindowsCredentials(&(credUiInfo), 0, &(authPackage), inBuffer, inBufferSize, &authBuffer, &authBufferSize, &(save), CREDUIWIN_GENERIC); if (rc == ERROR_SUCCESS) { CredUnPackAuthenticationBufferW(0, authBuffer, authBufferSize, username, &uLen, NULL, 0, password, &pLen); wstring ws(password); string res(ws.begin(), ws.end()); return res; }
正确实现方法
不需要使用CREDUIWIN_ENUMERATE_CURRENT_USER标记,保持CREDUIWIN_GENERIC标记即可拿到明文密码,预填充用户名的核心是提前构造输入认证缓冲区传入接口,而非修改标记或手动赋值pulAuthPackage:
- 提前定义需要默认填充的用户名字符串
- 调用
CredPackAuthenticationBufferW函数,传入CRED_PACK_GENERIC_CREDENTIALS标记,将预设用户名打包为符合接口要求的输入缓冲区,域名字段传NULL即可 - 将打包好的缓冲区作为
pvInAuthBuffer参数传入CredUIPromptForWindowsCredentials,弹窗打开时就会自动填充预设的用户名 - 接口返回后调用
CredUnPackAuthenticationBufferW解包时,第一个参数同样传入CRED_PACK_GENERIC_CREDENTIALS标记,即可正确解出明文密码 pulAuthPackage参数不需要提前手动赋值,接口调用成功后会自动填充实际使用的认证包ID,不需要额外配置
注意:所有申请的缓冲区使用完成后要调用
CoTaskMemFree释放,密码字段使用完成后要及时清空内存,避免敏感信息残留。
修正后代码示例
BOOL save = false; DWORD authPackage = 0; LPVOID authBuffer = NULL; ULONG authBufferSize = 0; CREDUI_INFO credUiInfo; WCHAR username[CREDUI_MAX_USERNAME_LENGTH] = {0}; WCHAR password[CREDUI_MAX_PASSWORD_LENGTH] = {0}; DWORD uLen = CREDUI_MAX_USERNAME_LENGTH; DWORD pLen = CREDUI_MAX_PASSWORD_LENGTH; // 配置需要默认填充的用户名 const WCHAR* defaultUsername = L"your_default_username"; credUiInfo.pszCaptionText = TEXT("Authentication"); credUiInfo.pszMessageText = TEXT("Please enter your Key in \"Password\"."); credUiInfo.cbSize = sizeof(credUiInfo); credUiInfo.hbmBanner = NULL; credUiInfo.hwndParent = NULL; LPVOID inBuffer = NULL; ULONG inBufferSize = 0; // 第一次调用获取输入缓冲区所需大小 CredPackAuthenticationBufferW(CRED_PACK_GENERIC_CREDENTIALS, (LPWSTR)defaultUsername, NULL, // 需要默认填充密码则传入对应密码字符串,否则传NULL NULL, &inBufferSize); inBuffer = CoTaskMemAlloc(inBufferSize); // 第二次调用完成缓冲区打包 CredPackAuthenticationBufferW(CRED_PACK_GENERIC_CREDENTIALS, (LPWSTR)defaultUsername, NULL, inBuffer, &inBufferSize); HRESULT rc = CredUIPromptForWindowsCredentials(&(credUiInfo), 0, &(authPackage), inBuffer, inBufferSize, &authBuffer, &authBufferSize, &(save), CREDUIWIN_GENERIC); // 释放输入缓冲区 if (inBuffer) CoTaskMemFree(inBuffer); if (rc == ERROR_SUCCESS) { // 传入对应标记解包,即可拿到明文密码 BOOL unPackRc = CredUnPackAuthenticationBufferW(CRED_PACK_GENERIC_CREDENTIALS, authBuffer, authBufferSize, username, &uLen, NULL, 0, // 不需要解析域名字段则传NULL password, &pLen); // 释放输出缓冲区 CoTaskMemFree(authBuffer); if (unPackRc) { wstring ws(password); string res(ws.begin(), ws.end()); // 清空密码内存,避免敏感信息残留 SecureZeroMemory(password, sizeof(password)); return res; } } return "";
内容的提问来源于stack exchange,提问作者Pyhoma
相关产品推荐
相关产品推荐

