I use “config” files all the time in my powershell scripts. This is a way for end-users to have options for the script when run as a task or repeatable programmatic method for completing tasks.
A recent set of scripts copies data from a given source system, that is not domain joined, or azure joined, to cloud based immutable storage (i.e. Azure, AWS, GCP). To do this manually has several different ways. To do via a script, also has several components to it. The most important is the credentials/secret use to access the storage. Again, I use a configuration file to do this.
That file, however, still needs to be protected. In this specific case, the machine is not joined to any cloud or domain (just a workgroup), and whose job is to backup an organizational identity store (Active Directory), then copy that data to immutable storage in the cloud.
To achieve keeping the configuration protected, I am using Protect-CmsMessage and UnProtect-CmsMessage with a self-signed cert. Exporting the certificate with the private key (protected with a password of course) then makes this process portable and independent of the original machine.
Now we are cooking with gas. Here is what I did to achieve this
Main requirements:
- Repeatable (must be able to be repeated, via code, unattended)
- Portable (must not be limited to a specific user or machine or break when any password is changed)
- Must be able to output the same source file on another machine without the original file and be usable with other scripts
The following PowerShell code does the following:
- Creates a self-signed cert for doc encryption using the current machine name
- Request a password to protect the private key in a certificate export
- Export the perviously generated cert based on the thumbprint protecting it with the provide password
#Requires -Version 5.1
#Requires -RunAsAdministrator
$CurDir = split-path -parent $MyInvocation.MyCommand.Definition
$SrcSys = $ENV:COMPUTERNAME
$CertName = "ProtectData-$SrcSys" #Specify the name of the cert to use for encryption
#Create config encryption cert in the local computer cert store, then export with the private key
$Cert = New-SelfSignedCertificate -DnsName "$CertName" -CertStoreLocation "Cert:\LocalMachine\My" -KeyUsage KeyEncipherment,DataEncipherment, KeyAgreement -Type DocumentEncryptionCert
$CertThumb = $Cert.Thumbprint
$CertPass = Read-Host "Please specify the config encryption cert export password (this will be required to import the cert on another machine): " | ConvertTo-SecureString -Force -AsPlainText
$CertParams = @{
Cert = "Cert:\LocalMachine\My\$CertThumb"
FilePath = "$CurDir\$CertName.pfx"
Password = $CertPass
}
Export-PfxCertificate @CertParams
Write-Output "Encryption cert exported to $CurDir\ProtectDataCert.pfx"
Write-Output “Encryption cert exported to $CurDir\ProtectDataCert.pfx”
This cert is now usable by other script on this same machine to decrypt secret data encrypted with this cert.
This cert can also be copied/imported on another machine to decrypt a file containing secret data.
Using this to achieve the goal of protecting file based secrets.
Sample source secret file (c:\temp\secret.txt):
#Amazon s3
$AWSs3Bucket = '<s3BucketName>'
$AWSARNUserName = 'arn:aws:iam::<AWSAccount>:user/<s3UserName>'
$AWSAccessKey = '<AWSs3AccessKey>'
$AWSSecretKey = '<AWSs3SecretKey>'
$AWSs3Region = 'us-west-2'
$AWSDeltaCopy = $true
Command to encrypt/protect source file:
Protect-CmsMessage -Path c:\temp\Secret.txt -To cn=ProtectData-MachineName -OutFile c:\temp\encryptedsecret.txt
Encrypted output:
-----BEGIN CMS-----
MIICjAYJKoZIhvcNAQcDoIICfTCCAnkCAQAxggFRMIIBTQIBADA1MCExHzAdBgNVBAMMFlByb3Rl
Y3REYXRhLU5BMUFERlJSRUMCEGN9PYqQQ7uQRn/rWLfO+MEwDQYJKoZIhvcNAQEHMAAEggEAkxRt
UYURs1239H3JVsDWs10iQuqZ2TByt8EB8p1uFIZ2Q+5pMKgzxCytqvcGxImf7LHGUdrEhS8bXHWu
h+bvPLukG2B0RLBgvXtldV7CYr97iqszqWAvnRrRC7l4sdvXMpZtkfo9Gq11vLrQ30rSmRxKOe6w
MgA8OTdJSu0UFY+P2ythwGJvplEIHaFtqvXKM21cB1WEFWfgHmKC2qF1aBiAysH5ZChG1e0tvB0B
CuSNy3v8VnMkjMc/RTOmjO8Oa7PV5K3AzTJnkI43bEG13b03cRqZP1e1kcmOOZAgNpjVjSBc8cbH
mAwwiZ5EM3sQR18isxtgt9zOPWVmpx2ZvjCCAR0GCSqGSIb3DQEHATAdBglghkgBZQMEASoEEDFS
8UGPFd0QQ/oe0dlVb1OAgfCRfjauhjNzzRE4zStkORQXIeav0A65PP6L13Ak6vokh8bsIqAy8uER
5RPyhWSnB9+CljMQit6R2i8CBMaxL0iu2dWAC3ycWIB1Mx6NP6q/09m/XPFibC0wrR26UlPUyrdB
k3s1d9C7DNfGwQqq7W9qt7yv9k8MKz4kKTg35lQytj0yP66ZWnh2kYjdARuSk9c/uprwYjW3QvSz
lXEYvOJ2UMX4nPso5pnCj3inQNsRLm+E9wURuCvNUiPYYu3pmYwpxExkJyjIliKdpgyRb0l1U10x
lUG88wZfiJ9h1Y6vkCrirYabMM5nP/BhNDf4k68=
-----END CMS-----
Command to decrypt/unprotect “secret” data
Unprotect-CmsMessage -Path c:\temp\encryptedsecret.txt | Out-File c:\temp\Secret.txt -Encoding ascii
The ultimate test for this is to take the encrypted file plus the exported certificate to another machine, and once imported, decrypt using the Unprotect-CmsMessage command to get the original file
UPDATED (11/6/2023):
I have assembled a sample script available on GitHub. Check it out here: https://github.com/cjramseyer/portabledatacrypt
Leave a comment