Over the past few days I’ve been writing a new PowerShell module called ProtectedData. Its purpose is to make it easy for script authors to encrypt secret data in such a way that it can be decrypted by multiple users and/or computers while still maintaining security of the data. It does this by leveraging RSA certificates; data is encrypted with the public key of one or more certificates, and can be decrypted by any user / computer that has the associated private key installed. This is in contrast to the default Export-Clixml and ConvertFrom-SecureString commands, which use DPAPI, and the data cannot be shared with other users, or in most cases, cannot be decrypted on other computers either.
Aside from making this module available to the public, my intention is also to build a presentation around the concept of securing secrets in PowerShell, to be given at user groups or other events (perhaps even the PowerShell Summit next year, if all goes well.)
The module isn’t really finished yet, but can be accessed on GitHub at https://github.com/dlwyatt/ProtectedData .
In the short term, I’d love to start to get some feedback on the module, in two main categories:
- If you’re someone who would use a module to perform data encryption, please provide feedback on the user experience. Are the help files, commands and parameters easy to understand? Do they provide the functionality that you feel is necessary to make the module useful? Any new features you’d like to see?
- If you’re a security geek who wants to do a code review, please let me know if you see any spots where I’ve flubbed the security aspects of the code. There’s a high-level description of how data is encrypted at the end of this post, and in the about_ProtectedData help file in the module. The code tries to minimize the amount of time that any plain text data is kept in memory, but that’s almost impossible to 100% guarantee in .NET due to how some of the Crypto classes are implemented (allocating and returning managed byte arrays), combined with the GC’s behavior. Still, the effort is made, and it was an interesting exercise for me to try to keep the potential for in-memory exposure of data to a minimum.
Here’s the bird’s eye view of what the Protect-Data command does:
- Generates a random AES key and IV, and uses these to encrypt the payload.
- Creates copies of the AES key and IV, each copy being protected by a different RSA public key, or by another password-derived AES key. The Password mechanism is intended as a backup, in case you lose access to the certificates, or encrypt the data with the wrong one (for which you don’t have a private key), that sort of thing.
- Creates a ProtectedData object, which contains the CipherText array, an array of KeyData objects (each one of which contains an encrypted copy of the payload’s Key and IV), and a Type field which is used when decrypting and reconstructing the original object in Unprotect-Data.
Password-derived keys and hashes are generated using the System.Security.Cryptography.Rfc2898DeriveBytes class (with two different randomly-generated salts, one for the key derivation, and one for the password hash). Users can configure the iteration count for these hashes, if desired; it defaults to 1000 iterations.
As far as I know, the security of this approach is sound. All of the encryption keys are ultimately protected either by the operating system (certificate private keys), or by the user entering something (passwords). Even if a malicious user has the source code and encrypted data in front of them, they should still need to either guess a password or gain access to a user’s account in order to expose any data.