zxsh-cryptography.ps1
# Functions # 1. Get-RandomBytes # 2. Get-PBKDF2 # 3. ConvertTo-EncryptedBytes # 4. ConvertFrom-EncryptedBytes (# Get-DecryptedText) ######################################## # 1. Get-RandomBytes <# .Synopsis Return a specified number random bytes. .Description Return a specified number random bytes. .INPUTS length Length of random bytes .OUTPUTS Array of random bytes of specified length. .Example C:\PS> get-randombytes 10 32 67 234 128 255 187 159 195 67 126 Return array of 10 random bytes. .Link None #> function Get-RandomBytes { param ( [ushort] $length = 1 ) Write-Debug "Get-RandomBytes[length] : $length" $result = New-Object byte[] $length # ZX: It sounds good in theory to make RNGCryptoServiceProvider a shared resource. # But it is probably more secure to recreate it each time we need it. # Leave it as it is for the time being, until such a time performance becomes an issue. [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($result) return $result } ######################################## # 2. Get-PBKDF2 <# .Synopsis Return a bytes using a passphrase based KDF. .Description Return a bytes using a passphrase based KDF. .INPUTS length Length of random bytes .OUTPUTS Array of random bytes generated via PBKDF2 .Example C:\PS> Get-PBKDF2 10 32 67 234 128 255 187 159 195 67 126 Return array of 10 random bytes. .Link None #> function Get-PBKDF2 { param ( [Parameter(Mandatory)][System.Security.SecureString] $passPhrase, [int] $saltBytesLength = 0, [uint] $itCountMax = 99999, [byte[]] $saltBytes = $null, [uint] $itCount = 0 ) Write-Host "Get-PBKDF2 [passPhrase]=[$passPhrase] [saltBytesLength]=[$saltBytesLength] [itCountMax]=[$itCountMax] [saltBytes]=[$saltBytes] [itCount]=[$itCount]" if ($null -eq $saltBytes) { $saltBytes = Get-RandomBytes $saltBytesLength } $saltBase64 = [Convert]::ToBase64String($saltBytes) if ($itCount -eq 0) { $itCount = Get-Random -Maximum $itCountMax } Write-Debug "Salt bytes: $saltBytes" Write-Debug "Salt bytes: $saltBase64" Write-Debug "It count : $itCount" $pdb = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($passPhrase, $saltBytes, $itCount) return @{ "saltBase64"= $saltBase64; "itCount" = $itCount; "pdb" = $pdb; } } ######################################## # 3. ConvertTo-EncryptedBytes <# .Synopsis Return a specified number random bytes. .Description Return a specified number random bytes. .INPUTS length Length of random bytes .OUTPUTS Array of random bytes of specified length. .Example C:\PS> get-randombytes 10 32 67 234 128 255 187 159 195 67 126 Return array of 10 random bytes. .Link None #> function ConvertTo-EncryptedBytes { param ( [Parameter(Mandatory)][string] $algorithmName, [Parameter(Mandatory)][System.Security.SecureString] $passPhrase, [Parameter(Mandatory)][string] $plainText ) Write-Host "Get-EncryptedBytes [algorithmName]=[$algorithmName] [passPhrase]=[$passPhrase] [plainText]=[$plainText]" [System.Security.Cryptography.SymmetricAlgorithm] $alg = $null [System.Collections.Hashtable] $pbkdf2 = $null $saltBase64 = $null $itCount = $null [System.Security.Cryptography.Rfc2898DeriveBytes] $pdb = $null $ivBase64 = $null $cipherBase64 = $null [System.Security.Cryptography.ICryptoTransform] $encryptor = $null [System.IO.MemoryStream] $ms = $null [System.Security.Cryptography.CryptoStream] $encryptionStream = $null [System.IO.StreamWriter] $sw = $null $alg = [System.Security.Cryptography.SymmetricAlgorithm]::Create($algorithmName) if ($null -eq $alg) { Write-Error "Invalid algorithm name. Ensure that its one of the following: [AES, DES, RC2, Rijndael, 3DES]" return } $pbkdf2 = Get-PBKDF2 $passPhrase ($alg.BlockSize / 8) $saltBase64 = $pbkdf2["saltBase64"] $itCount = $pbkdf2["itCount"] $pdb = $pbkdf2["pdb"] $alg.Key = $pdb.GetBytes($alg.KeySize / 8) Write-Host "EncKey $([Convert]::ToBase64String($alg.Key))" Write-Host "EncKey $([Convert]::ToBase64String($alg.IV))" try { $encryptor = $alg.CreateEncryptor($alg.Key, $alg.IV) $ms = [System.IO.MemoryStream]::new() $encryptionStream = [System.Security.Cryptography.CryptoStream]::new($ms, $encryptor, [System.Security.Cryptography.CryptoStreamMode]::Write) $sw = [System.IO.StreamWriter]::new($encryptionStream) $sw.AutoFlush = $true $sw.Write($plainText) $sw.Flush() $sw.Close() $encryptionStream.Close() $ms.Close() $ivBase64 = [Convert]::ToBase64String($alg.IV) $cipherBase64 = [Convert]::ToBase64String($ms.ToArray()) return [ordered]@{ "Alg" = $algorithmName; "Salt" = $saltBase64; "It" = $itCount; "IV" = $ivBase64; "Cipher"= $cipherBase64; } } catch { Write-Error $_.Exception } finally { if ($null -ne $sw) { $sw.Close() } if ($null -ne $encryptionStream) { $encryptionStream.Close() } if ($null -ne $ms) { $ms.Close() } } return $null } ######################################## # 4. ConvertFrom-EncryptedBytes <# .Synopsis Return a specified number random bytes. .Description Return a specified number random bytes. .INPUTS length Length of random bytes .OUTPUTS Array of random bytes of specified length. .Example C:\PS> get-randombytes 10 32 67 234 128 255 187 159 195 67 126 Return array of 10 random bytes. .Link None #> function ConvertFrom-EncryptedBytes { param ( [Parameter(Mandatory)][string] $algorithmName, [Parameter(Mandatory)][System.Security.SecureString] $passPhrase, [Parameter(Mandatory)][string] $cipherText, [Parameter(Mandatory)][int] $itCount, [Parameter(Mandatory)][string] $saltBase64, [Parameter(Mandatory)][string] $ivBase64 ) Write-Host "Get-DecryptedText [algorithmName]=[$algorithmName] [passPhrase]=[$passPhrase] [cipherText]=[$cipherText]" [System.Security.Cryptography.SymmetricAlgorithm] $alg = $null [System.Collections.Hashtable] $pbkdf2 = $null [System.Security.Cryptography.Rfc2898DeriveBytes] $pdb = $null $plainText = $null [System.Security.Cryptography.ICryptoTransform] $decryptor = $null [System.IO.MemoryStream] $ms = $null [System.Security.Cryptography.CryptoStream] $decryptionStream = $null [System.IO.StreamReader] $sr = $null $alg = [System.Security.Cryptography.SymmetricAlgorithm]::Create($algorithmName) if ($null -eq $alg) { Write-Error "Invalid algorithm name. Ensure that its one of the following: [AES, DES, RC2, Rijndael, 3DES]" return } # Restore pbkdf2 $pbkdf2 = Get-PBKDF2 $passPhrase -itCount $itCount -saltBytes ([Convert]::FromBase64String($saltBase64)) $pdb = $pbkdf2["pdb"] # Restore symmetric encryption algorithm $alg.Key = $pdb.GetBytes($alg.KeySize / 8) $alg.IV = [Convert]::FromBase64String($ivBase64) Write-Host "ResKey $([Convert]::ToBase64String($alg.Key))" Write-Host "ResKey $([Convert]::ToBase64String($alg.IV))" # Decrypting... try { $decryptor = $alg.CreateDecryptor($alg.Key, $alg.IV) [byte[]] $cipherBytes = [Convert]::FromBase64String($cipherText) $ms = [System.IO.MemoryStream]::new($cipherBytes) $decryptionStream = [System.Security.Cryptography.CryptoStream]::new($ms, $decryptor, [System.Security.Cryptography.CryptoStreamMode]::Read) $sr = [System.IO.StreamReader]::new($decryptionStream) $plainText = $sr.ReadToEnd() $sr.Close() $decryptionStream.Close() $ms.Close() return $plainText } catch [System.Security.Cryptography.CryptographicException] { Write-Error "Cannot decrypt content. Please check password." } catch { Write-Error "Error" $_.Exception } return $plainText } |