4

We have a build process that needs to decrypt a password which it then uses to connect to a database. We are using the Data Protection API (DPAPI) to encrypt the password at the machine scope on the build server (I'm logged in with my own domain account) using PowerShell:

[Security.Cryptography.ProtectedData]::Protect( $passwordBytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine );

The decryption is also done in a PowerShell script, which is executed by CruiseControl.NET, running as our build user:

 [Security.Cryptography.ProtectedData]::Unprotect( $encryptedbytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine );

The call to Unprotect, however, fails with the following message: "Key not valid for use in specified state.". According to the docs, this error can occur using impersonation, and the impersonating code should load the user's profile.

For security purposes, the build user is denied the Log on through Terminal Services right. To test the problem, I logged into the build server as myself, then used runas to open a PowerShell prompt as the build user:

runas /profile /user:DOMAIN\BUILDUSER powershell.exe

I then run the PowerShell script to unprotect/decrypt the password, and get the same error.

I used Process Monitor to watch for any failures or access denied events, and there weren't any.

I'm able to decrypt the password when I log in with my own domain account.

What's going on? Is my build user's profile not being loaded? Is there some security setting somewhere I don't know about to enable this? Why can't I decrypt the password?

Aaron Jensen
  • 25,861
  • 15
  • 82
  • 91

2 Answers2

3

Developer error. It turns out I was encrypting at the CurrentUser scope, not the LocalMachine scope. I updated my script and everything is working now.

Aaron Jensen
  • 25,861
  • 15
  • 82
  • 91
1

Do you pay attention in the way you code the crypted ByteArray in your file. Here under I extract the way I do it on one of my servers for a connexion string (I replace here with an entered string)

Convert to Base64 to put it in the config file

Clear-Host

Add-Type -assembly System.Security

$passwordASCII = Read-Host -Prompt "Enter Password"

$bakCryptedPWD_ByteArray = [System.Security.Cryptography.ProtectedData]::Protect( $passwordASCII.tochararray(), $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )

Set-Content -LiteralPath c:\Temp\pass.txt -Value ([Convert]::ToBase64String($bakCryptedPWD_ByteArray))

Back to string taking case of ANSI characters (may have special characters in password)

Clear-Host

Add-Type -assembly System.Security

$resCryptedPWD_ByteArray = [Convert]::FromBase64String((Get-Content -LiteralPath c:\Temp\pass.txt))

$clearPWD_ByteArray = [System.Security.Cryptography.ProtectedData]::Unprotect( $resCryptedPWD_ByteArray, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine )

$enc = [system.text.encoding]::Default

$enc.GetString($clearPWD_ByteArray)
JPBlanc
  • 70,406
  • 17
  • 130
  • 175
  • This isn't the issue. You get a different error than mine if you pass in an array of bytes that can't be decrypted. – Aaron Jensen Jun 06 '11 at 20:27