注意:答案可以来自 VB.NET 或 C#。我对这个问答没有偏好。
我正在使用下面基于 Certes 和 Bouncy Castle 的代码来完成、生成 Let's Encrypt TLS 证书并将其导入到我的 CurrentUser 存储中。问题是,即使我将私钥标记为可导出,但在生成的证书中找不到它。
正如我们所看到的,导出私钥的选项被禁用,即使密钥存储显示它存在。
我已经完成了这个示例,并且代码似乎做了与我的几乎相同的事情。唯一突出的区别是它加载
RsaKeyPairGenerator
来生成新的密钥对。这不适用于我的情况,因为我没有创建自己的证书,并且我已经拥有证书和密钥 Byte
数组。
我的代码是否遗漏了什么或做错了什么?我没有发现它。如何生成和存储私钥可见且可导出的
X509Certificate2
实例?
这是我的代码:
Public Class Tls
Public Shared Async Function ImportAsync(NewCertificate As X509Certificate2) As Task(Of Result)
Dim oCollection As X509Certificate2Collection
Dim oQuery As Func(Of X509Certificate2, Boolean)
Await Task.CompletedTask
oQuery = Function(OldCertificate) OldCertificate.Subject = NewCertificate.Subject
Using oStore As New X509Store(StoreName.My, StoreLocation.CurrentUser)
oStore.Open(OpenFlags.ReadWrite)
oCollection = New X509Certificate2Collection(oStore.Certificates.Where(oQuery).ToArray)
oStore.RemoveRange(oCollection)
oStore.Add(NewCertificate)
End Using
Return Result.Ok
End Function
Public Shared Async Function GenerateAsync(Order As IOrderContext, CityCode As String) As Task(Of Result(Of X509Certificate2))
Dim oCertificateChain As CertificateChain
Dim oCertificate As X509Certificate2
Dim oPrivateKey As IKey
Dim oInfo As CsrInfo
oInfo = New CsrInfo With {
.OrganizationUnit = "MyUnit",
.Organization = "MyOrg",
.CountryName = "MyCountry",
.Locality = "MyTown",
.State = "MyState"
}
oPrivateKey = KeyFactory.NewKey(KeyAlgorithm.RS256)
oCertificateChain = Await Order.Generate(oInfo, oPrivateKey)
oCertificate = GetSignedCertificate(oCertificateChain.Certificate.ToDer, oPrivateKey.ToDer, CityCode)
Return oCertificate.ToResult
End Function
Private Shared Function GetSignedCertificate(Certificate As Byte(), PrivateKey As Byte(), Password As String) As X509Certificate2
Dim oUnsignedCertificate As X509.X509Certificate
Dim oSignedCertificate As X509Certificate2
Dim oCertificateParser As X509CertificateParser
Dim oCertificateEntry As X509CertificateEntry
Dim oPrivateKey As PrivateKeyInfo
Dim oParameter As AsymmetricKeyParameter
Dim oKeyEntry As AsymmetricKeyEntry
Dim oPfxStore As Pkcs12Store
Dim sSubject As String
Dim oRandom As SecureRandom
Dim eFlags As X509KeyStorageFlags
' Prepare the private key
oPrivateKey = PrivateKeyInfo.GetInstance(PrivateKey)
oParameter = PrivateKeyFactory.CreateKey(oPrivateKey)
oKeyEntry = New AsymmetricKeyEntry(oParameter)
oRandom = New SecureRandom
eFlags = X509KeyStorageFlags.Exportable Or X509KeyStorageFlags.PersistKeySet Or X509KeyStorageFlags.UserKeySet
' Convert to PKCS#12 format
oCertificateParser = New X509CertificateParser
oUnsignedCertificate = oCertificateParser.ReadCertificate(Certificate)
oCertificateEntry = New X509CertificateEntry(oUnsignedCertificate)
sSubject = oUnsignedCertificate.SubjectDN.ToString
oPfxStore = New Pkcs12Store
oPfxStore.SetCertificateEntry(sSubject, oCertificateEntry)
oPfxStore.SetKeyEntry($"{sSubject}_key", oKeyEntry, {oCertificateEntry})
Using oStream As New MemoryStream
oPfxStore.Save(oStream, Password.ToCharArray, oRandom)
oSignedCertificate = New X509Certificate2(oStream.ToArray, Password, eFlags)
End Using
Return oSignedCertificate
End Function
End Class
经过大量拼凑来自各种不同来源的片段(太多记不清了),我想出了这个。它不包含几乎那么多的 Bouncy Castle 东西,但它确实有效。
Private Shared Function GetSignedCertificate(Certificate As Byte(), PrivateKey As Byte()) As X509Certificate2
Dim oSecondaryRsa As RSACryptoServiceProvider
Dim oCertificate As X509Certificate2
Dim oPrimaryRsa As RSACryptoServiceProvider
Dim oParameters As CspParameters
Dim oPrivateKey As PrivateKeyInfo
Dim oParameter As AsymmetricKeyParameter
Dim oKeyEntry As AsymmetricKeyEntry
Dim eFlags As X509KeyStorageFlags
oPrivateKey = PrivateKeyInfo.GetInstance(PrivateKey)
oParameter = PrivateKeyFactory.CreateKey(oPrivateKey)
oKeyEntry = New AsymmetricKeyEntry(oParameter)
oSecondaryRsa = DotNetUtilities.ToRSA(DirectCast(oKeyEntry.Key, RsaPrivateCrtKeyParameters))
oParameters = New CspParameters With
{
.KeyContainerName = Guid.NewGuid.ToString.ToUpperInvariant,
.ProviderType = 1,
.Flags = CspProviderFlags.UseMachineKeyStore
}
oPrimaryRsa = New RSACryptoServiceProvider(oParameters)
oPrimaryRsa.ImportCspBlob(oSecondaryRsa.ExportCspBlob(True))
oPrimaryRsa.PersistKeyInCsp = True
eFlags = X509KeyStorageFlags.Exportable Or X509KeyStorageFlags.PersistKeySet Or X509KeyStorageFlags.UserKeySet
oCertificate = New X509Certificate2(Certificate, String.Empty, eFlags)
Return oCertificate.CopyWithPrivateKey(oPrimaryRsa)
End Function