Learning to Apply Cryptography

Working on the ProtectedData module has been quite a learning experience for me. The purpose of that module is to provide an API for PowerShell scripters which can be just as simple to use as the Data Protection API – via cmdlets such as ConvertFrom-SecureString and ConvertTo-SecureString – while allowing encrypted data to be securely shared among different users and computers. Incidentally, PowerShell is going to have built-in support for this kind of thing starting with version 5.0, via the new Protect-CmsMessage and Unprotect-CmsMessage commands, which you can see in the September preview of Windows Management Framework 5.0.

Both the ProtectedData module and the new CmsMessage cmdlets leverage public-key cryptography to securely share the data. Before working on this module, I had a fairly high-level, system administrator’s understanding of crypto. I knew how to deploy a public key infrastructure according to best practices, and how to request certificates from a public certificate authority, such as DigiCert. I knew the names of the commonly-used encryption protocols, which ones were for symmetric encryption, signing, or asymmetric encryption, and was reasonably up-to-date on the relative security of these algorithms with various key sizes. I knew that at a very high, conceptual level, public-key cryptography works through some sort of mathematical magic where you can take some sensitive data, apply an algorithm to it using one half of the public / private key pair, and then the only way to get the original data back is to apply an algorithm to the cipher text using the other half of the public / private key pair. The practical result is that so long as you know the private key is well protected, and you trust that the person holding the private key is who they say they are (via digital certificates), you can use this technique to protect to either ensure privacy, or to produce digital signatures.

In order to write the ProtectedData module, I had to deepen my understanding of these crypto algorithms a bit, both conceptually, and with regards to the specific libraries that I would be using. However, I don’t know how the actual math behind these encryption algorithms works, and I don’t much care, either. You don’t really need to know about the math unless you’re writing an implementation of an established algorithm, or you’re a hacker or cryptanalyst trying to crack them. All I’m concerned with is practical application: using established crypto libraries to protect data using various algorithms.

Here, I’ll share the general approach that the ProtectedData module uses for each type of encryption / decryption:


Symmetric Encryption

The module uses the AES algorithm with 256-bit keys for all of its symmetric encryption, but the process is largely the same no matter what algorithm you use. When you want to encrypt a piece of application data, whatever it happens to be, you use an algorithm such as AES to do that part. Symmetric algorithms are very fast, and they can be applied to data of any size. Generally speaking, you will use a randomly-generated key for each thing you want to encrypt. From that point on, everything becomes about securely sharing copies of that random, 32-byte AES key; you no longer have to worry about the application data itself, because everyone uses the same encrypted copy of that.


RSA

RSA is the most commonly-used form of public-key cryptography today, so far as I know, and it’s from RSA that my original understanding of public-key techniques originated. You only need a single public / private key pair in order to either sign data and verify signatures, or to encrypt and decrypt data. The basic process for sharing encrypted data looks like this:

  1. Encrypt your application data with AES, using a randomly-generated AES key.
  2. Using the recipient’s RSA public key, encrypt a copy of the AES key.
  3. Send the encrypted AES key to the recipient. Using their private key, they’ll be able to decrypt the AES key, and then use that to decrypt the actual data.

ECDH

Recently, I added support for ECC (Elliptic Curve Cryptography) to the ProtectedData module. ECC is a different mathematical approach to public-key crypto, which can supposedly be more secure than older algorithms such as RSA, while using much smaller key sizes.

There is no elliptic-curve variant of RSA, so in order to add some form of ECC support to the module, I had to figure out a new algorithm. In the .NET Framework, the only ECC algorithm that is suitable for protecting copies of AES keys is ECDH, or Elliptic Curve Diffie-Hellman.

Diffie-Hellman, unlike RSA, requires two public / private key pairs. At a very high level, the math magic behind Diffie-Hellman is based on the principle that if you combine the private key from one pair and the public key from another pair, using a certain algorithm, you will get the same result. It doesn’t matter which public / private key you use, so long as it’s one private and one public.

The overall process of using ECDH is a little bit more complicated than the process of using RSA:

  1. Encrypt your application data using AES, using a randomly-generated AES key. (I’ll refer to this as the “payload key” later, to avoid confusion.)
  2. Read the recipient’s ECDH public key from their certificate.
  3. Randomly generate a new ECDH public / private key pair. (This is generally referred to as an “ephemeral key”.)
  4. Using your new ephemeral private key, and the recipient’s public key, produce a second 32-byte AES key (which I’ll refer to as the “derived key”.)
  5. Encrypt your payload key with AES, using the derived key.
  6. Send the encrypted copy of the payload key, along with your ephemeral ECDH public key, to the recipient. They will be able to combine their ECDH private key with your ephemeral public key to produce the same derived key, which will decrypt the payload key, which will decrypt the data.

Password-derived keys

There’s a backup mechanism in the ProtectedData module which allows you to decrypt the data with a password, instead of with a certificate. Like ECDH, this involves producing a derived key, and then using AES to encrypt a copy of your payload key. So long as the receiving party knows the password, they can produce the same derived key.


That pretty much sums up the encryption / decryption techniques I’ve learned to leverage in scripts so far. This doesn’t touch on digital signatures at all; the ProtectedData module is purely for encryption and decryption at this point (to prevent information disclosure.) I may follow up on this article with a closer look at how each of these techniques is accomplished within the .NET Framework, if people are interested.

About Dave Wyatt

Microsoft MVP (PowerShell), IT professional.
This entry was posted in PowerShell, Professional. Bookmark the permalink.

9 Responses to Learning to Apply Cryptography

  1. Casey Robertson says:

    Exactly what I’m looking for. Hope I can figure it out! Looks like you can use password based decryption? I assume you can use credentials in the machine store too? Cert:\MY\etc\etc. If domain-joined machines I guess using our internal PKI would be the best? The use case is a Powershell script where we want to use Invoke-RestMethod to work with F5 load balancers. The challenge is securing the logon creds. Encrypting to and from a string works fine when I run it locally of course but DPAPI doesn’t allow us to run this as a script via SCCM (since the agent uses the machine account). Anyway, thanks again – if you are willing to answer questions please let me know via email or here on the comments.

    Like

  2. Dave Wyatt says:

    If you are just wanting to use the ProtectedData module as-is, it’s pretty simple. It does support password-based encryption, but that doesn’t help you with unattended scripts since you’d have to hard-code or protect the password anyway. I included that functionality in the ProtectedData module mainly as a backup mechanism; so one could retrieve their data even if they somehow lost their private key.

    For unattended scripts, you should just use a certificate. You’d either generate or install a certificate on the server(s) where the script needs to run, and use that certificate when encrypting the credentials. The general usage would look something like this, assuming you have a secret in variable $secret (which should be a string, SecureString, PSCredential, or byte array), and a certificate thumbprint in a variable named $thumbprint.

    # To encrypt the credentials and save them to an xml file:
    $encrypted = $secret | Protect-Data -Certificate Cert:\LocalMachine\My\$thumbprint -SkipCertificateVerification
    $encrypted | Export-Clixml -Path c:\someFileName.xml

    # To decrypt the credentials from your SCCM script:
    $encrypted = Import-Clixml -Path c:\someFileName.xml
    $secret = $encrypted | Unprotect-Data -Certificate Cert:\LocalMachine\My\$thumbprint -SkipCertificateVerification

    The -SkipCertiifcateVerification switch is something you’ll use if you want to leverage self-signed certificates. (I’ve never actually NOT used this switch, so maybe I should just make that behavior the default at some point.) I used LocalMachine cert:\ drive paths in this example (which would be appropriate for something running as LocalSystem), but you can also install the cert in a CurrentUser store, or even just pass in a path to a .cer file on disk for the encryption step (without installing it.).

    If you have other questions, feel free to contact me. You can open issues on the project’s GitHub page, or just email me at dlwyatt115@gmail.com .

    Like

    • Casey Robertson says:

      This is great…thanks Dave. I assume after “-Certificate” I’m passing the thumbprint?

      Any specific attributes the cert requires if I have it generated internally by our enterprise CA? Figure that would be the best route. Or how have you seen it implemented?

      Thanks – if you prefer me to email let me know.

      Casey

      Like

      • Dave Wyatt says:

        The -Certificate parameter accepts a number of options. You can just pass in a thumbprint if you want (assuming the certificate is installed in either the current user or local computer’s stores), but you can also pass in an X509Certificate2 object, or a full path to the .CER file or to the certificate on the Cert:\ drive (which will perform better than just passing in the thumbprint, particularly in PowerShell v2.0.)

        The certificate needs to have the “Key Encipherment” (for RSA) / “Key Agreement” (for ECDH) key usage extension enabled. Other than that, no special requirements. As far as I know, the standard User and Computer cert templates both meet this requirement, so you shouldn’t have to worry about it.

        Like

  3. Casey Robertson says:

    Ok Dave – forgive me….but on the Cert request….do I need to mark the private key as exportable? Then the target machines need that private key passed in the PowerShell to decrypt the creds that were encrypted with the public key? Sorry – new to all this.

    Like

  4. Dave Wyatt says:

    Doesn’t matter whether the private key is exportable, as far as using the module is concerned. That just affects whether you can create a .pfx / .p12 file containing the certificate and its private key later.

    The machine that’s doing the decryption does need the private key, though, and if you’re planning to distribute the same certificate to multiple computers, then you’ll need to be able to export it.

    Like

    • Casey Robertson says:

      Worked like a charm Dave……exported the key from my management machine as a .pfx. Imported on another box. Ran the modules and it decrypted the xml creds and connected to my F5 load balancer using those creds. Thanks again! Added your blog to my permanent “follow” list.

      Casey

      Like

  5. Matt D. says:

    If I’m understanding this right…

    I if I want to use a PowerShell script that is run on client devices which uses an encrypted domain SA account, the client needs to have access to either the AES.Key or the Private.Cer.

    Is there no method to do this where the end user can’t directly decrypt the password (even if difficult)?

    Like

    • Dave Wyatt says:

      That’s correct. In order to keep that sort of thing secret from the end-user, the secret must _never_ exist in decrypted form on their computer. If it does, then they can always get to the value themselves.

      What I do in these situations is set up a custom PowerShell remoting endpoint somewhere, and grant users access to that. When you set up the endpoint, there is a -RunAsCredential option, and it can then perform actions on the user’s behalf without ever exposing the password to them. You can use the new xJea module for that, if you’re using PowerShell v5, or you can just create the endpoint using the same processes that have been around since PowerShell v2 and v3. (Register-PSSessionConfiguration, etc.) Boe Prox did a great series of articles for the “Hey, Scripting Guy!” blog back in 2014: http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/31/introduction-to-powershell-endpoints.aspx

      Like

Leave a comment