PowerShell V5 New Feature: Protect/Unprotect-CmsMessage

Windows PowerShell V5, due out sometime in 2015, sports a number of new features: OneGet, PowerShell Get, enhanced DSC, ConvertFrom-String, support for authoring classes in PowerShell script, Compress/Expand-Archive, support for creating symbolic links, hard links and junctions, etc.

One of the more obscure but useful features is the support for cryptographically protecting messages as documented in the IETF standard RFC5652.  This involves the creation of a certificate which I will show you how to do.  You can then protect and unprotect messages using that certificate.  However, where it gets interesting is when you export a public certificate from the original certificate.  You can give the public certificate to anybody and they can use that to encrypt (protect) a message.  That message cannot not unencrypted (unprotected) by anyone except the individual that holds the original certificate.  The original certificate contains both the public and private key.  It is the private key that is used to unprotect the message. 

This is the fundamental basis for asymmetric cryptography.   One key encrypts a message that only the other key can decrypt.  The public key can be distributed broadly but the private  key needs to be held securely.

Here is how it works.  First, we need to create a certificate INF file that is configured for document encryption.  We will feed this file into a tool that will create our certificate:

[Version]
Signature = "$Windows NT$"

[Strings]
szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1"

[NewRequest]
Subject = "cn=foo.bar@baz.com"
MachineKeySet = false
KeyLength = 2048
KeySpec = AT_KEYEXCHANGE
HashAlgorithm = Sha1
Exportable = true
RequestType = Cert
KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE"
ValidityPeriod = "Years"
ValidityPeriodUnits = "1000"

[Extensions]
%szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_DOCUMENT_ENCRYPTION%"

Save this content into a text file called DocumentEncryption.inf.  Then run the following command:

PS C:\> certreq -new DocumentEncryption.inf DocumentEncryption.cer
Installed Certificate:
  Serial Number: 106ace908d57cc9548f830cc67e672e0
  Subject: CN=foo.bar@baz.com
  Thumbprint: 852dcc0c3384c5050e58ee5e655aee3981bc309f
  Microsoft Strong Cryptographic Provider
  526fad1d353ed734b914417afade0eff_f8d1fad6-91ae-4e10-aee5-017e5a7a97d9

 

This not only creates the certifcate .cer file but it also installs the certificate into the Certificate store.  You can see it by listing the cert: drive using the new dynamic parameter –DocumentEncryptionCert e.g.:

PS C:\> Get-ChildItem Cert:\CurrentUser\My\ -DocumentEncryptionCert


    Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My


Thumbprint                                Subject
----------                                -------
852DCC0C3384C5050E58EE5E655AEE3981BC309F  CN=foo.bar@baz.com

 

At this point, you can round trip a protected message on this machine, under the user account that created and imported the certificate using certreq.exe:

PS C:\> $msg = Protect-CmsMessage -Content "This is a secret message!" -To *foo.bar@baz.com
PS C:\> $msg
-----BEGIN CMS-----
MIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD2Zvby5i
YXJAYmF6LmNvbQIQEGrOkI1XzJVI+DDMZ+Zy4DANBgkqhkiG9w0BAQcwAASCAQA5EgX8IpfY+7aG
ozMJIO+ytwrBKp2PMluFjSBSj8aRkwk8T8kDHoSs9++jb9gu/GQUBdZc27AqIawFsBZILVFDmpej
1mLM+9RZ31rom/VxguNLPeqrz1SNN8ox47yO3cX/1QbfhtUhlYPeNpjMU3W9DF48br7SUlAAzLKj
Jrs6SoOmvV7TA93sLE9y5WQYq6FZlfjjZOf1Fio50nd91+oqDL6uUL4qsv4bKpGWGW/bSK7WT23S
20K+qimKfX4ScO6XGYP70NQy6q/unlROACpzssvbPf4e+VU9GHAuajj50SABJ2E9M32bYtmuAneN
n4p7Elbu3rsQyikgIT3HU1hhMEwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEHaFotyx5PCQ9UPL
mhcF/QCAIKr02MPfZl2GL0qOSensD34jPMpN2dKKN2PL2kgRIsLN
-----END CMS-----
PS C:\> $msg | Unprotect-CmsMessage
This is a secret message!

 

BTW the –To parameter will also take the certificate thumbprint as well as the path to the .CER file.  That demos the basic capability but round tripping on the same machine isn’t all that interesting.  Where it gets interesting is when you provide others with a public key in which they can protect messages they send to you.  Those protected messages can only be decrypted by you.

First lets export the public key part of the certificate:

 
$cert = Get-ChildItem -Path cert:\CurrentUser\My\852DCC0C3384C5050E58EE5E655AEE3981BC309F 
Export-Certificate -Cert $cert -FilePath DocumentEncryption-Public.cer 

Now copy the DocumentEncryption-Public.cer file to another machine.  Let’s use it to protect a message:

PS C:\> $msg = Protect-CmsMessage -Content "I am he as you are he as you are me" `
                                  -To .\DocumentEncryption-Public.cer PS C:\> $msg -----BEGIN CMS----- MIIBwgYJKoZIhvcNAQcDoIIBszCCAa8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD2Zvby5i YXJAYmF6LmNvbQIQEGrOkI1XzJVI+DDMZ+Zy4DANBgkqhkiG9w0BAQcwAASCAQBl3BE6UDdXxNRe /TATEgqasqVL4FZi2RVsm6s8RWUKH/GIUUe1EI2N3BeBHZP847DCAkiAKrd16Kds3yaCF4mcmKao lCd0TiInUA5WenDnxO40VW85MJrLWM6sjhQB0bMCNa0UMRV9IzRAAr1lSKDKcWupMScCQvQQ9JHR qCLRgPmWtGA+oMvfl5xs8FTS6oUvNOGh3MwMW7ZOMrk5y6vBltiI5TY34PRVZ/pYl+jnyjSi/tfP vmGp/GqmK9OgIGpgRjRJ8QHnWJ4CXAvL3zj3LXXyPevHKXBum8EOwpiM//zF5kl6gPdYrrqqQJuG OdQkNHXLZunZwtySAVL9n5TcMFwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEA9egmjPe/PsOo5j Wadz5WGAMAObP28v4rN1iCEjqEuY9Yjhgu8/m8kD2eWdm7/KRbpFCADSF6k5crTyqMApMUug6Q== -----END CMS-----

 

In case you were wondering, having the public key is not enough to decrypt the message – even on the same machine you used to encrypt the message with the public certificate:

PS C:\> $msg | Unprotect-CmsMessage –To .\DocumentEncryption-Public.cer
Unprotect-CmsMessage : The enveloped-data message does not contain the
specified recipient.
At line:1 char:8
+ $msg | Unprotect-CmsMessage
+        ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Unprotect-CmsMessage], Crypto
   graphicException
    + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicExcept
   ion,Microsoft.PowerShell.Commands.UnprotectCmsMessageCommand

 

If you would prefer to install the public key certificate on the second machine, you can do that easily:

PS C:\> Import-Certificate -FilePath DocumentEncryption-Public.cer `
                           -CertStoreLocation cert:\CurrentUser\My

 

Then you can use either the certification thumbprint or the subject e.g. *foo.bar@baz.com as the argument to the –To parameter on Protect-CmsMessage.

Now let’s copy paste the protected message back to the original machine with the certificate containing the private key and unprotect it:

PS C:\> $msg = @'
>>> -----BEGIN CMS-----
>>> MIIBwgYJKoZIhvcNAQcDoIIBszCCAa8CAQAxggFKMIIBRgIBADAuMBoxGDAWBgNVBAMMD2Zvby5i
>>> YXJAYmF6LmNvbQIQEGrOkI1XzJVI+DDMZ+Zy4DANBgkqhkiG9w0BAQcwAASCAQBl3BE6UDdXxNRe
>>> /TATEgqasqVL4FZi2RVsm6s8RWUKH/GIUUe1EI2N3BeBHZP847DCAkiAKrd16Kds3yaCF4mcmKao
>>> lCd0TiInUA5WenDnxO40VW85MJrLWM6sjhQB0bMCNa0UMRV9IzRAAr1lSKDKcWupMScCQvQQ9JHR
>>> qCLRgPmWtGA+oMvfl5xs8FTS6oUvNOGh3MwMW7ZOMrk5y6vBltiI5TY34PRVZ/pYl+jnyjSi/tfP
>>> vmGp/GqmK9OgIGpgRjRJ8QHnWJ4CXAvL3zj3LXXyPevHKXBum8EOwpiM//zF5kl6gPdYrrqqQJuG
>>> OdQkNHXLZunZwtySAVL9n5TcMFwGCSqGSIb3DQEHATAdBglghkgBZQMEASoEEA9egmjPe/PsOo5j
>>> Wadz5WGAMAObP28v4rN1iCEjqEuY9Yjhgu8/m8kD2eWdm7/KRbpFCADSF6k5crTyqMApMUug6Q==
>>> -----END CMS-----
>>> '@
204> $msg | Unprotect-CmsMessage
I am he as you are he as you are me

 

OK so these two commands are working as advertised but you may be asking what the practical application of this is.  Well, we’ve always had this issue of how to protect passwords that have to be embedded in scripts.  This asymmetric approach can help here but I think only when the script runs on machines and/or user accounts that are limited in access.  That is, I could envision checking in the public certificate into version control. Anybody could use that cert to protect passwords and embedded the protected password text into a script.  Folks like contractors that have access to source control cannot determine the password from the public key and the protected password plainly visible in scripts.  However, if you protect the private certificate by only installing it on the machines that run the scripts (build machines, test machines) *and* you can limit who can access the machines the scripts run on you have protected your passwords at least a bit more securely.

Disclaimer: I am not a security or certificate guru.  I’m interested in hearing how folks might use this asymmetric encryption functionality.  I’m also curious if the private key certificate can be installed onto a machine so that A) only a specific user can access the key and B) the key can’t be exported.  The INF file contains a field called Exportable that is set to true in the sample INF file above.  I need to experiment with it set to false to see if that would prevent the private key from being exported once it is installed on a specific user account on a specific machine.

Advertisements
This entry was posted in PowerShell 5.0. Bookmark the permalink.

6 Responses to PowerShell V5 New Feature: Protect/Unprotect-CmsMessage

  1. Dave Wyatt says:

    A) Yes, sort of. Any certificate installed into Cert:\CurrentUser has a private key that is only readable by that user through the Windows API. (It’s protected by their DPAPI master key.) That said, anyone with admin access to a computer, or physical access to an unencrypted hard drive, can steal those private keys in most cases.

    B) Yes, sort of. The exportable flag again only covers access through the normal API; attackers don’t care whether you set that particular bit or not. 🙂

    As far as use cases go, I have several already. I use a module called ProtectedData, which I wrote before the CmsMessage cmdlets were made public, but the basic functionality and use cases are the same:

    – I use it to encrypt copies of my PowerShellGet / NuGet / etc API keys in my PowerShell profile script, so it doesn’t hurt anything if I happen to show those profile scripts during demonstrations.

    – The DscConfiguration module uses ProtectedData in order to encrypt copies of credentials in source control, just about exactly the scenario you described in this post. The build server has the certificate installed so it can decrypt the stuff from source control when it compiles the MOFs.

    – DSC itself uses a similar technique (though not the CmsMessage or ProtectedData commands specifically) when it encrypts credentials in MOF documents. You make the .cer (public) file available to the computer that’s compiling the MOF, and the certificate with private key needs to be installed on the target computer so it can decrypt its own MOF.

    Essentially, anytime you want to encrypt something and share it with multiple users, certificates are the way to go. That puts the burden of key protection / management back into familiar territory on the operating system. Windows is full of examples of this. Encrypting File System, for example, relies on EFS certificates (and you can optionally add additional certs to an encrypted file, so multiple users can work with it.)

  2. Pingback: Windows PowerShell V5 Goodies in the April WMF5 Preview | Keith Hill's Blog

  3. jakebruun says:

    I found this extremly helpful in crafting a certificate request I could use for DSC credential encryption. Linked to question on [stack overflow](http://stackoverflow.com/questions/35659351/encryption-certificates-must-contain-data-encipherment-or-key-encipherment).

  4. Reblogged this on Programming, PowerShell and Pants and commented:
    This interesting article gives some background details on some of the problems I ran into after upgrading my DSC dev machine to WMF 5.0 10586. This is because in WMF5.0 the DSC credential encryption mechanism was converted to use Protect/Unprotect-CMSMessage. It clears up a lot of things for me and is a worthwhile read if you’re using DSC credential encryption on WMF5.0.

  5. Pingback: Protecting output content from prying eyes | pshirwin

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s