using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace Fleet.Identity; /// /// 点对点会话的 TLS 证书:自签名的 ECDSA P-256(和身份密钥是两把不同的密钥)。 /// 对端不按证书链校验,而是按 SPKI 的 SHA-256 指纹校验(指纹由服务端经 Agent 列表下发)。 /// public static class NodeCertificate { /// 生成自签名证书,返回无口令的 PFX 字节(文件权限由写入方设为 0600)。 public static byte[] CreateSelfSignedPfx(string commonName, TimeSpan validity, TimeProvider time) { using ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256); var request = new CertificateRequest("CN=" + commonName, key, HashAlgorithmName.SHA256); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension( [new Oid("1.3.6.1.5.5.7.3.1"), new Oid("1.3.6.1.5.5.7.3.2")], critical: false)); DateTimeOffset now = time.GetUtcNow(); using X509Certificate2 certificate = request.CreateSelfSigned(now.AddDays(-1), now.Add(validity)); return certificate.Export(X509ContentType.Pfx); } public static X509Certificate2 LoadPfx(byte[] pfx) => X509CertificateLoader.LoadPkcs12(pfx, null, X509KeyStorageFlags.Exportable); /// 证书公钥(SPKI)的 SHA-256:对端用它来固定这张证书。 public static byte[] SpkiFingerprint(X509Certificate2 certificate) => SHA256.HashData(certificate.PublicKey.ExportSubjectPublicKeyInfo()); }