【问题标题】:Accessing Windows Credential Manager from PowerShell从 PowerShell 访问 Windows 凭据管理器
【发布时间】:2015-03-17 15:32:46
【问题描述】:

我在 Windows Server 2008 R2 上使用 PowerShell 2.0(因为 SP2010 是必需的)。我需要从 Windows 凭据管理器中检索进程的凭据。我似乎无法让它工作。

我得到了这段代码:

[Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
(new-object Windows.Security.Credentials.PasswordVault).RetrieveAll() | % { $_.RetrievePassword(); $_ }

两行代码都报错

Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime : Unable to find type [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]: make sure that the assembly containing this type is loaded.

(new-object Windows.Security.Credentials.PasswordVault).RetrieveAll() | % {$_.RetrievePassword(); $_ } 

分别。我一直在尝试以某种方式导入 PasswordVault 类。到目前为止,Google 让我失望了,我什至无法找出它驻留在哪个程序集中。我错过了什么?

【问题讨论】:

    标签: powershell credential-manager


    【解决方案1】:

    来自微软的SecretManagement and SecretStore 似乎是官方解决方案。他们是officially released on March 25, 2021。秘密可以是凭证。

    Install-Module Microsoft.PowerShell.SecretManagement
    Install-Module SecretManagement.JustinGrote.CredMan  # windows credential manager
    Register-SecretVault SecretManagement.JustinGrote.CredMan
    Set-Secret -Name TestSecret -Secret "TestSecret"
    Get-Secret -Name TestSecret -AsPlainText
    
    TestSecret
    

    【讨论】:

    • 可悲的是,在发布一年后 (02/2020) 它仍处于(预) alpha 阶段,距离准备生产代码还有数英里。 ? 负责任的 MS 团队不会处理面对新版本 PowerShell、PowerShellGet 或此特定模块的工作量。
    • 模块于 2021 年 3 月 25 日正式发布(1.0),并于 2021 年 7 月 13 日更新(1.1)。KUTlime 的评论不再适用。
    • 另请注意,SecretManagement powershell 模块不能使用任意凭证名称,因为此模块在凭证名称中添加 ps:。例如,您将无法存储和检索git: 凭据。
    【解决方案2】:

    在 powershell5 中输入:

       Install-Module CredentialManager -force
    

    然后

       New-StoredCredential -Target $url -Username $ENV:Username -Pass ....
    

    以后

       Get-StoredCredential -Target .... 
    

    模块的源代码是https://github.com/davotronic5000/PowerShell_Credential_Manager

    【讨论】:

    【解决方案3】:

    如果有人只想要一个代码 sn-p 以便他们可以分发脚本而不必指示最终用户安装模块或包含 DLL 文件,那么这应该可以解决问题。

    $code = @"
    using System.Text;
    using System;
    using System.Runtime.InteropServices;
    
    namespace CredManager {
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      public struct CredentialMem
      {
        public int flags;
        public int type;
        public string targetName;
        public string comment;
        public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
        public int credentialBlobSize;
        public IntPtr credentialBlob;
        public int persist;
        public int attributeCount;
        public IntPtr credAttribute;
        public string targetAlias;
        public string userName;
      }
    
      public class Credential {
        public string target;
        public string username;
        public string password;
        public Credential(string target, string username, string password) {
          this.target = target;
          this.username = username;
          this.password = password;
        }
      }
    
      public class Util
      {
        [DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern bool CredRead(string target, int type, int reservedFlag, out IntPtr credentialPtr);
    
        public static Credential GetUserCredential(string target)
        {
          CredentialMem credMem;
          IntPtr credPtr;
    
          if (CredRead(target, 1, 0, out credPtr))
          {
            credMem = Marshal.PtrToStructure<CredentialMem>(credPtr);
            byte[] passwordBytes = new byte[credMem.credentialBlobSize];
            Marshal.Copy(credMem.credentialBlob, passwordBytes, 0, credMem.credentialBlobSize);
            Credential cred = new Credential(credMem.targetName, credMem.userName, Encoding.Unicode.GetString(passwordBytes));
            return cred;
          } else {
            throw new Exception("Failed to retrieve credentials");
          }
        }
    
        [DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
        private static extern bool CredWrite([In] ref CredentialMem userCredential, [In] int flags);
    
        public static void SetUserCredential(string target, string userName, string password)
        {
          CredentialMem userCredential = new CredentialMem();
    
          userCredential.targetName = target;
          userCredential.type = 1;
          userCredential.userName = userName;
          userCredential.attributeCount = 0;
          userCredential.persist = 3;
          byte[] bpassword = Encoding.Unicode.GetBytes(password);
          userCredential.credentialBlobSize = (int)bpassword.Length;
          userCredential.credentialBlob = Marshal.StringToCoTaskMemUni(password);
          if (!CredWrite(ref userCredential, 0))
          {
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
          }
        }
      }
    }
    "@
    Add-Type -TypeDefinition $code -Language CSharp
    # How to store credentials
    [CredManager.Util]::SetUserCredential("Application Name", "Username", "Password")
    # How to retrieve credentials
    [CredManager.Util]::GetUserCredential("Application Name")
    # How to just get the password
    [CredManager.Util]::GetUserCredential("Application Name").password
    

    上述代码在 PowerShell 7 和 PowerShell 5 上进行了测试,但如果您使用后者,您可能需要较新版本的 .Net 框架。

    编辑:在代码 sn-p 中添加了缺失的分号,如 @alazyworkaholic 所述

    【讨论】:

    • 这对我来说效果很好。喜欢我不需要下载或安装任何模块的方式。谢谢!
    • public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten 行末尾缺少 ;。把它放进去,它就可以工作了。
    【解决方案4】:

    我发现了一个非常好的帖子,这段代码将打印所有用户名、资源和密码

    [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
    $vault = New-Object Windows.Security.Credentials.PasswordVault
    $vault.RetrieveAll() | % { $_.RetrievePassword();$_ }
    

    感谢Todd's post

    【讨论】:

      【解决方案5】:

      根据文档,Windows Server 2008 R2 不支持 PasswordVault 类。

      支持的最低服务器 Windows Server 2012

      https://msdn.microsoft.com/library/windows/apps/windows.security.credentials.passwordvault.aspx

      【讨论】:

      • 好的,重点。因此我的问题是,如何访问 Windows Server 2008 上的凭据管理器?一定有办法的。
      【解决方案6】:

      您需要访问 Win32 API 才能与凭据管理器进行交互。

      CredMan.ps1 from the Technet scripting gallery 很好地证明了这一点。

      对于更简单的使用模式,例如仅列出主体或添加新凭据,您还可以使用cmdkey,这是一个用于凭据管理的内置 Windows 命令行实用程序

      为了在 PowerShell 中重用存储的凭据,这个家伙似乎找到了一种方法,使用类似于 CredMan.ps1 的技术从凭据存储的通用凭据句柄构建PSCredentialGet-StoredCredential

      【讨论】:

      • 谢谢,只是为了确保在我花时间研究它之前,它能让我找回密码,对吧?
      • 不,至少cmdkey 不会。你为什么要找回密码?
      • 因为我需要使用存储在凭据管理器中的凭据远程调用进程。我认为这部分是不言而喻的,如果不使用存储在其中的凭据,为什么还需要访问凭据管理器?
      • 在没有实施此类解决方案的任何实际经验的情况下,我认为这样做的“正确”方法是通过 SSPI 或类似方法导出令牌或安全上下文,而不是摆弄直接输入明文密码
      • 我在脚本中使用了 System.Management.Automation.PSCredential 的一个实例,所以如果你知道一种从凭据管理器中获取它的方法,我会全力以赴。我是说眼睛。
      猜你喜欢
      • 1970-01-01
      • 2011-11-02
      • 1970-01-01
      • 1970-01-01
      • 2020-02-15
      • 1970-01-01
      • 2022-12-06
      • 2012-03-02
      • 1970-01-01
      相关资源
      最近更新 更多