【问题标题】:Why does my private key not work to decrypt a key encrypted by the public key?为什么我的私钥无法解密由公钥加密的密钥?
【发布时间】:2016-07-17 16:00:59
【问题描述】:

我正在尝试将 this MSDN page 示例代码转换为 Powershell 脚本。

目标是创建由公钥加密的文件,以便传输到可以访问私钥的环境中,以便对其进行解密。

我正在使用 New-SelfSignedCertificate 创建证书,因为我不需要任何人信任这些证书,并且我使用的是直接从 Windows 密钥库中获取的带有公钥和私钥的完整证书,因为我是仍然只是测试代码。

我的问题是,当我加密时,一切似乎都正常,但在解密时我收到错误消息:

错误:使用“2”参数调用“解密”的异常:“要发送的数据 被解密超过了这个模数的最大值 128 字节。"

看起来New-SelfSignedCertificate 正在创建 2048 位的公钥,即使在 certs mmc snapin 中我可以看到证书有一个私钥,我也无法通过 UI 或通过代码。

例如下面的代码:

$cert = Get-Item 'Cert:\LocalMachine\AddressBook\<ThumbPrint>'
$cert.HasPrivateKey
$cert.PrivateKey

结果

True

和空

这里是代码

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,但这似乎不起作用,而且我真的不想使用更小的密钥大小,我宁愿弄清楚这有什么问题代码。

感谢您的帮助!

比尔

【问题讨论】:

  • @briantist 我不认为这是重复的,因为我没有尝试使用 RSA 加密/解密整个文件,只有文件前几个字节中的 AES 密钥。
  • 所以数据不超过128字节?
  • 根据 MSDN 示例,数据为 256 字节。该数据长度应该没问题,因为加密有效,而不是解密。我也无法修改代码以导致更小的密钥大小。
  • 虽然加密似乎有效,但另一个问题的解密似乎也发生了错误,数据大小是问题所在。 This answer to another question(从第一个链接)提供了更好的解释。

标签: powershell encryption rsa encryption-asymmetric


【解决方案1】:

看来,问题在于 PowerShell v4 中的 New-SelfSignedCertificate 选择提供程序,不适合与 RSACryptoServiceProvider 类一起使用,并且没有允许显式指定提供程序的 -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 的提供程序。

【讨论】:

  • 感谢您提供有关 PS 5 和选择提供商的额外信息。我一直在寻找一个强制更新的充分理由,看起来就是这样。
猜你喜欢
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 2018-02-27
  • 2011-07-24
  • 2010-09-21
  • 1970-01-01
  • 2015-08-23
  • 2022-01-15
相关资源
最近更新 更多