39

I'm using PowerShell 2.0 (necessary because of SP2010) On Windows Server 2008 R2. I need to retrieve credentials for a process from the Windows Credential Manager. I can't seem to make it work.

I was given this piece of code:

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

both lines of code throw errors

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.

and

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

respectively. I've been trying to somehow import the PasswordVault class. So far Google has failed me, I haven't even been able to find out which assembly it resides in. What am I missing?

1
  • 1
    That piece of code assumes Windows Runtime which was introduced in Windows Server 2012. PasswordVault is not supported on Windows Server 2008. I can't guide you further as I don't have Windows Server 2008 at hand. You would probably need to use alternative APIs, stackoverflow.com/a/67944064 is one example. Commented Jun 23, 2023 at 10:39

6 Answers 6

49

In powershell5 type:

   Install-Module CredentialManager -force

Then

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

and later

   Get-StoredCredential -Target .... 

Source code for the module is https://github.com/davotronic5000/PowerShell_Credential_Manager

== EDIT 2023 ==

Original is archived, install newer fork with:

   Install-Module -Name TUN.CredentialManager

Check out Github repository for more details.

Sign up to request clarification or add additional context in comments.

6 Comments

OP specifically asked for PS2, why is a PS5 answer being voted up so much? Install-Module is not a valid cmdlet in PS2
You can install the module on PS2 nevertheless. Install-Module is only shortcut.
The module is no longer being maintained ("I am no longer working on this project or PowerShell much at all. If anyone else wants to take a fork and continue supporting this project. I would be happy to link to that project from here to guide people in the right direction.").
Hi, I've created a new module forked from the CredentialManager module of Dave, just install TUN.CredentialManager instead of CredentialManager (so call "Install-Module TUN.CredentialManager -Force" ) and you'll get a PowerShell Core compatible version along with more than 512 byte password length support and internal use of secure string instead of storing the clear password (where possible according to cmdlet calls, see new parameters IncludeSecretPassword and ExcludeClearPassword). New website here: github.com/echalone/PowerShell_Credential_Manager
@MarkusSzumovski you should probably inform the original owner so that he can update the description of his Github "I would be happy to link to that project from here to guide people in the right direction." github.com/davotronic5000/PowerShell_Credential_Manager
|
14

You'll need to access the Win32 API to interact with the Credential Manager.

CredMan.ps1 from the Technet scripting gallery nicely demonstrates this.

For simpler usage patterns, like just listing principals or adding new credentials, you can also use cmdkey, a built-in Windows Command-line utility for credential management

For reusing stored Credentials in PowerShell, this guy seems to have found a way to build a PSCredential from a Generic Credential handle from the Credential Store, using a technique similar to that of CredMan.ps1: Get-StoredCredential

7 Comments

Thanks, just to make sure, before I devote time to studying it, it will allow me to retrieve the password, right?
No, at least cmdkey won't. Why would you ever want to retrieve a password?
Because I need to invoke a process remotely with credentials stored in Credential Manager. I thought this part was self-evident, why else would I need to access Credential Manager if not to use the credentials stored inside?
Without having any practical experience with implementing such a solution, I would think the "proper" way to do so would be to export a token or a security context through an SSPI or similar, instead of fiddling with the password in plain-text directly
I'm using an instance of System.Management.Automation.PSCredential in the script, so if you know of a way to get that out of the Credential Manager, I'm all ears. I mean eyes.
|
14

If anybody just wants a code snippet so that they can distribute scripts without having to instruct the end users to install modules or include DLL files, this should do the trick.

$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

The above code was tested on PowerShell 7 and PowerShell 5, though you may need a somewhat recent version of the .Net framework if you're using the latter.

Comments

6

SecretManagement and SecretStore from Microsoft appear to be the official solution. They were officially released on March 25, 2021. The secret can be a credential.

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

4 Comments

The modules was officially released on March 25, 2021 (1.0) and updated (1.1) on July 13, 2021. KUTlime's comment no longer applies.
Please also note that with SecretManagement powershell module you can't use arbitrary credential name, since this module prepends ps: in credential name. For example, you will be unable to store and retrieve git: credentials.
Minimum PowerShell version: 5.1. How is it relevant to the question about PowerShell 2.0?
I am getting error : Set-Secret: Unable to set secret because no vault was provided and there is no default vault designated. What's wrong?
4

According to the docs, the PasswordVault class isn't supported on Windows Server 2008 R2.

Minimum supported server Windows Server 2012

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

1 Comment

Alright, point taken. Therefore my question is, how do I access Credential Manager on Windows Server 2008? There must be a way.
2

A solution for Windows PowerShell v5.1 (and possibly earlier versions) as well as PowerShell (Core) 7:

The third-party BetterCredentials module, available in the PowerShell Gallery, provides programmatic access to CredMan (Windows Credential Manager).

You can install, e.g., with:

Install-Module BetterCredentials -AllowClobber

-AllowClobber is necessary, because the module's Get-Credential command is meant to override the built-in one: it is syntax-compatible with the built-in one while offering additional functionality via additional parameters.[1]

Notably, the -Store switch can be used to store the returned credentials in CredMan.

For more control over how the entry is stored, use Set-Credential.

Get-Credential automatically returns already-stored credentials, if available, and if only a username was originally specified, it is also sufficient for retrieval.

The following commands, which explicitly interact with the CredMan vault, require the full entry target name, as indicated in the .Target property values of the decorated [pscredential] instances that Find-Credential returns.

  • E.g., for credentials stored with Get-Credential -Store -UserName Foo, the .Target property value is LegacyGeneric:target=MicrosoftPowerShell:user=foo

Find-Credential lists all stored credentials by default; while it does have a -Filter parameter, it is severely limited and in my experience doesn't work reliably (as of module version 4.5) - better to use post-filtering with Where-Object, as shown in this answer.

To remove stored credentials, use Remove-Credential.

To test whether credentials with a given target name already exist in the CredMan vault, use Test-Credential.


[1] It's unlikely that you'll need to, but you can still invoke the overridden cmdlet, namely via Microsoft.PowerShell.Security\Get-Credential

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.