在 Windows 上不使用 OpenSSL 从 pfx 文件或证书存储中提取私钥

Posted

技术标签:

【中文标题】在 Windows 上不使用 OpenSSL 从 pfx 文件或证书存储中提取私钥【英文标题】:Extract private key from pfx file or certificate store WITHOUT using OpenSSL on Windows 【发布时间】:2019-03-26 13:20:13 【问题描述】:

正如标题所示,我想在不使用 OpenSSL 或任何其他第三方工具的情况下导出我的私钥。如果我需要 .cer 文件或 .pfx 文件,我可以通过 MMC 或 PowerShell pkiclient 轻松导出这些文件,但我找不到获取私钥的方法。

https://docs.microsoft.com/en-us/powershell/module/pkiclient/export-certificate?view=win10-ps

使用https://www.sslshopper.com/ssl-converter.html 之类的在线工具是不行的。

PS版本:

PS C:\Users\oscar> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.17134.228
PSEdition                      Desktop
PSCompatibleVersions           1.0, 2.0, 3.0, 4.0...
BuildVersion                   10.0.17134.228
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

我可以这样获取公钥:

(Get-PfxCertificate -FilePath C:\Users\oscar\Desktop\localhost.pfx).GetPublicKey()

并像这样导出整个证书:

(Get-PfxCertificate -FilePath C:\Users\oscar\Desktop\localhost.pfx).GetRawCertData()

结果来自

PS C:\Users\oscar> $mypwd = ConvertTo-SecureString -String "MyPassword" -Force -AsPlainText
PS C:\Users\oscar> $mypfx = Get-PfxData -FilePath C:\Users\oscar\Desktop\localhost.pfx -Password $mypwd
PS C:\Users\oscar> $mypfx

OtherCertificates EndEntityCertificates
----------------- ---------------------
                [Subject]...


PS C:\Users\oscar> $mypfx.EndEntityCertificates

Thumbprint                                Subject
----------                                -------
8ED4971564E35099D6DB490C3756E2AD43AAAAAA  CN=localhost

测试了来自@Brad 的命令,但出现以下错误。

私钥不能以纯文本形式导出

certutil -exportPFX -p "myPassword" -privatekey -user my <Certificate Serial Number> C:\localhost.pfx

类似于 MMC 证书中的证书导出向导,只有在包含密钥的情况下才能导出到 .pfx

【问题讨论】:

您当然需要一个.pfx 文件,因为.cer 文件不存储私钥。你的$PSVersionTable 是什么?你能用Get-PfxData -FilePath 'mycertificate.pfx' -Password (ConvertTo-SecureString -Force -AsPlainText -String 'MyClearTextPassword')吗? @PetruZaharia 是的,我知道,将其作为您可以导出的示例。 :) 用 PSVersion 和我尝试过的方法更新了问题。我可以,但我还没有找到导出私钥的方法。 【参考方案1】:

我遇到了同样的问题,并在 PS Gallery 的 PSPKI Powershell module 的帮助下解决了它。虽然我了解您正在寻找一种最好使用 Windows 中的某些内置功能的解决方案,但从 PS Gallery 安装模块可能是可以接受的。至少我的情况是这样。

首先安装 PSPKI 模块(我假设 PSGallery 存储库已经设置好了):

Install-Module -Name PSPKI

PSPKI 模块提供了一个 Cmdlet Convert-PfxToPem,它将 pfx 文件转换为 pem 文件,其中包含作为 base64 编码文本的证书和私人密钥:

Convert-PfxToPem -InputFile C:\path\to\pfx\file.pfx -Outputfile C:\path\to\pem\file.pem

现在,我们需要做的就是用一些正则表达式的魔法来拆分 pem 文件。例如,像这样:

(Get-Content C:\path\to\pem\file.pem -Raw) -match "(?ms)(\s*((?<privatekey>-----BEGIN PRIVATE KEY-----.*?-
----END PRIVATE KEY-----)|(?<certificate>-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----))\s*)2"

$Matches["privatekey"] | Set-Content "C:\path\to\key\file.pem"
$Matches["certificate"] | Set-Content "C:\path\to\certificate\file.pem"

【讨论】:

很好的答案,但我不想像你说的那样使用任何第三方库。但是,由于这是迄今为止最好的答案,我会将其标记为已接受,直到有更好的选择为止。 :) 我添加了一个 PowerShell 脚本,它结合了 .NET 方法将私钥导出到 Pkcs8 PEM 文件。我还想指出,PSPKI Convert-PfxToPem 级别非常低;使用 PInvoke 调用 Win32 方法。由于 .NET 添加了对 CNG(下一代加密货币)的支持,因此我们通过 System.Security.Cryptography 命名空间拥有了我们需要的所有功能。 您可以在没有 第三方 库的情况下执行此操作:$cert = Get-PfxCertificate -FilePath $pfxFilePath; Export-Certificate -FilePath $derFilePath -Cert $cert; certutil -encode $derFilePath $pemFilePath | Out-Null 现在您已拥有 pem 文件,请按照发布的答案的其余部分进行操作。 (我希望我们可以在 cmets 中更好地格式化代码......)【参考方案2】:

基于 PowerShellGuy 提到的内容。

这对你有用吗?

# first get your cert, either via pure .NET, or through the PSDrive (Cert:\)

# this is just an example

# get cert from PSDrive
$cert = Get-ChildItem Cert:\LocalMachine\My | where Subject -eq 'CN=MySubject'

# get cert from .NET
$My     = [System.Security.Cryptography.X509Certificates.StoreName]::My
$Store  = [System.Security.Cryptography.X509Certificates.X509Store]::new($My,'localmachine')
$Store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::MaxAllowed)

$cert   = $Store.Certificates | where Subject -eq 'CN=MySubject'

# get private key
# PKCS8, way #1
$BytesPkcs8 = $cert.PrivateKey.ExportPkcs8PrivateKey()
[System.Convert]::ToBase64String($BytesPkcs8)

# PKCS8, way #2
$Pkcs = [System.Security.Cryptography.CngKeyBlobFormat]::Pkcs8PrivateBlob
$BytesPkcs8 = $cert.PrivateKey.Key.Export($Pkcs)
[System.Convert]::ToBase64String($BytesPkcs8)

# RSA
$BytesRsa = $cert.PrivateKey.ExportRSAPrivateKey()
[System.Convert]::ToBase64String($BytesRsa)

那么 Base64 字符串是你要找的吗?

【讨论】:

【参考方案3】:

试试这个以 Pkcs8 格式导出私钥的脚本

Azure PowerShell - Extract PEM from SSL certificate

【讨论】:

很好的答案。虽然这最终提取了我的私钥,其中每行长度为 76 个字符。我不知道这是否真的有区别,但通常行长是 64 个字符。当我使用在线 SSL 工具提取我的私钥时,结果与您的脚本相比是相同的,除了行长不同。 @AnupamChand,我没有遇到换行长度的任何问题。事实上,我见过没有换行符且没有格式问题的情况。插入换行符以使其更具可读性。 是的,我可以确认。当我将输出 PEM 与实际 PEM 进行比较时,我的评论只是基于我的观察。但是当我为我的应用程序使用“非格式化”PEM 时,它运行良好。感谢您的脚本。【参考方案4】:

如果我理解正确的话,certutil 应该为你做。

certutil -exportPFX -p "ThePasswordToKeyonPFXFile" my [serialNumberOfCert] [fileNameOfPFx]

【讨论】:

看起来不错,但即使助手说Export certificate and private key 我收到了消息Private key is NOT plain text exportable。我只能导出到.pfx。查看打印屏幕的更新问题。【参考方案5】:

密钥可导出时,可以使用多种 API 导出为多种格式。最常见的是 PKCS#8(行业标准)和 XML(MSFT 专有 afaik)。

看看这些答案:

Export CngKey in PKCS8 with encryption c# C# (.NET) RSACryptoServiceProvider import/export x509 public key blob and PKCS8 private key blob Convert RSACryptoServiceProvider RSA XML key to PKCS8 RSACng and CngKeyBlobFormat import and export formats

【讨论】:

很好的答案,但我更喜欢使用某种内置的 Windows util 而不必编写自己的程序来解决它。【参考方案6】:

试试这样的:

$mypwd = ConvertTo-SecureString -String "MyPassword" -Force -AsPlainText
$mypfx = Get-PfxData -FilePath C:\Users\oscar\Desktop\localhost.pfx -Password $mypwd
Export-PfxCertificate -PFXData $mypfx -FilePath C:\Users\oscar\Desktop\localhost.pfx -Password $NewPwd

【讨论】:

Export-PfxCertificate - “将证书或 PFXData 对象导出到个人信息交换 (PFX) 文件。”我只想要私钥。 docs.microsoft.com/en-us/powershell/module/pkiclient/…【参考方案7】:

嗯。您是否尝试过打开证书存储并以这种方式获取私钥?可以肯定的是,这仅适用于 RSA/DSA 证书。

$store = New-Object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreName]::My,"localmachine")
$store.Open("MaxAllowed")
$cert = $store.Certificates | ?$_.subject -match "^CN=asdasd"
$cert.PrivateKey.ToXmlString($false)
$store.Close()

【讨论】:

我确实从中得到了一个值,但必须对其进行修改。您的代码结果为:&lt;RSAKeyValue&gt;&lt;Modulus&gt;base64 value&lt;/Modulus&gt;&lt;Exponent&gt;AQAB&lt;/Exponent&gt;&lt;/RSAKeyValue&gt;。但是,使用$cert.PrivateKey.ToXmlString(1),然后使用 RSA Key Converter - XML to PEM 进行转换,我确实得到了 base64 中的私钥。不过,我还没有找到一种使用内置 Windows util 的方法。

以上是关于在 Windows 上不使用 OpenSSL 从 pfx 文件或证书存储中提取私钥的主要内容,如果未能解决你的问题,请参考以下文章

windows从源码编译openssl1.1.1(vs2019vs2017)

Slack 通知在 Windows 8 上不起作用

使用 C++/libcurl/openssl 从应用程序保护服务器通信

Python pysftp put_r 在 Windows 上不起作用

在 OpenGL 屏幕中显示消息在 Windows 上不起作用

从 Windows 上的 bash 脚本运行 Openssl - 主题不以“/”开头