从凭证提供程序向C#服务提供凭证信息

问题描述:

我写了一个自定义Windows Credentials Provider来捕获用户登录名。此提供程序的主要目的之一是存储用户的密码,并在登录时将其用于Windows上运行的C#服务。从凭证提供程序向C#服务提供凭证信息

这导致我发现存储凭证信息的位置和方式的问题。在登录时(即用户提供有效的登录信息并选择登录),服务应该能够获取信息并使用它。

什么是将证书信息传递给服务的正确方式?

该登录用户提供的凭证都或多或少根据对凭据提供所提供的微软样品,其中GetSerialization看起来像这样的部分:

hr = ProtectIfNecessaryAndCopyPassword(_rgFieldStrings[SFI_PASSWORD], _cpus, &pwzProtectedPassword); 

g_pwzProtectedPassword = pwzProtectedPassword; 

if (SUCCEEDED(hr)) 
{ 
    PWSTR pszDomain; 
    PWSTR pszUsername; 
    hr = SplitDomainAndUsername(_pszQualifiedUserName, &pszDomain, &pszUsername); 
    if (SUCCEEDED(hr)) 
    { 
     KERB_INTERACTIVE_UNLOCK_LOGON kiul; 
     hr = KerbInteractiveUnlockLogonInit(pszDomain, pszUsername, pwzProtectedPassword, _cpus, &kiul); 
     if (SUCCEEDED(hr)) 
     { 
      // We use KERB_INTERACTIVE_UNLOCK_LOGON in both unlock and logon scenarios. It contains a 
      // KERB_INTERACTIVE_LOGON to hold the creds plus a LUID that is filled in for us by Winlogon 
      // as necessary. 
      hr = KerbInteractiveUnlockLogonPack(kiul, &pcpcs->rgbSerialization, &pcpcs->cbSerialization); 
      if (SUCCEEDED(hr)) 
      { 
       ULONG ulAuthPackage; 
       hr = RetrieveNegotiateAuthPackage(&ulAuthPackage); 
       if (SUCCEEDED(hr)) 
       { 
        pcpcs->ulAuthenticationPackage = ulAuthPackage; 
        pcpcs->clsidCredentialProvider = CLSID_CredentialProvider; 
        // At this point the credential has created the serialized credential used for logon 
        // By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know 
        // that we have all the information we need and it should attempt to submit the 
        // serialized credential. 
        *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED; 
       } 
      } 
     } 
     CoTaskMemFree(pszDomain); 
     CoTaskMemFree(pszUsername); 
    } 
    CoTaskMemFree(pwzProtectedPassword); 
} 

编辑:

我试着使用Windows Credential Manager API来保存密码,但似乎没有效果。当我尝试获取登录后的用户名/密码时,我什么也没有返回。

DWORD cbCreds = (DWORD)(1 + strlen(password)); 

CREDENTIALW cred = { 0 }; 
cred.Type = CRED_TYPE_GENERIC; 
cred.TargetName = TargetName; 
cred.CredentialBlobSize = cbCreds; 
cred.CredentialBlob = (LPBYTE)password; 
cred.Persist = CRED_PERSIST_LOCAL_MACHINE; 
cred.UserName = *username; 

return ::CredWriteW(&cred, 0) ? true : false; 

认为管道将是与服务进行通信的正确方式。 使用Windows Credential Manager保存/加载密码时,由于登录会话不同并且用户的SID在未登录时被禁用,所以不起作用。