我需要生成用于代理之间安全通信的证书。每个代理生成一个证书,并且必须将其发送到另一台计算机上的系统CA,以进行签名(并由其他代理信任)。我正在使用C#,并为代理使用以下代码:
//generate certificate
ECDsa elipticCurveNistP256Key = ECDsa.Create(ECCurve.CreateFromValue("1.2.840.10045.3.1.7")); // nistP256 curve
CertificateRequest certificateRequest = new CertificateRequest("CN=" + agentId, elipticCurveNistP256Key, HashAlgorithmName.SHA256);
certificateRequest.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, false));
certificateRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
false));
// Add the SubjectAlternativeName extension
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Parse(agentIpAddress));
certificateRequest.CertificateExtensions.Add(sanBuilder.Build());
certificateRequest.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.8")
},
true));
certificateRequest.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
以及CA系统的以下代码:
X509Certificate2 signedCertificate = certificateRequest.Create(
caCertificatePFX,
DateTimeOffset.UtcNow.AddDays(-1),
DateTimeOffset.UtcNow.AddDays(30),
new byte[] {1, 2, 3, 4});
当然,我也使用代码在此处未显示的机器之间进行通信。但是我至少有两个问题:
我希望在证书生成和签名之间有一个完全的隔离,但是即使尝试了很多,这也是我可以设法上班的唯一代码。如果我没有记错的话,此代码在CA系统上创建了证书,这不是理想的情况(CA可以访问代理私钥),但是如果我没有找到更好的证书,那么我可以接受。
第二个问题是,即使我接受第一个问题,我仍然需要将CertificateRequest对象从一台计算机发送到另一台计算机,并且CertificateRequest无法序列化。我发现方法CreateSigningRequest()“创建代表当前对象状态的ASN.1 DER编码的PKCS#10 CertificationRequest值”。但是我还没有找到使它再次成为CertificateRequest对象的方法,以便可以运行CA系统代码。
有人知道我该怎么做吗?希望将证书的生成和证书的签名完全分开,但是如果那不可能的话,至少要创建一个CertificateRequest对象。
谢谢
如您所述,没有办法回读PKCS#10请求。这主要是因为缺少太多东西,无法成为“确定的”证书颁发机构,因此拥有读者只会使很多“不良的”证书颁发机构受益。 (由于您的CA不支持吊销,因此它也是一个“不良”的CA,但是您可以使用较短的生存期证书来缓解这种情况。)
PKCS#10请求包含:
如果您不使用数据格式,则数据格式版本是无关紧要的,并且对于“封闭式”颁发者(仅向直接已知方颁发证书的CA),签名并不是很重要。因此,您只需要传输公钥以及请求所需的任何其他数据(查看您的当前代码,代理ID和IP地址)。
唯一棘手的部分是发送公钥...但是使用.NET Core 3.0+,您可以将所有密钥标准化为其SubjectPublicKeyInfo格式:
byte[] spki = elipticCurveNistP256Key.ExportSubjectPublicKeyInfo();
虽然PublicKey
类型具有ImportSubjectPublicKeyInfo方法非常聪明,但尚未发生。对于通用解析,您想尝试所有主要的密钥类型,但是由于您是另一侧的封闭式CA,因此您可以先验地知道它是ECDSA:
using (ECDsa clientPub = ECDsa.Create())
{
clientPub.ImportSubjectPublicKeyInfo(transmittedSpki, out _);
// the rest of your code goes here.
}
我强烈建议使用CA软件签署证书请求。期间。
任何尝试拥有自己的CA代码的尝试都会使解决方案在许多方面都不可靠,易碎且容易出错。从Microsoft ADCS(Windows)和EJBCA(Windows / Linux)开始,有几个选项。任何其他设计都将很糟糕。