Saving credentials for reuse by powershell and error ConvertTo-SecureString : Key not valid for use in specified state

前端 未结 5 2119
长情又很酷
长情又很酷 2020-11-29 07:07

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: ht

相关标签:
5条回答
  • 2020-11-29 07:39

    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

    0 讨论(0)
  • 2020-11-29 07:40

    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'.

    0 讨论(0)
  • 2020-11-29 07:48

    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.

    0 讨论(0)
  • 2020-11-29 07:58

    You have to create the password string on the same computer and with the same login that you will use to run it.

    0 讨论(0)
  • 2020-11-29 07:59

    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.

    0 讨论(0)
提交回复
热议问题