Managing Credentials (Part 5)

When PowerShell auto-encrypts a secure string, it uses your identity as secret. Only you can decrypt the secure string. What if you want to encrypt a secret with a shared passphrase?

Here is one classic approach, using a passphrase for encryption:

# $secretKey MUST be of length 8 or 16
# anyone who knows the secret can decrypt the password 
$secretKey = 'mysecretmysecret'
$password = 'myPassword'

$SecureString = ConvertTo-SecureString -String $password -AsPlainText -Force
$RandomSecureString = ConvertTo-SecureString -String $secretKey -AsPlainText -Force
$encryptedPassword = ConvertFrom-SecureString -SecureString $SecureString -SecureKey $RandomSecureString
$encryptedPassword

The secret decryption key ($secretKey) needs to be a text with 8 or 16 characters length. Anyone who knows this key can decrypt the secret (which is why the secretKey should not be part of a script and is added to the below script only to illustrate the decryption process. You better ask for it interactively):

# this is the key to your secret
$secretKey = 'mysecretmysecret'
# this is the encrypted secret as produced by the former code
$encryptedPassword = '76492d1116743f0423413b16050a5345MgB8AEMARQAxAFgAdwBmAHcARQBvAGUAKwBOAGoAYgBzAE4AUgBnAHoARABSAHcAPQA9AHwANQA3ADYAMABjAGYAYQAwAGMANgBkADQAYQBiADYAOAAyAGYAZAA5AGYAMwA5AGYAYQBjADcANQA5ADIAYwAzADkAMAA2ADQANwA1ADcAMQA3ADMAMwBmAGMAMwBlADIAZQBjADcANgAzAGQAYQA1AGIAZABjADYAMgA2AGQANAA='

$RandomSecureString = ConvertTo-SecureString -String $secretKey -AsPlainText -Force
$securestring = $encryptedPassword | ConvertTo-SecureString -SecureKey $RandomSecureString
# Result is a secure string
$SecureString

The result is a secure string that you could use to compose a full credential:

$credential = New-Object -TypeName PSCredential('yourcompanyyouruser', $SecureString)
$credential

You can also double-check the clear text secret:

 
PS C:Userstobwe> $credential.GetNetworkCredential().Password
myPassword
 

Note that the owner of a secure string (the one who created it) can always get back to the plain text password. This is not a security issue. The one that created the secure string already knew the secret at a given point in the past. A secure string protects sensitive data from third party, and stores it in memory in a way that no other user can touch it.

The issue with secret keys and symmetric encryption is that you need to distribute the secret key, that it needs to be protected, and can be used both for encryption and decryption.

A much safer way was introduced in PowerShell 5: Protect-CMSMessage and Unprotect-CMSMessage use digital certificates and asymmetric encryption. This way, the party encrypting a secret does not need to know the decryption key, and vice versa. The encrypting party only needs to specify who (which certificate) should be able to decrypt the secret.

Twitter This Tip! ReTweet this Tip!

Managing Credentials (Part 4)

In the previous script we showed how you can save a credential object in encrypted form to disk. A similar approach just saves the secret password as encrypted text. This creates the encrypted password file:

# read in the password, and save it encrypted
$text = Read-Host -AsSecureString -Prompt 'Enter Password'
$text | Export-Clixml -Path "$homedesktopmypassword.xml"

It can only be read back from the person that saved it, and only on the same machine. A second script could take the password and use it to log in to another system without user interaction:

# read in the secret and encrypted password from file
$password = Import-Clixml -Path "$homedesktopmypassword.xml"

# add the username and create a credential object
$username = 'yourCompanyyourUserName'
$credential = New-Object -TypeName PSCredential($username, $password)

The credential object can then be used with any cmdlet that takes the -Credential parameter.

# use the credential with any cmdlet that exposes the –Credential parameter
# to log in to remote systems
Get-WmiObject -Class Win32_LogicalDisk -ComputerName SomeServer -Credential $credential

Twitter This Tip! ReTweet this Tip!

Managing Credentials (Part 3)

For unattended scripts, it is generally unsafe and not recommended to hard-code secret passwords into a script.

As an alternative, you could ask for the password once, then create a credential object and use it wherever needed in your script. This part asks for a password, then constructs a credential object:

$password = Read-Host -AsSecureString -Prompt 'Enter Password'
$username = 'myCompanymyUserName'
$credential = New-Object -TypeName PSCredential($username, $password)

The credential object can then be used with any cmdlet that takes the -Credential parameter.

# use the credential with any cmdlet that exposes the –Credential parameter
# to log in to remote systems
Get-WmiObject -Class Win32_LogicalDisk -ComputerName SomeServer -Credential $credential

Twitter This Tip! ReTweet this Tip!

Managing Credentials (Part 2)

For scripts running unattended, you can create login credentials from code. This requires the secret password to be saved as clear text in a script (which obviously is unsafe unless you encrypt your script with the encrypting file system (EFS) or take other measures to protect the content):

$password = 'topsecret!' | ConvertTo-SecureString -AsPlainText -Force
$username = 'myCompanymyUserName'
$credential = New-Object -TypeName PSCredential($username, $password) 

# use the credential with any cmdlet that exposes the –Credential parameter
# to log in to remote systems
Get-WmiObject -Class Win32_LogicalDisk -ComputerName SomeServer -Credential $credential

Twitter This Tip! ReTweet this Tip!

Managing Credentials (Part 1)

Let’s assume you need to run a script every day that requires credentials. A safe way of storing credentials is to save them encrypted in a file. This code asks for credentials, then saves them in an XML file on your desktop:

$credential = Get-Credential -UserName trainuser02 -Message 'Please provide credentials' 
$credential | Export-Clixml -Path "$homedesktopmyCredentials.xml"

The password is encrypted with your identity, so only you (and only on the machine where the credentials were saved) can access the credential.

Here is the code to read the saved credentials for later use:

$credential = Import-Clixml -Path "$homedesktopmyCredentials.xml"

# use the credential with any cmdlet that exposes the –Credential parameter
# to log in to remote systems
Get-WmiObject -Class Win32_LogicalDisk -ComputerName SomeServer -Credential $credential

Twitter This Tip! ReTweet this Tip!

Parsing Raw Text (Part 3)

In the previous tip we illustrated how you can use Select-String to find lines in raw text containing a specific word. It took some effort to extract the actual value(s) representing a given pattern:

 
PS C:> $data = ipconfig | select-string 'IPv4' 
PS C:> [regex]::Matches($data,"b(?:d{1,3}.){3}d{1,3}b") | Select-Object -ExpandProperty Value

192.168.2.112
 

This effort is not necessary, though, because Select-String is already using a regular expression match, and returns match objects.

 
PS C:> ipconfig | 
  Select-String 'b(?:d{1,3}.){3}d{1,3}b' | 
  Select-Object -Property *



IgnoreCase : True
LineNumber : 16
Line       :    IPv4 Address. . . . . . . . . . . : 192.168.2.112
Filename   : InputStream
Path       : InputStream
Pattern    : b(?:d{1,3}.){3}d{1,3}b
Context    : 
Matches    : {192.168.2.112}

IgnoreCase : True
LineNumber : 17
Line       :    Subnet Mask . . . . . . . . . . . : 255.255.255.0
Filename   : InputStream
Path       : InputStream
Pattern    : b(?:d{1,3}.){3}d{1,3}b
Context    : 
Matches    : {255.255.255.0}

IgnoreCase : True
LineNumber : 19
Line       :                                        192.168.2.1
Filename   : InputStream
Path       : InputStream
Pattern    : b(?:d{1,3}.){3}d{1,3}b
Context    : 
Matches    : {192.168.2.1}
 

So you can use a simple Where-Object with the -like operator to do pre-filtering, identifying only the lines you are after (i.e. the ones containing the word “IPv4”), then submit a RegEx pattern to Select-String, and evaluate the results:

 
PS C:> ipconfig | 
  # do raw prefiltering and get only lines containing this word
  Where-Object { $_ -like '*IPv4*' } |
  # do RegEx filtering using a pattern for IPv4 addresses
  Select-String 'b(?:d{1,3}.){3}d{1,3}b' | 
  # get the matching values
  Select-Object -ExpandProperty Matches |
  # get the value for each match
  Select-Object -ExpandProperty Value

192.168.2.112
 

Twitter This Tip! ReTweet this Tip!