Using self-signed certificates for encryption

Update: Changed Hash Algorithm from SHA1 to SHA256 and pointed out that the certificate template needs to be properly encoded.

We needed a way to securely store credentials for a number of tasks in our Citrix environment so I suggested using an encryption certificate for that.

It’s quite simple:

[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=CitrixPowershellEncryption"
MachineKeySet = false
KeyLength = 2048
KeySpec = AT_KEYEXCHANGE
HashAlgorithm = SHA256
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 certificate template as ASCII-encoded CitrixEncryptionTemplate.inf (it contains both the data and key encipherment key usage attributes to make it compliant with WMF5) and use it to create a certificate like this:

certreq.exe -new C:\Users\megamorf\Desktop\CitrixEncryptionTemplate.inf C:\Users\megamorf\Desktop\CitrixEncryption.cer

This will do two things:

  1. Generate the public key C:\Users\megamorf\Desktop\CitrixEncryption.cer
  2. Store the full certificate in Cert:\CurrentUser\My

I exported the full certificate with private key and all extensions as pfx and used a long and complex passphrase to protect it.

Import the pfx for the account where you need it and use it like this:

$Cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object {$_.Subject -like "CN=CitrixPowershellEncryption"}

# Encryption:
$msg = "my super secret password"
$EncodedPwd = [system.text.encoding]::UTF8.GetBytes($msg)
$EncryptedBytes = $Cert.PublicKey.Key.Encrypt($EncodedPwd, $true)
$EncryptedPwd = [System.Convert]::ToBase64String($EncryptedBytes)
$EncryptedPwd

lwH5A9ltjlHyFmjJ84A+XSQC+ZQE6Yta/WaYnW1zPwf7tZNeNwbDx4KiiHMGardRO/q+Ixb9DDRT/kWckx8sLzp5MTOfiN5phsT8Nh+hh8GIcLKr0GrSTAhI
5kmvOgrYQAWFVBiS+WXS7v52g6VBNR8D41KgZxMChH5syE8pzNftGMfulDLjaf/pVNloPgfMvRy1yvf49lPJF5sfjEvu4heunJc4RhNZiHIx65SdWKRzOOpq
Jgi+3Qd1/6Qtpzj0ln5qEJEpjx/rRO9VdtTpnpEjYs9sJf0sY9PTHJU2I452jpLyy7RmCclzybz7TBMncZivbcexqpmqkAQ31VVyhg==

# Decryption:
$EncryptedBytes = [System.Convert]::FromBase64String($EncryptedPwd)
$DecryptedBytes = $Cert.PrivateKey.Decrypt($EncryptedBytes, $true)
$DecryptedPwd = [system.text.encoding]::UTF8.GetString($DecryptedBytes)
$DecryptedPwd

my super secret password

Using PowerShell v5

# Encryption:
# version 1 - public key is stored in the filesystem
Protect-CmsMessage -Content $msg -To .\CitrixEncryption.cer -OutFile .\EncryptedMessage.cms

# version 2 - public key from certificate store is used
Protect-CmsMessage -Content $msg -To "CN=CitrixPowershellEncryption" -OutFile .\EncryptedMessage.cms

# Decryption:
Unprotect-CmsMessage -Path .\EncryptedMessage.cms

my super secret password

Sources:

 

Advertisements