Сохранение учетных данных для повторного использования с помощью powershell и ошибкой. ConvertTo-SecureString: ключ недопустим для использования в указанном состоянии.

Я делал что-то подобное описанному в этом посте, чтобы сохранить учетные данные в защищенном файле, чтобы наш автоматизированный процесс мог использовать это для запуска удаленных сценариев PS с помощью команды Invoke: http://blogs.technet.com/b/robcost/archive/2008/05/01/powershell-tip-storing-and-using-password-credentials.aspx

Это прекрасно работает, когда я запускаю это под своей учетной записью - пароль читается из зашифрованного файла, передается команде Invoke и все в порядке.

Сегодня, когда мой сценарий был готов к прайм-тайму, я попытался запустить его под учетной записью Windows, которая будет использоваться автоматическим процессом, и получил следующую ошибку, пока мой сценарий пытался прочитать защищенный пароль из файла:

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

Попросил моего напарника запустить под своим аккаунтом и он получил такую ​​же ошибку.

Это код, который я использую для сохранения учетных данных:

$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 ***"

Это код в скрипте, который запускается автоматическим процессом для чтения их для использования в Invoke-команде:

$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

Ошибка происходит в строке $password = get-content $PathToFolderWithCredentials\pass.txt | ConvertTo-SecureString

Есть идеи?

4 ответа

Решение

ConvertFrom-SecureString занимает Key (а также SecureKey) параметр. Вы можете указать ключ для сохранения зашифрованной стандартной строки, а затем снова использовать ключ в ConvertTo-SecureString вернуть безопасную строку, независимо от учетной записи пользователя.

http://technet.microsoft.com/en-us/library/dd315356.aspx

В проекте я реализовал асимметричное шифрование, при котором люди шифруют пароль с помощью открытого ключа, а процесс автоматизации имеет закрытый ключ для расшифровки паролей: обработка паролей в рабочей конфигурации для автоматического развертывания

Вы должны создать строку пароля на том же компьютере и с тем же логином, который вы будете использовать для его запуска.

Приведенное ниже позволит сохранить учетные данные в виде файла, а затем эти учетные данные будут использоваться другим сценарием, запущенным другим пользователем, удаленно.

Код был взят из замечательной статьи, созданной Дэвидом Ли, с небольшими изменениями от меня https://blog.kloud.com.au/2016/04/21/using-saved-credentials-securely-in-powershell-scripts/

Первый шаг - сохранить безопасный пароль в файл с помощью AES. Ниже будет работать как отдельный скрипт:

            # 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

Затем в вашем сценарии, где вам нужно использовать учетные данные, используйте следующее:

            #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

Помните, что если пользователь может получить доступ к файлу пароля и файлу ключа, он может расшифровать пароль для пользователя.

Другой подход заключается в защите данных с использованием области "LocalMachine" вместо "CurrentUser", которая используется 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();
}

Зашифрованная строка может использоваться ConvertTo-SecureString, которая использует область действия "CurrentUser".

Предполагая, что у вас есть известный список из N пользователей, которые будут использовать учетные данные (например, один разработчик userMe и пользователь системы / службы userSys) вы можете просто (заставить этих пользователей) сделать N копий pass.txt файл: один для каждого пользователя.

Так что пароль userX приведет, например, к 2 *.pass.txt файлы:

  • userX.userMe.pass.txt
  • userX.userSys.pass.txt

Когда userMe хочет кредиты, которые он / она читает userX.userMe.pass.txt и т.п.

Другие вопросы по тегам