我编写了一个服务,通过其目标名称从 Windows 检索 Windows 凭据。 我编写了两种获取凭证的方法。代码在我的机器(Windows 11)中运行良好,但在 Windows 服务器中找不到凭证。
代码= Windows Service(.Net 8编写的后台服务)以管理员身份运行
获取凭证:
我是怎么做到的:
致电 CredRead:
private static string? ReadCredential(string target, Func<CredentialEnum.CREDENTIAL, string> readFunc)
{
if (string.IsNullOrWhiteSpace(target))
{
throw new ArgumentException($"{nameof(target)} cannot be null or empty.", nameof(target));
}
nint credPtr = nint.Zero;
try
{
target = target.Trim();
bool success = CredRead(target, CredentialTypeEnum.CRED_TYPE.GENERIC, 0, out credPtr);
if (success)
{
CredentialEnum.CREDENTIAL credential = Marshal.PtrToStructure<CredentialEnum.CREDENTIAL>(credPtr);
return readFunc(credential);
}
}
catch (Exception ex)
{
Log.Error(ex, "Error reading credential");
}
finally
{
if (credPtr != nint.Zero)
{
CredFree(credPtr);
credPtr = nint.Zero;
}
}
return null;
}
信用阅读:
//https://learn.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credreada
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CredRead(string target, CredentialTypeEnum.CRED_TYPE type, int reservedFlag, out nint credentialPtr);
调用CreadMan.ps1脚本:
private async Task ExecuteCredManCommand(string psCommand, CustomerHost host)
{
string scriptPath = GetScriptFullPath("CredMan.ps1");
psCommand = $"&{scriptPath + " " + $"-GetCred -Target '{host.CredentialName.Trim()}' -CredType Generic"}";
StringBuilder stdOutBuffer = new StringBuilder();
StringBuilder stdErrBuffer = new StringBuilder();
var result = Cli.Wrap("powershell")
.WithArguments($"-Command \"{psCommand}\"")
.WithValidation(CommandResultValidation.None)
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
.ListenAsync();
int exitCode = 0;
await foreach (var cmdEvent in result)
{
if (cmdEvent is ExitedCommandEvent exited)
{
exitCode = exited.ExitCode;
_logger.LogInformation($"Process exited; Code: {exited.ExitCode}");
}
}
// Parse the output to retrieve UserName and Password
string output = stdOutBuffer.ToString();
string[] lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
_logger.LogInformation($"CredMan Output: {lines}");
foreach (var line in lines)
{
//" UserName : k-is\\administrator"
if (line.Trim().StartsWith("UserName"))
{
host.UserName = line.Split(':')[1].Trim();
}
else if (line.Trim().StartsWith("Password"))
{
host.Password = line.Split(':')[1].Trim();
}
}
}
CredMan 函数在服务器上返回错误: 错误是“写入主机”未找到“$Target”作为“$CredType”类型的凭据。“”
#region Reading selected credential
if($GetCred)
{
if(-not $Target)
{
Write-Host "You must supply a target URI."
return
}
# may be [PsUtils.CredMan+Credential] or [Management.Automation.ErrorRecord]
[Object] $Cred = Read-Creds $Target $CredType
if($null -eq $Cred)
{
Write-Host "Credential for '$Target' as '$CredType' type was not found."
return
}
if($Cred -is [Management.Automation.ErrorRecord])
{
return $Cred
}
[String] $CredStr = @"
Found credentials as:
UserName : $($Cred.UserName)
Password : $($Cred.CredentialBlob)
Target : $($Cred.TargetName.Substring($Cred.TargetName.IndexOf("=")+1))
Updated : $([String]::Format("{0:yyyy-MM-dd HH:mm:ss}", $Cred.LastWritten.ToUniversalTime())) UTC
Comment : $($Cred.Comment)
"@
Write-Host $CredStr
}
#endregion
当我在 powershell ISE 中运行 CredMan.ps1 时,它成功返回给定的凭据。 powershell代码是:
cd pathofcredman
.\CredMan.ps1 -GetCred -Target NameOfTarget -CredType Generic
如果有人可以帮助我如何前进,我将不胜感激