PEMEncrypt is a cross-platform PowerShell module handling string encryption and decryption using RSA keys only. It enables strings to be encrypted when the client only has the public key available, in the event the encrypted string is being sent to a secure endpoint housing the private key where it will be decrypted for further use. The same module can be implemented on the receiving endpoint to decrypt the strings as well, if desired.
Recently, I needed the ability to encrypt strings with only a public RSA key. I only needed to encrypt strings and only had access to the public key to encrypt with, as I would be sending that encrypted string to a secure endpoint which would decrypt the data sent and process it from there.
All of the PowerShell examples I came across online, both on sites like Stack Overflow as well as within modules in the PowerShell Gallery, focused on encrypting strings by using public methods found on the X509Certificate2
class. If I had the full certificate, that wouldn't be an issue, but I only had the public RSA key / PEM file and I soon found out that instantiating an X509Certificate2
with only a public key is a bit difficult, if not impossible.
Install-Module PEMEncrypt -Scope CurrentUser -Repository PSGallery
Please see the Releases section of this repository for instructions.
To view the function help at any time, please use Get-Help Protect-PEMString
or Get-Help Unprotect-PEMString
from your PowerShell session directly.
# Using a password-less private key
$encrypted = 'Hello','How are you today?' | Protect-PEMString -PublicKey .\public.pem
$encrypted | Unprotect-PEMString -PrivateKey .\private.pem
# Use Get-Credential to prompt for credentials so it's not in plain text
$encrypted = 'Hello','How are you today?' | Protect-PEMString -PublicKey .\public_des3.pem
$keyCreds = Get-Credential -UserName key -Message 'Please enter the password for the private key'
$encrypted | Unprotect-PEMString -PrivateKey .\private_des3.pem -Password $keyCreds.Password
# Build a SecureString using a plain-text password
$encrypted = 'Hello','How are you today?' | Protect-PEMString -PublicKey .\public_des3.pem
$password = ConvertTo-SecureString 'P@$$w0rd' -AsPlainText -Force
$encrypted | Unprotect-PEMString -PrivateKey .\private_des3.pem -Password $password
Why not use existing tools like the built-in *-CMSMessage
cmdlets or modules like ProtectedData or EncryptDecrypt?
The answer here is simple: I did not have a certificate and was not in a position to generate a new one for this use case; I had to use the public RSA key provided to me to encrypt my string with. Since I only had the public key and not a valid certificate, I could not use it to instantiate a certificate object.
As outlined by @praus in Issue #3, an RSA key "won't be able to encrypt strings longer than the key modulus size in bytes minus padding size (11 bytes for PKCS #1). For example, if your RSA key is 2048 bit long (therefore its modulus is 2048 bits/256 bytes), you will only be able to encrypt plaintexts that are 245 bytes in length or less. RSACryptoServiceProvider will throw CryptographicException when the plaintext is too long."
If you are running into this error and cannot shorten your string that you are trying to decrypt, you will need to generate a larger key pair to do the encryption with that supports the string size needed.
Interested in helping out with PEMEncrypt development? Please check out our Contribution Guidelines!
Building the module locally to test changes is as easy as running the build.ps1
file in the root of the repo. This will compile the module with your changes and import the newly compiled module at the end by default.
Want to run the Pester tests locally? Pass Test
as the value to the Task
script parameter like so:
.\build.ps1 -Task Test
Please adhere to our Code of Conduct when interacting with this repo.
Here are some helpful commands to generate key pairs with PEMEncrypt in comparison with the same commands in openssl
and ssh-keygen
OpenSSL
# No password
openssl genrsa -out key 4096
openssl rsa -in key -outform PEM -pubout -out key.pem
# With password
openssl genrsa -des3 -out protected 4096 -passout pass:foobar
openssl rsa -in protected -outform PEM -pubout -out protected.pem -passin pass:foobar
PEMEncrypt
- Generates public/private key pair at once by default.
- Defaults to 4096 bit-length, but length left here for comparison.
# No password
## Aliased command below translates to: New-RSAKeyPair -NoSSH -Length 4096 -Path key
genrsa -out key 4096
# With password
## Aliased command below translates to: New-RSAKeyPair -NoSSH -Length 4096 -Path key -Password foobar
genrsa -out protected -p foobar 4096
ssh-keygen
- Prompts for password if none is specified, even if no password is desired
# No password
ssh-keygen -f ~/.ssh/id_newkey -t rsa -b 4096
# With password
ssh-keygen -f ~/.ssh/id_protected -t rsa -b 4096 -N foobar
PEMEncrypt
- Generates the key pair with no password when no password provided
# No password
## Aliased command below translates to: New-RSAKeyPair -NoPEM -Length 4096 -Path ~/.ssh/id_newkey
genssh -out ~/.ssh/id_newkey -b 4096
# With password
## Aliased command below translates to: New-RSAKeyPair -NoPEM -Length 4096 -Path ~/.ssh/id_protected -Password foobar
genssh -out ~/.ssh/id_protected -b 4096 -p foobar