为什么我的私钥无法解密由 public 密钥加密的密钥?
Why does my private key not work to decrypt a key encrypted by the public key?
我正在尝试将 this MSDN page 示例代码转换为 Powershell 脚本。
objective 用于创建由 public 密钥加密的文件,以便传输到可以访问私钥的环境中,以便可以对其进行解密。
我正在使用 New-SelfSignedCertificate
来创建证书,因为我不需要任何人信任这些证书,而且我正在使用带有 public 的完整证书和直接来自Windows Key Store 因为我还在测试代码。
我的问题是,当我加密时,似乎一切正常,但当我解密时,我收到错误消息:
ERROR: Exception calling "Decrypt" with "2" argument(s): "The data to
be decrypted exceeds the maximum for this modulus of 128 bytes."
看起来 New-SelfSignedCertificate
正在创建 2048 位的 public 密钥,即使在 certs mmc snapin 中我可以看到该证书有私钥,但我看不到任何它的属性,通过 UI 或代码。
例如下面的代码:
$cert = Get-Item 'Cert:\LocalMachine\AddressBook\<ThumbPrint>'
$cert.HasPrivateKey
$cert.PrivateKey
结果
True
和 NULL
这是代码
function ConvertTo-EncryptedFile
{
[outputType([System.IO.FileInfo])]
param
(
[parameter(Mandatory = $true)]
[string]$path,
[string]$client
)
$cert = Get-ClientCert -client $client
if(Test-Path $path)
{
$file = Get-Item $path
$folder = $file.DirectoryName
$Name = $file.Name
$destination = Join-Path $folder -ChildPath "$Name.encrypted"
$serviceProvider = [System.Security.Cryptography.RSACryptoServiceProvider]$cert.PublicKey.Key
$aesManaged = New-Object System.Security.Cryptography.AesManaged
$aesManaged.KeySize = 256
$aesManaged.BlockSize = 128
$aesManaged.Mode = 'CBC'
$transform = $aesManaged.CreateEncryptor()
$keyformatter = New-Object System.Security.Cryptography.RSAPKCS1KeyExchangeformatter $serviceProvider
[byte[]]$keyEncrypted = $keyformatter.CreateKeyExchange($aesManaged.Key, $aesManaged.GetType())
[byte[]]$lenK = New-Object byte[] 4
[byte[]]$lenIV = New-Object byte[] 4
[int]$lKey = $keyEncrypted.Length
$lenK = [System.BitConverter]::GetBytes($lKey)
[int]$lIV = $aesManaged.IV.Length
$lenIV = [System.BitConverter]::GetBytes($lIV)
$outFS = New-Object System.IO.FileStream @($destination, [System.IO.FileMode]::Create)
$outFS.Write($lenK, 0, 4)
$outFS.Write($lenIV, 0, 4)
$outFS.Write($keyEncrypted, 0, $lKey)
$outFS.Write($aesManaged.IV, 0, $lIV)
$outStreamEncrypted = New-Object System.Security.Cryptography.CryptoStream @($outFS, $transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
$count = 0
$offset = 0
$blockSizeBytes = $aesManaged.BlockSize / 8
$data = New-Object byte[] $blockSizeBytes
$bytesRead = 0
$inFS = New-Object System.IO.FileStream @($path, [System.IO.FileMode]::Open)
do
{
$count = $inFS.Read($data, 0, $blockSizeBytes)
$offset += $count
$outStreamEncrypted.Write($data, 0, $count)
$bytesRead += $blockSizeBytes
}
while ($count -gt 0)
$inFS.Close()
$outStreamEncrypted.FlushFinalBlock()
$outStreamEncrypted.Close()
$outFS.Close()
$inFS.Dispose()
$outStreamEncrypted.Dispose()
$outFS.Dispose()
Remove-Variable transform
$aesManaged.Dispose()
Write-Output (Get-Item $destination)
}
else
{
throw "File to encrypt not found at path: $path"
}
}
function ConvertFrom-EncryptedFile
{
param
(
[parameter(Mandatory = $true)]
[string]$path,
[string]$client
)
$cert = Get-ClientCert -client $client
if (Test-Path $path)
{
$destination = $path.Substring(0, $path.LastIndexOf('.'))
}
else
{
throw "File to decrypt not found at $path"
}
if ($cert.HasPrivateKey)
{
$rsaPrivateKey = New-Object System.Security.Cryptography.RSACryptoServiceProvider ($cert.PrivateKey)
}
$aesManaged = New-Object System.Security.Cryptography.AesManaged
$aesManaged.KeySize = 256
$aesManaged.BlockSize = 128
$aesManaged.Mode = 'CBC'
[byte[]]$lenK = New-Object System.Byte[] 4
[byte[]]$lenIV = New-Object System.Byte[] 4
[System.IO.FileStream]$inFs = New-Object System.IO.FileStream @($path, [System.IO.FileMode]::Open)
$inFs.Seek(0, 'Begin')
$inFs.Seek(0, 'Begin')
$inFs.Read($lenK, 0, 3)
$infs.Seek(4, 'Begin')
$infs.Read($lenIV, 0, 3)
[int]$lenK = [System.BitConverter]::ToInt32($lenK, 0)
[int]$lenIV = [System.BitConverter]::ToInt32($lenIV, 0)
[int]$startC = $lenK + $lenIV + 8
[int]$lenC = [int]$inFs.Length - $startC
[byte[]]$keyEncrypted = New-Object System.Byte[] $lenK
[byte[]]$iv = New-Object System.Byte[] $lenIV
$inFs.Seek(8, 'Begin')
$inFs.Read($keyEncrypted, 0, $lenK)
$inFs.Seek(8 + $lenK, 'Begin')
$inFs.Read($iv, 0, $lenIV)
[byte[]]$keyDecrypted = $rsaPrivateKey.Decrypt($keyEncrypted, $false)
}
它在解密 AES 密钥时停止,因为我还没有能够通过那个障碍。
我试过将 AES 密钥大小从 256 减少到 128,但这似乎不起作用,而且我真的不想使用更小的密钥大小,我宁愿找出问题所在使用此代码。
感谢您的帮助!
比尔
看来,问题是 New-SelfSignedCertificate
在 PowerShell v4 中选择提供程序,它不适合与 RSACryptoServiceProvider
class 一起使用,并且没有 -Provider
参数,允许明确指定提供者。
解决此问题的一个方法是更新到 PowerShell v5。在 PowerShell v5 New-SelfSignedCertificate
cmdlet 中有 -Provider
参数,因此您可以指定所需的提供程序:
PS> $Cert=New-SelfSignedCertificate -DnsName Test -CertStoreLocation Cert:\CurrentUser\My -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider'
PS> $Cert.PrivateKey
PublicOnly : False
CspKeyContainerInfo : System.Security.Cryptography.CspKeyContainerInfo
KeySize : 2048
KeyExchangeAlgorithm : RSA-PKCS1-KeyEx
SignatureAlgorithm : http://www.w3.org/2000/09/xmldsig#rsa-sha1
PersistKeyInCsp : True
LegalKeySizes : {System.Security.Cryptography.KeySizes}
要列出已安装的提供程序,您可以使用以下命令:
PS> $Providers=New-Object -ComObject X509Enrollment.CCspInformations
PS> $Providers.AddAvailableCsps()
PS> $Providers|Format-Table Name,Type
Name Type
---- ----
Microsoft Software Key Storage Provider 0
Microsoft Passport Key Storage Provider 0
Microsoft Smart Card Key Storage Provider 0
Microsoft Base Cryptographic Provider v1.0 1
Microsoft Base DSS and Diffie-Hellman Cryptographic Provider 13
Microsoft Base DSS Cryptographic Provider 3
Microsoft Base Smart Card Crypto Provider 1
Microsoft DH SChannel Cryptographic Provider 18
Microsoft Enhanced Cryptographic Provider v1.0 1
Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider 13
Microsoft Enhanced RSA and AES Cryptographic Provider 24
Microsoft RSA SChannel Cryptographic Provider 12
Microsoft Strong Cryptographic Provider 1
由于您需要支持 RSA 的提供商,因此您需要选择类型为 1、12 或 24 的提供商。
我正在尝试将 this MSDN page 示例代码转换为 Powershell 脚本。
objective 用于创建由 public 密钥加密的文件,以便传输到可以访问私钥的环境中,以便可以对其进行解密。
我正在使用 New-SelfSignedCertificate
来创建证书,因为我不需要任何人信任这些证书,而且我正在使用带有 public 的完整证书和直接来自Windows Key Store 因为我还在测试代码。
我的问题是,当我加密时,似乎一切正常,但当我解密时,我收到错误消息:
ERROR: Exception calling "Decrypt" with "2" argument(s): "The data to be decrypted exceeds the maximum for this modulus of 128 bytes."
看起来 New-SelfSignedCertificate
正在创建 2048 位的 public 密钥,即使在 certs mmc snapin 中我可以看到该证书有私钥,但我看不到任何它的属性,通过 UI 或代码。
例如下面的代码:
$cert = Get-Item 'Cert:\LocalMachine\AddressBook\<ThumbPrint>'
$cert.HasPrivateKey
$cert.PrivateKey
结果
True
和 NULL
这是代码
function ConvertTo-EncryptedFile
{
[outputType([System.IO.FileInfo])]
param
(
[parameter(Mandatory = $true)]
[string]$path,
[string]$client
)
$cert = Get-ClientCert -client $client
if(Test-Path $path)
{
$file = Get-Item $path
$folder = $file.DirectoryName
$Name = $file.Name
$destination = Join-Path $folder -ChildPath "$Name.encrypted"
$serviceProvider = [System.Security.Cryptography.RSACryptoServiceProvider]$cert.PublicKey.Key
$aesManaged = New-Object System.Security.Cryptography.AesManaged
$aesManaged.KeySize = 256
$aesManaged.BlockSize = 128
$aesManaged.Mode = 'CBC'
$transform = $aesManaged.CreateEncryptor()
$keyformatter = New-Object System.Security.Cryptography.RSAPKCS1KeyExchangeformatter $serviceProvider
[byte[]]$keyEncrypted = $keyformatter.CreateKeyExchange($aesManaged.Key, $aesManaged.GetType())
[byte[]]$lenK = New-Object byte[] 4
[byte[]]$lenIV = New-Object byte[] 4
[int]$lKey = $keyEncrypted.Length
$lenK = [System.BitConverter]::GetBytes($lKey)
[int]$lIV = $aesManaged.IV.Length
$lenIV = [System.BitConverter]::GetBytes($lIV)
$outFS = New-Object System.IO.FileStream @($destination, [System.IO.FileMode]::Create)
$outFS.Write($lenK, 0, 4)
$outFS.Write($lenIV, 0, 4)
$outFS.Write($keyEncrypted, 0, $lKey)
$outFS.Write($aesManaged.IV, 0, $lIV)
$outStreamEncrypted = New-Object System.Security.Cryptography.CryptoStream @($outFS, $transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
$count = 0
$offset = 0
$blockSizeBytes = $aesManaged.BlockSize / 8
$data = New-Object byte[] $blockSizeBytes
$bytesRead = 0
$inFS = New-Object System.IO.FileStream @($path, [System.IO.FileMode]::Open)
do
{
$count = $inFS.Read($data, 0, $blockSizeBytes)
$offset += $count
$outStreamEncrypted.Write($data, 0, $count)
$bytesRead += $blockSizeBytes
}
while ($count -gt 0)
$inFS.Close()
$outStreamEncrypted.FlushFinalBlock()
$outStreamEncrypted.Close()
$outFS.Close()
$inFS.Dispose()
$outStreamEncrypted.Dispose()
$outFS.Dispose()
Remove-Variable transform
$aesManaged.Dispose()
Write-Output (Get-Item $destination)
}
else
{
throw "File to encrypt not found at path: $path"
}
}
function ConvertFrom-EncryptedFile
{
param
(
[parameter(Mandatory = $true)]
[string]$path,
[string]$client
)
$cert = Get-ClientCert -client $client
if (Test-Path $path)
{
$destination = $path.Substring(0, $path.LastIndexOf('.'))
}
else
{
throw "File to decrypt not found at $path"
}
if ($cert.HasPrivateKey)
{
$rsaPrivateKey = New-Object System.Security.Cryptography.RSACryptoServiceProvider ($cert.PrivateKey)
}
$aesManaged = New-Object System.Security.Cryptography.AesManaged
$aesManaged.KeySize = 256
$aesManaged.BlockSize = 128
$aesManaged.Mode = 'CBC'
[byte[]]$lenK = New-Object System.Byte[] 4
[byte[]]$lenIV = New-Object System.Byte[] 4
[System.IO.FileStream]$inFs = New-Object System.IO.FileStream @($path, [System.IO.FileMode]::Open)
$inFs.Seek(0, 'Begin')
$inFs.Seek(0, 'Begin')
$inFs.Read($lenK, 0, 3)
$infs.Seek(4, 'Begin')
$infs.Read($lenIV, 0, 3)
[int]$lenK = [System.BitConverter]::ToInt32($lenK, 0)
[int]$lenIV = [System.BitConverter]::ToInt32($lenIV, 0)
[int]$startC = $lenK + $lenIV + 8
[int]$lenC = [int]$inFs.Length - $startC
[byte[]]$keyEncrypted = New-Object System.Byte[] $lenK
[byte[]]$iv = New-Object System.Byte[] $lenIV
$inFs.Seek(8, 'Begin')
$inFs.Read($keyEncrypted, 0, $lenK)
$inFs.Seek(8 + $lenK, 'Begin')
$inFs.Read($iv, 0, $lenIV)
[byte[]]$keyDecrypted = $rsaPrivateKey.Decrypt($keyEncrypted, $false)
}
它在解密 AES 密钥时停止,因为我还没有能够通过那个障碍。
我试过将 AES 密钥大小从 256 减少到 128,但这似乎不起作用,而且我真的不想使用更小的密钥大小,我宁愿找出问题所在使用此代码。
感谢您的帮助!
比尔
看来,问题是 New-SelfSignedCertificate
在 PowerShell v4 中选择提供程序,它不适合与 RSACryptoServiceProvider
class 一起使用,并且没有 -Provider
参数,允许明确指定提供者。
解决此问题的一个方法是更新到 PowerShell v5。在 PowerShell v5 New-SelfSignedCertificate
cmdlet 中有 -Provider
参数,因此您可以指定所需的提供程序:
PS> $Cert=New-SelfSignedCertificate -DnsName Test -CertStoreLocation Cert:\CurrentUser\My -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider'
PS> $Cert.PrivateKey
PublicOnly : False
CspKeyContainerInfo : System.Security.Cryptography.CspKeyContainerInfo
KeySize : 2048
KeyExchangeAlgorithm : RSA-PKCS1-KeyEx
SignatureAlgorithm : http://www.w3.org/2000/09/xmldsig#rsa-sha1
PersistKeyInCsp : True
LegalKeySizes : {System.Security.Cryptography.KeySizes}
要列出已安装的提供程序,您可以使用以下命令:
PS> $Providers=New-Object -ComObject X509Enrollment.CCspInformations
PS> $Providers.AddAvailableCsps()
PS> $Providers|Format-Table Name,Type
Name Type
---- ----
Microsoft Software Key Storage Provider 0
Microsoft Passport Key Storage Provider 0
Microsoft Smart Card Key Storage Provider 0
Microsoft Base Cryptographic Provider v1.0 1
Microsoft Base DSS and Diffie-Hellman Cryptographic Provider 13
Microsoft Base DSS Cryptographic Provider 3
Microsoft Base Smart Card Crypto Provider 1
Microsoft DH SChannel Cryptographic Provider 18
Microsoft Enhanced Cryptographic Provider v1.0 1
Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider 13
Microsoft Enhanced RSA and AES Cryptographic Provider 24
Microsoft RSA SChannel Cryptographic Provider 12
Microsoft Strong Cryptographic Provider 1
由于您需要支持 RSA 的提供商,因此您需要选择类型为 1、12 或 24 的提供商。