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:
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 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:
- Encrypt your application data with AES, using a randomly-generated AES key.
- Using the recipient’s RSA public key, encrypt a copy of the AES key.
- 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.
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:
- 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.)
- Read the recipient’s ECDH public key from their certificate.
- Randomly generate a new ECDH public / private key pair. (This is generally referred to as an “ephemeral key”.)
- 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”.)
- Encrypt your payload key with AES, using the derived key.
- 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.
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.