IHeartPowerShell.com Not your father's shell... <3

Getting Started With PSEncryptedData

When I was trying to figure out how my team or other dev teams could start using DSC I had a couple goals I wanted to try to achieve:

  1. Highly privileged credentials will be required and must be secured
  2. Source control will be used to track changes and to automate builds
  3. Provide both the admins and dev teams a way to bring their configurations together

These goals gave rise to a number of possible options, from simple to complex. If you have used DSC beyond a sample configuration then you know that eventually you need to use PSCredential in your configuration and that you can specify in your configuration how you want those PSCredential values encrypted at rest in the MOF file.

But how do you get those PSCredential objects into your configuration file if there is no human to provide input to Get-Credential? Sure you can use ConvertTo-SecureString -String 'secret' -AsPlainText -Force and if you can guarantee where you store the configuration script is secure then that may be fine.

You could also on your build system login/runas the account that will perform the build and use ConvertFrom-SecureString to produce an encrypted secret that only that user on that system will be able to use. How do you share that login credential with others so that they can produce their own encrypted secret?

You can even used a shared key with ConvertFrom-SecureString but then how do you secure access to that key?

You could even create a central application that encrypts a secret for a user using a shared secret that is only accessible on the central application and DSC build system either via a secured file/registry key. But what happens when you need to rotate that shared secret?

Certificate Encryption

If you are using DSC, you are most likely using certificates to encrypt the PSCredential values in the MOF configuration file as a best practice, because passwords in the clear are a really bad idea and other methods mentioned above can be tricky.

Every certificate has two pieces of information, a public key and a private key. Just as the names state the private key is meant to be kept private and the public one can be used by anyone.

You don't typically encrypt data with a private key because anyone could decrypt it with the public key. This method is typically used for signing data that comes from the private entitiy but that's another blog entry and I digress.

However, if we encrypt a secret with the public key, only the entities with the private key will be able to decrypt the secret.

So, if anyone can encrypt data using the public key which can be distributed, only the entity with the private key, e.g. DSC build server(s) will be able to decrypt the secret before stuffing it into a PSCredential or SecureString.

PSEncryptedData

If you want to get started and don't have the module installed, you can get it from the PowerShell Gallery which is by far my favorite feature of PowerShell now.

Install-Module PSEncrytpedData

Once you have it installed you'll need a certificate. PowerShell v5 has an enhanced New-SelfSignedCertificate cmdlet that you can use to generate the appropriate certificate as show here:

New-SelfSignedCertificate -Subject 'CN=PowerShell DSC Encryption'

However, if you are stuck in PowerShell v4 like I was when I first made the module, you can use the New-PSEncryptedDataCertificate function as shown here:

New-PSEncryptedDataCertificate -Name 'PowerShell DSC Encryption'

Great, so you have a certificate, you should generate it on the system where you want the private key to exist, then export the public key with Export-Certificate and you should probably also use Export-PfxCertificate to export the entire certificate for source control. This is ok, there is a shared password you will need to access the certificate pfx file to be able to reinstall or install it on other systems.

In the end, you should be able to see a certificate installed into some location in your key store:

PS C:\> Get-ChildItem Cert:\LocalMachine\My

   PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

Thumbprint                                Subject
----------                                -------
B6052E85F1EB02011AC34B05042EC085080B29F6  CN=PowerShell DSC Encryption

Data Encryption

The core of the module is all about encrypting data at rest. It takes string or SecureString values and encrypts them with the specified certificate and stores what information is needed to decrypt the data.

You can specify the certificate in three different ways.

  1. Path to the certificate, e.g. Cert:\LocalMachine\My\THUMBPRINT
  2. The thumbprint of the certificate
  3. The X509Certificate2 instance

You can now encrypt data:

'Secret' | ConvertTo-PSEncryptedData -Thumbprint B6052E85F1EB02011AC34B05042EC085080B29F6

This produces an encrypted base64 string:

eyJQcm92aWRlciI6eyJLZXlTaXplIjoyNTYsIkZlZWRiYWNrU2l6ZSI6OCwiUGFkZGluZyI6IlBLQ1M3IiwiS2V5IjoiRklvU1U3aWMxOWRkNU0xVmx6TTNt
V1BmN2tlY0ZXQmUvZ2xIY3JmcDdwMVBKd3VUcEdtUmZaZ0E1SWZUTjRvWGQwN1lpZWtoVG9NcjVvUTJjNW12M1ZMUmhQSWpTNysweUE0UkJKSEM2Vjl3Y2VI
T1dIOUVIZ1pkdXNYcTNWUWY1TXFKa0M1MEJwNml1SHJ0LzUxWlVoT3RRN0tvU0I2MmZveUtNVGNCeHlZYVR0dUpDOFMwU1I2dHlXUzJob09yZXpyZFZFTE5T
ODZSb2xNcWxOU2NrRjJvbENXQ0Z5V2hiRVAveEs4RHZldkpsL29Ga1hTNE44aXJuMEIxVDdRekx5TzNZTmZBME9PZTNMT1pIMjVRNEVWbkJrNnB4TXlaZ1px
UlVraFJtYUlkOHB3TmM0S3J4RC8zRUZFek5vQWhEZjBNV2p3SndWeFRkK2dqZVFoclNBPT0iLCJUeXBlIjoiU3lzdGVtLlNlY3VyaXR5LkNyeXB0b2dyYXBo
eS5BZXNDcnlwdG9TZXJ2aWNlUHJvdmlkZXIiLCJJViI6IitWSStyYjVaSk5YRE85VnRicnd6b2c9PSIsIkJsb2NrU2l6ZSI6MTI4LCJNb2RlIjoiQ0JDIn0s
IkVuY29kaW5nIjoiRGVmYXVsdCIsIkRhdGEiOiJja05hNVN3bGhQbk1lalNHWUhhdUxBPT0iLCJDZXJ0aWZpY2F0ZSI6IkI2MDUyRTg1RjFFQjAyMDExQUMz
NEIwNTA0MkVDMDg1MDgwQjI5RjYifQ==

You can also encrypt a SecureString the same way:

Read-Host -AsSecureString -Prompt 'Secret' | ConvertTo-PSEncryptedData -Thumbprint B6052E85F1EB02011AC34B05042EC085080B29F6

Decryption works the same way with ConvertFrom-PSEncryptedData and as long as you have access to the private key of the certificate thumbprint that was used to encrypt the string it is almost like magic.

'Secret' | ConvertTo-PSEncryptedData -Thumbprint B6052E85F1EB02011AC34B05042EC085080B29F6 | ConvertFrom-PSEncryptedData

If you don't have access to the private key you will see the following error:

ConvertFrom-PSEncryptedData : The certificate 'CN=PowerShell DSC Encryption' with thumbprint 'B6052E85F1EB02011AC34B05042EC085080B29F6' does not contain a private key or access is denied.

Data At Rest

Having those large base64 strings all over the DSC configuration makes it a hassle to deal with, but if you are used to things like that feel free to stop here, you know all you need to get started.

For me, I like to encapsulate complexity as much as possible and putting the encrypted data in the configuration only creates noise. To store the data instead in external files use the functions Export-PSEncryptedData and Import-PSEncryptedData.

Each are called just like ConvertTo-PSEncryptedData and ConvertFrom-PSEncryptedData except you now have to pass a file path:

'Secret' | Export-PSEncryptedData -Thumbprint B6052E85F1EB02011AC34B05042EC085080B29F6 -OutputPath .\secret.encrypted

Import-PSEncryptedData -Path .\secret.encrypted

PSCredential Encryption

The above could be used to encrypt the passwords of PSCredential but then you'd still have to mash that up to the username and do some calls to New-Object to get it all together.

Why do that more than once? So I wrapped it into a simple call just like export/import but instead of taking a string or SecureString it works with instances of PSCredential only.

Get-Credential | Export-PSEncryptedCredential -Thumbprint B6052E85F1EB02011AC34B05042EC085080B29F6 -OutputPath .\credential.encrypted

Import-PSEncryptedCredential -Path .\credential.encrypted

Security Matters

I hope this helps you think about security while using DSC, because if your process isn't secure today it is only a matter of time before all those things that keep your CISO and his team up at night happen as you are just making it easier for the bad guys.

If you want to see how these functions work to help increase their security or want to contribute to the module check out the GitHub project.

IHeartPowerShell.com In 2016 And Beyond

I have been neglecting this blog for nearly three years. PowerShell has changed a lot in three years from workflow to desired state configuration and classes. PowerShell is well on its way to becoming the must know language for the Microsoft automation platform. Even that may change as Microsoft continues to push into providing their frameworks and tools for non-Microsoft platforms PowerShell will be usable everywhere. But if you are still following this blog, you probably already know that so I digress...

Even though I haven't shared on this blog what I have been doing with PowerShell, I am still using it almost daily. Even as my job changed from a sysadmin/engineer to a solutions architect I still find myself using PowerShell to either analyze data or implement a proof-of-concept idea. I still evangelize to anyone that will listen the importance of automation and the capabilities PowerShell brings in this DevOps culture.

I will start sharing snippets of some of the work I have been doing over the last three years and the insight that comes in the future.

For now, you can check out a module I posted to the PowerShell Gallery that handles data encryption using certificates. I built this specifically from my experience dealing with DSC credential in files so they can be stored in source control. Feel free to contribute to or get the source over on GitHub

Install-Module PSEncryptedData

All previous blog posts can be found on the old Blogger site