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());
}