RIT*_*ITT 5 encryption powershell openssl
我一直在尝试使用 PowerShell 加密 RSA 私钥,其方式与低于 1.1.0 的 Openssl 版本相同。
使用rashadrivera.com中的优秀代码,我可以使用 PowerShell 从 pfx 文件中提取私钥。然后我想做的是使用与 openssl 相同的密码密钥派生方法来加密私钥。感谢mti2935对 openssl 遗留密钥派生 EVP_BytesToKey 的精彩解释。
我知道这里的方法被认为是过时的,因此这是一个纯粹的学术练习,旨在尝试在 PowerShell 中实现相同的目标。
以下PowerShell函数可以创建pem格式的加密私钥,但openssl无法解密它。它要求输入密码,然后失败。
openssl rsa -check -in .\encryptedprivkey.pem
这是powershell函数
function Export-PrivateKeyPemEncrypted([System.Security.Cryptography.X509Certificates.X509Certificate2]$pfx, [System.String]$outputPath) {
# Process RSA key
$rsa = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($pfx)
$rsaCng = ([System.Security.Cryptography.RSACng]$rsa)
$keyToEncryptThumbPrint = $rsaCng.Key
$dataToEncrypt = $keyToEncryptThumbPrint.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
# Convert the password into a byte array
$passphrase = "passphrase123456"
[byte[]] $passwordBytes = [Text.Encoding]::UTF8.GetBytes($passphrase) # 16 bytes
# Create an 8 byte salt
# to be added onto the end
# of the password to increase its randomness
$array = @()
for($i=0;$i -lt 8;$i++)
{
$array += [math]::Round($(Get-Random -Minimum 50.1 -Maximum 190.1))
}
[byte[]] $saltBytes = $array
# Create a new instance of the MD5 hashing algorythum
$md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
# Pre openssl V1.1.0 way to create an encryption key
# from a password. This is a bit like the
# obelete standard
# RFC2898 PBKDF1, but not exactly.
# Google EVP_BytesToKey
[byte[]]$firstIteration = $md5.ComputeHash($passwordBytes + $saltBytes) # 16 bytes
[byte[]]$secondIteration = $md5.ComputeHash($firstIteration + $passwordBytes + $saltBytes) # 16 bytes
# Derive the encryption key and Initialization vector
[byte[]]$key = $firstIteration + $secondIteration
[byte[]]$IV = $md5.ComputeHash($secondIteration + $passwordBytes + $saltBytes) # 16 bytes
# Geneate an AES symetrical encryption standard object
# This is the encryption algorythum that will encrypt
# our private key using the key derived from the password above
$aesManaged = New-Object System.Security.Cryptography.AesManaged
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
# Instruct AES object what the key and IV are
$aesManaged.Key = $key
$aesManaged.IV = $IV
$ivAsString = [System.BitConverter]::ToString($IV) -replace "-"
Write-Host " iv = $ivAsString"
Write-Host " key = $([System.BitConverter]::ToString($aesManaged.Key) -replace "-")"
# Now do the actual encypting of the RSA private key
# Data to encrypt must be binary formatted into an array
# of bytes
$encryptor = $aesManaged.CreateEncryptor()
[byte[]] $encryptedData = $encryptor.TransformFinalBlock($dataToEncrypt, 0, $dataToEncrypt.Length)
$aesManaged.Dispose()
# Format the base64 string into lines of 64
# which is what openssl does
$base64CertText = [System.Convert]::ToBase64String($encryptedData) -replace ".{64}", "`$&`r`n"
# Creat a variable to store the encypted key
$out = New-Object String[] -ArgumentList 5
# PEM file. See RFC1421 page 24
# for heading explanations
$out[0] = "-----BEGIN RSA PRIVATE KEY-----"
$out[1] = "Proc-Type: 4,ENCRYPTED"
$out[2] = "DEK-Info: AES-256-CBC,$ivAsString`r`n"
$out[3] = $base64CertText
$out[4] = "-----END RSA PRIVATE KEY-----"
$out | Out-File $outputPath
# this removes CR/LF combination that openssl hates
(Get-Content $outputPath) | Set-Content $outputPath
}
Run Code Online (Sandbox Code Playgroud)
要使用 powershell 功能,我首先执行以下操作,将我的证书和私钥提取到名为 mypfx.pfx 的 pfx 文件中
$privKeyPasWd = ConvertTo-SecureString -String "passphrase123456" -Force -AsPlainText
$pfxExportOptions = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$pfxAsCertificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("C:\Certs\mypfx.pfx", $privKeyPasWd, $pfxExportOptions)
$privateKeyName = "C:\Certs\encryptedprivkey.pem"
Export-PrivateKeyPemEncrypted $pfxAsCertificate $privateKeyName
Run Code Online (Sandbox Code Playgroud)
我注意到 PowerShell 函数创建的 pem 文件的大小比使用 openssl 命令加密的相同私钥大
openssl rsa -aes256 -in .\privkey.pem -out .\encryptedprivkey.pem
PowerShell 添加了哪些 Openssl 没有添加的功能?非常感谢您的帮助。
脚本中有两个问题:
由于加密密钥将以 PKCS#1 格式创建,因此要加密的密钥也必须具有 PKCS#1 格式。然而,事实并非如此,因为
$keyToEncryptThumbPrint.Export([System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob)
Run Code Online (Sandbox Code Playgroud)
以 PKCS#8 格式导出密钥(这也是加密密钥比预期长的原因,因为 PKCS#8 格式的密钥是算法标识符和 PKCS#1 格式的密钥的组合,s.here)。
以 PKCS#1 格式导出密钥的一种方法是使用RSA.ExportRSAPrivateKey()方法。为此,必须按如下方式修改脚本:
# Export key in PKCS#1 format
$dataToEncrypt = $rsaCng.ExportRSAPrivateKey()
Run Code Online (Sandbox Code Playgroud)
请注意,使用此方法需要 Powershell 7.x。
第二个问题是没有正确考虑盐与IV之间的关系。IV 不得使用盐来导出,EVP_BytesToKey()就像当前代码中发生的那样。相反,必须生成一个随机 IV,其中 IV 的前 8 个字节是盐 s。有关更多详细信息,请参阅PEM 加密格式部分:
# Create 16 byte IV
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$IV = New-Object System.Byte[](16)
$rng.GetBytes($IV)
# Get 8 byte salt
$saltBytes = $IV[0..7]
Run Code Online (Sandbox Code Playgroud)
块Create an 8 byte salt...[byte[]]$IV = $md5.ComputeHash($secondIteration + $passwordBytes + $saltBytes)并且必须相应地删除该行。
通过这些更改,我可以在我的计算机上创建 PKCS#1 格式的加密密钥,该密钥通过一致性检查并通过 OpenSSL 成功解密:
openssl rsa -check -in <path to encrypted PKCS#1 key>
Run Code Online (Sandbox Code Playgroud)
为了完整起见:对于私钥,从 .NET Core 3.0 开始,除了ExportRSAPrivateKey()上面用于导出 DER 编码的 PKCS#1 密钥之外,还用于ExportPkcs8PrivateKey()导出 DER 编码的 PKCS#8 密钥和ExportEncryptedPkcs8PrivateKey()导出 DER 编码的加密 PKCS #8 键。不支持加密的 PKCS#1 密钥。从 .NET 7 Preview 3 开始,还支持 PEM 导出:ExportRSAPrivateKeyPem()、ExportPkcs8PrivateKeyPem()和ExportEncryptedPkcs8PrivateKeyPem().
完整代码:
function Export-PrivateKeyPemEncrypted([System.Security.Cryptography.X509Certificates.X509Certificate2]$pfx, [System.String]$outputPath) {
# Process RSA key
$rsa = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($pfx)
$rsaCng = ([System.Security.Cryptography.RSACng]$rsa)
# Export key in PKCS#1 format
$dataToEncrypt = $rsaCng.ExportRSAPrivateKey()
# Convert the password into a byte array
$passphrase = "passphrase123456"
[byte[]] $passwordBytes = [Text.Encoding]::UTF8.GetBytes($passphrase) # 16 bytes
# Create 16 byte IV
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$IV = New-Object System.Byte[](16)
$rng.GetBytes($IV)
# Get 8 byte salt
$saltBytes = $IV[0..7]
# Create a new instance of the MD5 hashing algorythum
$md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
# Pre openssl V1.1.0 way to create an encryption key
# from a password. This is a bit like the
# obelete standard
# RFC2898 PBKDF1, but not exactly.
# Google EVP_BytesToKey
[byte[]]$firstIteration = $md5.ComputeHash($passwordBytes + $saltBytes) # 16 bytes
[byte[]]$secondIteration = $md5.ComputeHash($firstIteration + $passwordBytes + $saltBytes) # 16 bytes
# Derive the encryption key and Initialization vector
[byte[]]$key = $firstIteration + $secondIteration
# Geneate an AES symetrical encryption standard object
# This is the encryption algorythum that will encrypt
# our private key using the key derived from the password above
$aesManaged = New-Object System.Security.Cryptography.AesManaged
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
# Instruct AES object what the key and IV are
$aesManaged.Key = $key
$aesManaged.IV = $IV
$ivAsString = [System.BitConverter]::ToString($IV) -replace "-"
Write-Host " iv = $ivAsString"
Write-Host " key = $([System.BitConverter]::ToString($aesManaged.Key) -replace "-")"
# Now do the actual encypting of the RSA private key
# Data to encrypt must be binary formatted into an array
# of bytes
$encryptor = $aesManaged.CreateEncryptor()
[byte[]] $encryptedData = $encryptor.TransformFinalBlock($dataToEncrypt, 0, $dataToEncrypt.Length)
$aesManaged.Dispose()
# Format the base64 string into lines of 64
# which is what openssl does
$base64CertText = [System.Convert]::ToBase64String($encryptedData) -replace ".{64}", "`$&`r`n"
# Creat a variable to store the encypted key
$out = New-Object String[] -ArgumentList 5
# PEM file. See RFC1421 page 24
# for heading explanations
$out[0] = "-----BEGIN RSA PRIVATE KEY-----"
$out[1] = "Proc-Type: 4,ENCRYPTED"
$out[2] = "DEK-Info: AES-256-CBC,$ivAsString`r`n"
$out[3] = $base64CertText
$out[4] = "-----END RSA PRIVATE KEY-----"
$out | Out-File $outputPath
# this removes CR/LF combination that openssl hates
(Get-Content $outputPath) | Set-Content $outputPath
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1679 次 |
| 最近记录: |