我想做的是创建一个有效的证书来签署电子文档。这需要带有私钥的x509Certificate2对象。我无法确定的是如何使用我创建的CA证书创建具有私钥的签名证书。
如果可能的话,我只想使用.Net代码(我使用的是4.7.2)。
我拥有的代码使用私钥创建了有效的CA证书,但是,子证书没有私钥,因此不能用于对电子文档进行签名。
这里有我现在的代码...
private void CreateCerts()
{
const string issuerName = "Issuer";
const string signerName = "Signer";
var root = GetCertificate(issuerName, StoreName.AuthRoot);
if (root == null)
{
root = CreateRoot(issuerName);
}
var signer = GetCertificate(signerName, StoreName.My);
if (signer == null)
{
signer = CreateSigner(root, signerName);
}
}
private X509Certificate2 CreateSigner(X509Certificate2 root, string name)
{
X509Certificate2 signer = null;
using (RSA rsa = RSA.Create(2048))
{
CertificateRequest req = new CertificateRequest(
$"CN={name}",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, false));
req.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
false));
req.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.8")
},
true));
req.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
using (X509Certificate2 cert = req.Create(
root,
DateTimeOffset.UtcNow.AddDays(-1),
DateTimeOffset.UtcNow.AddDays(90),
new byte[] { 1, 2, 3, 4, 5, 6 }))
{
using (var password = new SecureString())
{
password.AppendChar('P');
password.AppendChar('W');
var childExport = cert.Export(X509ContentType.Pfx, password);
signer = new X509Certificate2(childExport, password);
AddCertToStore(signer, StoreName.My, StoreLocation.LocalMachine);
}
}
}
return signer;
}
private X509Certificate2 GetCertificate(string commonName, StoreName storeName)
{
X509Certificate2 certificate = null;
// Look for a certificate in the local machine store.
// We will search for a certificate that has a CN (common name) that matches
// the currently logged-in user.
using (var store = new X509Store(storeName, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
foreach (var cert in store.Certificates)
{
var subjectNames = cert.SubjectName.Name.Split(',');
foreach (var subjectName in subjectNames)
{
if (subjectName.Equals($"CN={commonName}"))
{
certificate = cert;
break;
}
}
if (certificate != null)
{
break;
}
}
}
return certificate;
}
private X509Certificate2 CreateRoot(string name)
{
X509Certificate2 root = null;
using (RSA parent = RSA.Create(4096))
{
CertificateRequest parentReq = new CertificateRequest(
$"CN={name}",
parent,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
parentReq.CertificateExtensions.Add(
new X509BasicConstraintsExtension(true, false, 0, true));
parentReq.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(parentReq.PublicKey, false));
using (X509Certificate2 parentCert = parentReq.CreateSelfSigned(
DateTimeOffset.UtcNow.AddDays(-45),
DateTimeOffset.UtcNow.AddDays(365)))
{
using (var password = new SecureString())
{
password.AppendChar('P');
password.AppendChar('W');
var export = parentCert.Export(X509ContentType.Pfx, password);
root = new X509Certificate2(export, password);
AddCertToStore(root, StoreName.AuthRoot, StoreLocation.LocalMachine);
}
}
}
return root;
}
private void AddCertToStore(X509Certificate2 cert, StoreName name, StoreLocation location)
{
using (var store = new X509Store(name, location))
{
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
}
}
答案 0 :(得分:1)
从另一个证书创建证书时,会将输入密钥视为仅是公共密钥(实际上,它将接受构造函数中的PublicKey类型)。
如果要将其与私钥匹配,请使用CopyWithPrivateKey(扩展名)方法:
using (X509Certificate2 cert = req.Create(
root,
DateTimeOffset.UtcNow.AddDays(-1),
DateTimeOffset.UtcNow.AddDays(90),
new byte[] { 1, 2, 3, 4, 5, 6 }))
{
using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(rsa))
{
...
}
}