Reputation: 6262
I was doing something like described in this post to save credentials in a secured file so our automated process can use that to run remote PS scripts via Invoke-command: http://blogs.technet.com/b/robcost/archive/2008/05/01/powershell-tip-storing-and-using-password-credentials.aspx
This works great when I run this under my account - password is read from encrypted file, passed to Invoke-command and everything is fine.
Today, when my script was ready for its prime time, I tried to run it under windows account that will be used by automated process and got this error below while my script was trying to read secured password from a file:
ConvertTo-SecureString : Key not valid for use in specified state.
At \\remoted\script.ps1:210 char:87
+ $password = get-content $PathToFolderWithCredentials\pass.txt | convertto-sec
urestring <<<<
+ CategoryInfo : InvalidArgument: (:) [ConvertTo-SecureString], C
ryptographicException
+ FullyQualifiedErrorId : ImportSecureString_InvalidArgument_Cryptographic
Error,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand
Asked my workmate to run under his account and he got the same error.
This is the code I am using to save credentials:
$PathToFolderWithCredentials = "\\path\removed"
write-host "Enter login as domain\login:"
read-host | out-file $PathToFolderWithCredentials\login.txt
write-host "Enter password:"
read-host -assecurestring | convertfrom-securestring | out-file $PathToFolderWithCredentials\pass.txt
write-host "*** Credentials have been saved to $pathtofolder ***"
This is the code in the script to run by automated process to read them to use in Invoke-command:
$login= get-content $PathToFolderWithCredentials\login.txt
$password = get-content $PathToFolderWithCredentials\pass.txt | convertto-securestring
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $login,$password
Error happens on line $password = get-content $PathToFolderWithCredentials\pass.txt | convertto-securestring
Any ideas?
Upvotes: 36
Views: 137169
Reputation: 14391
Assuming you have a known list of N users who will use the credentials (e.g. one developer userMe
and a system/service user userSys
) you can just (get those users to) make N copies of the pass.txt
file: one for each user.
So the password of userX
will result in e.g. 2 *.pass.txt
files:
userX.userMe.pass.txt
userX.userSys.pass.txt
When userMe wants the creds he/she reads userX.userMe.pass.txt
etc.
Upvotes: 0
Reputation: 151
The below will allow credentials to be saved as a file, then those credentials to be used by another script being run by a different user, remotely.
The code was taken from a great article produced by David Lee, with only some minor adjustments from myself https://blog.kloud.com.au/2016/04/21/using-saved-credentials-securely-in-powershell-scripts/
First step is to save a a secure password to a file using AES. The below will run as a stand alone script:
# Prompt you to enter the username and password
$credObject = Get-Credential
# The credObject now holds the password in a ‘securestring’ format
$passwordSecureString = $credObject.password
# Define a location to store the AESKey
$AESKeyFilePath = “aeskey.txt”
# Define a location to store the file that hosts the encrypted password
$credentialFilePath = “credpassword.txt”
# Generate a random AES Encryption Key.
$AESKey = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey)
# Store the AESKey into a file. This file should be protected! (e.g. ACL on the file to allow only select people to read)
Set-Content $AESKeyFilePath $AESKey # Any existing AES Key file will be overwritten
$password = $passwordSecureString | ConvertFrom-SecureString -Key $AESKey
Add-Content $credentialFilePath $password
Then in your script where you need to use credentials use the following:
#set up path and user variables
$AESKeyFilePath = “aeskey.txt” # location of the AESKey
$SecurePwdFilePath = “credpassword.txt” # location of the file that hosts the encrypted password
$userUPN = "domain\userName" # User account login
#use key and password to create local secure password
$AESKey = Get-Content -Path $AESKeyFilePath
$pwdTxt = Get-Content -Path $SecurePwdFilePath
$securePass = $pwdTxt | ConvertTo-SecureString -Key $AESKey
#crete a new psCredential object with required username and password
$adminCreds = New-Object System.Management.Automation.PSCredential($userUPN, $securePass)
#use the $adminCreds for some task
some-Task-that-needs-credentials -Credential $adminCreds
Please be aware that if the user can get access to the password file and the key file, they can decrypt the password for the user.
Upvotes: 3
Reputation: 301587
ConvertFrom-SecureString
takes a Key
( and SecureKey
) parameter. You can specify the key to save the encrypted standard string and then use the key again in ConvertTo-SecureString
to get back the secure string, irrespective of the user account.
http://technet.microsoft.com/en-us/library/dd315356.aspx
In a project, I have implemented asymmetric encryption, whereby people encrypt the password using the public key and the automation process has the private key to decrypt passwords: Handling passwords in production config for automated deployment
Upvotes: 33
Reputation: 3881
You have to create the password string on the same computer and with the same login that you will use to run it.
Upvotes: 38
Reputation: 936
Another approach would be to protect the data using scope 'LocalMachine' instead of 'CurrentUser' which is the one used by ConvertFrom-SecureString.
public static string Protect(SecureString input, DataProtectionScope dataProtectionScope = DataProtectionScope.CurrentUser, byte[] optionalEntropy = null)
{
byte[] data = SecureStringToByteArray(input);
byte[] data2 = ProtectedData.Protect(data, optionalEntropy, dataProtectionScope);
for (int i = 0; i < data.Length; i++)
{
data[i] = 0;
}
return ByteArrayToString(data2);
}
private static byte[] SecureStringToByteArray(SecureString s)
{
var array = new byte[s.Length * 2];
if (s.Length > 0)
{
IntPtr intPtr = Marshal.SecureStringToGlobalAllocUnicode(s);
try
{
Marshal.Copy(intPtr, array, 0, array.Length);
}
finally
{
Marshal.FreeHGlobal(intPtr);
}
}
return array;
}
private static string ByteArrayToString(byte[] data)
{
var stringBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
stringBuilder.Append(data[i].ToString("x2", CultureInfo.InvariantCulture));
}
return stringBuilder.ToString();
}
The encrypted string can be used by ConvertTo-SecureString which is using scope 'CurrentUser'.
Upvotes: 1