在 Windows Server 上服务失败时从 Windows 凭据管理器获取凭据

问题描述 投票:0回答:1

我编写了一个服务,通过其目标名称从 Windows 检索 Windows 凭据。 我编写了两种获取凭证的方法。代码在我的机器(Windows 11)中运行良好,但在 Windows 服务器中找不到凭证。

代码= Windows Service(.Net 8编写的后台服务)以管理员身份运行

获取凭证:

  1. 方式 1 : CredMan.ps1 返回退出代码 0 但 credman 脚本错误
  2. 方式2:Advapi32.dll返回错误1168“ERROR_NOT_FOUND”

我是怎么做到的:

致电 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

如果有人可以帮助我如何前进,我将不胜感激

c# powershell windows-services credentials windows-server
1个回答
0
投票

正如hier所说问题是服务作为“本地系统帐户”运行,甚至它具有管理权限,但Windows凭据不允许它访问凭据。为了访问凭证,用户应该与凭证的创建者是同一用户。 最简单的方法是右键单击“Windows 服务”中的服务并说以用户身份运行服务...但正确的方法是定义一个服务帐户来完成这项工作。

© www.soinside.com 2019 - 2024. All rights reserved.