Summary: Microsoft Scripting Guy, Ed Wilson, shows how to easily decrypt the Windows PowerShell secure string password.
Hey, Scripting Guy! We have an FTP site that I have to use on a regular basis. I need an easy way to get a credential and use that credential with the FTP site so that I can download a file that changes on a daily basis. I found a few scripts and functions on the Internet that will decrypt the secure string password from a Windows PowerShell credential object, but they all seem really complicated. Is there an easy way to do this?
—AD
Hello AD,
Microsoft Scripting Guy, Ed Wilson, is here. Today I am sitting here drinking a cup of Earl Grey tea with a pinch of lavender in it. I love the way the two flavors complement each other. As I look over my email, I ran across your question. Although you did not include a link to the complicated versionsof the scripts and functions you ran across, I will venture to say that my method should be relatively painless.
Note What I am showing here today is exactly by design, and is not a hack.
Using Get-Credential for credentials
I love using the Get-Credential cmdlet to retrieve a credential object. It is already set up to work; and therefore, it is easy to use. If I need credentials, I do not need to mess around writing HTAs, creating various WinForms to prompt for a user name and password, or worry about how to mask the password—all of which were problems before Windows PowerShell and the Get-Credential cmdlet. So, like I say, I absolutely love it. To use the Get-Credential cmdlet, I generally store the resulting credential object in a variable. (I do not have to do so, because I can use it directly if I need to, but it is more flexible to store it in a variable.) Here is the command.
$credential = Get-Credential
When I run this command, a dialog box appears. The box is already set up to use, with a user name on the top, and it masks the password in the bottom box. The box is shown here.
The problem with Get-Credential
The problem with the Get-Credential cmdlet is that it returns a PSCredential object. In itself, this is not an issue, but it does mean that I can only use the credential object for cmdlets and for other items that know what a PSCredential object is. Not even all .NET classes know how to deal with a PSCredential object, so when it comes to connecting to legacy databases and things like websites and FTP sites, there is little hope of being able to use the object directly.
Luckily, I can use the Get-Member cmdlet to look at the members of a PSCredential object. The members are listed here.
PS C:\> $credential | gm
TypeName: System.Management.Automation.PSCredential
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetNetworkCredential Method System.Net.NetworkCredential GetNetworkCredential()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.S...
GetType Method type GetType()
ToString Method string ToString()
Password Property securestring Password {get;}
UserName Property string UserName {get;}
At first, it looks like things are relatively easy. If I want to see the user name, all I need to do is access the UserNameproperty as shown here.
PS C:\> $credential.UserName
mydomain\someuser
That is cool. Now what about the password? When I access the Passwordproperty, it returns SecureString. This is shown here.
PS C:\> $credential.Password
System.Security.SecureString
Hmmm, what if I look at the members of SecureString? I pipe it to the Get-Member cmdlet, and I see the following members.
PS C:\> $credential.Password | gm
TypeName: System.Security.SecureString
Name MemberType Definition
---- ---------- ----------
AppendChar Method void AppendChar(char c)
Clear Method void Clear()
Copy Method securestring Copy()
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
InsertAt Method void InsertAt(int index, char c)
IsReadOnly Method bool IsReadOnly()
MakeReadOnly Method void MakeReadOnly()
RemoveAt Method void RemoveAt(int index)
SetAt Method void SetAt(int index, char c)
ToString Method string ToString()
Length Property int Length {get;}
Ok, cool. I will try the ToStringmethod and see what happens.
PS C:\> $credential.Password.ToString()
System.Security.SecureString
Well, that was less than illuminating…
What if I try the Lengthproperty? Does it give me anything?
PS C:\> $credential.Password.Length
17
It does.This is promising. I can at least write code that checks the length of the password and provides some sort of feedback to users regarding the length of the password they supply. It could be rather a cool solution.
What if I use the ConvertFrom-SecureString cmdlet? This is a standard Windows PowerShell cmdlet, so I decide to pipe the password to the ConvertFrom-SecureString cmdlet. The following illustrates the output.
PS C:\> $credential.Password | ConvertFrom-SecureString
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000052ded6c2db80e748933432e19b9de8b10000
000002000000000003660000c00000001000000016dc35885d76d07bab289eb9927cfc1e000000000480
0000a0000000100000003106cde553f45b08d13d89d11336170b280000005cc865c1ee1b57e84ed3d1a2
d3f2d0ec0f189b532e61c18d1f31444d6f119a1e8368477fd2d81f54140000000cb0262e58b08ae14f37
22c14c69684841b6b21c
This latest representation is a string, and therefore there are no more options available for decrypting the password—at least, none that are very direct or easy to use.
Get a network credential
The solution, is to go back to the PSCredential object itself. It has a method that is designed to help with the exact scenario. I need to provide credentials to a legacy type of interface that does not know how to handle a PSCredential. Therefore, I need to be able to get both the user name and the password in an easy-to-use and easy-to-digest manner. When I call the GetNetworkCredential method from the PSCredential object, it returns the user name and the domain name. This is shown here.
PS C:\> $credential.GetNetworkCredential()
UserName Domain
-------- ------
someuser mydomain
If I pipe the returned object to the Get-Member cmdlet, however, I see that I now have a NetworkCredential object. The members are listed here.
PS C:\> $credential.GetNetworkCredential() | gm
TypeName: System.Net.NetworkCredential
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetCredential Method System.Net.NetworkCredential GetCredential(uri uri, str...
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Domain Property string Domain {get;set;}
Password Property string Password {get;set;}
SecurePassword Property securestring SecurePassword {get;set;}
UserName Property string UserName {get;set;}
I see the password has a SecureString for the SecurePassword property, but there is also the Password property that is a plain string. So, I pipe the NetworkCredential object to the Format-List cmdlet and the following appears.
PS C:\> $credential.GetNetworkCredential() | fl *
UserName : someuser
Password : SomeUsersPassword
SecurePassword : System.Security.SecureString
Domain : mydomain
If I need only the password, I simply retrieve the Passwordproperty as shown here.
PS C:\> $credential.GetNetworkCredential().password
SomeUsersPassword
By the way, I can also get the password length here. This is because all string objects contain a Length property. This is shown here.
PS C:\> $credential.GetNetworkCredential().password.length
17
AD, that is all there is to using the NetworkCredential object. Join me tomorrow when I will talk about more cool Windows PowerShell stuff.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy