using System.Security.Cryptography;using System.Security.Cryptography.X509Certificates; // .NET 9+: X509CertificateLoader replaces the obsolete X509Certificate2 constructorsusing X509Certificate2 cert = X509CertificateLoader.LoadCertificateFromFile("cert.pem"); // PEM or DER Console.WriteLine($"subject: {cert.Subject}");Console.WriteLine($"common name: {cert.GetNameInfo(X509NameType.SimpleName, forIssuer: false)}");Console.WriteLine($"issuer: {cert.Issuer}");Console.WriteLine($"serial: {cert.SerialNumber}");Console.WriteLine($"valid: {cert.NotBefore.ToUniversalTime():O} -> {cert.NotAfter.ToUniversalTime():O}");Console.WriteLine($"signed with: {cert.SignatureAlgorithm.FriendlyName}"); // the issuer's algorithm foreach (var san in cert.Extensions.OfType<X509SubjectAlternativeNameExtension>()){ foreach (string dns in san.EnumerateDnsNames()) Console.WriteLine($"DNS name: {dns}");}foreach (var usage in cert.Extensions.OfType<X509KeyUsageExtension>()){ Console.WriteLine($"key usage: {usage.KeyUsages}");} using ECDsa? key = cert.GetECDsaPublicKey(); // null for RSA or Ed25519 certificatesif (key is not null){ Console.WriteLine($"key: ECDSA {key.KeySize}-bit, curve {key.ExportParameters(false).Curve.Oid.Value}");}Console.WriteLine($"SHA-256 fingerprint: {cert.GetCertHashString(HashAlgorithmName.SHA256)}"); // Validate against your own trust anchor instead of the machine's root storeusing X509Certificate2 root = X509CertificateLoader.LoadCertificateFromFile("ca.pem");using X509Certificate2 leaf = X509CertificateLoader.LoadCertificateFromFile("leaf.pem"); using var chain = new X509Chain();chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;chain.ChainPolicy.CustomTrustStore.Add(root);chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // a test CA publishes no CRL or OCSPbool trusted = chain.Build(leaf);Console.WriteLine($"chain trusted: {trusted}, {chain.ChainElements.Count} certificates");foreach (X509ChainStatus status in chain.ChainStatus) Console.WriteLine($"status: {status.Status}"); How it works
X509CertificateLoader.LoadCertificateFromFileaccepts PEM and DER. For PKCS#12 files it has a separateLoadPkcs12FromFile, so a certificate path can never accidentally load a private key.SubjectandIssuerare display strings;GetNameInfo(X509NameType.SimpleName, false)extracts the common name without string parsing.- Extensions come typed:
X509SubjectAlternativeNameExtension.EnumerateDnsNames()(.NET 7+) lists SAN DNS names,X509KeyUsageExtension.KeyUsagesis a flags enum. GetECDsaPublicKey()returns anECDsaready forVerifyData, ornullwhen the certificate holds another key type.X509ChainwithCustomRootTrustbuilds and validates the path to the root you supply;ChainStatusexplains a failure (UntrustedRoot,NotTimeValid,PartialChain…).
Gotchas
NotBeforeandNotAfterareDateTimevalues in local time (Kind == Local). Convert withToUniversalTime()before printing or comparing with UTC timestamps, as above.new X509Certificate2(path)andnew X509Certificate2(bytes)are obsolete since .NET 9 (warningSYSLIB0057): they silently accept PKCS#12 and other formats.X509CertificateLoaderloads exactly one format per method.SignatureAlgorithmdescribes the issuer's signature over this certificate (sha256ECDSA), not what the certificate's key can do.- On Linux,
X509Chainfetches missing intermediates and revocation data over the network unless told otherwise. In tests and air-gapped hosts setRevocationMode = NoCheckdeliberately, never in production validation.