FluentCertificates is a library using the Immutable Fluent Builder pattern for easily creating, finding, and exporting certificates. It makes it simple to generate your own certificate chains or just stand-alone self-signed certificates.
This project is published in several NuGet packages:
- FluentCertificates: Top-level package that imports the Builder, Extensions, and Finder packages.
- FluentCertificates.Builder: Provides
CertificateBuilder
for building certificates and also includes a bunch of convenient extension methods. Examples below - FluentCertificates.Extensions: Provides additional extension methods. Examples below
- FluentCertificates.Finder: Provides
CertificateFinder
for finding certificates across X509Stores and directories. Examples below
Documentation is incomplete. More examples can be found in the project's unit tests.
CertificateBuilder
requires the FluentCertificates.Builder package and is found under the FluentCertificates
namespace.
The absolute minimum needed to create a certificate, whether it's useful or not.
using var cert = new CertificateBuilder().Create();
For signing, exporting and passing to a 3rd party CA.
//A public & private keypair must be created first, outside of the CertificateBuilder, otherwise you'd have no way to retrieve the private-key used for the new CertificateSigningRequest object
using var keys = RSA.Create();
//Creating a CertificateSigningRequest
var csr = new CertificateBuilder()
.SetUsage(CertificateUsage.Server)
.SetSubject(b => b.SetCommonName("*.fake.domain"))
.SetSubjectAlternativeNames(x => x.AddDnsNames("*.fake.domain", "fake.domain"))
.SetKeyPair(keys)
.CreateCertificateSigningRequest();
//The CertificateRequest object is accessible here:
var certRequest = csr.CertificateRequest;
//CSR can be exported to a string
Console.WriteLine(csr.ToPemString());
//Or to a file or StringWriter instance
csr.ExportAsPem("csr.pem");
Using the fluent style:
using var webCert = new CertificateBuilder()
.SetFriendlyName("Example self-signed web-server certificate")
.SetUsage(CertificateUsage.Server)
.SetSubject(b => b.SetCommonName("*.fake.domain"))
.SetSubjectAlternativeNames(x => x.AddDnsNames("*.fake.domain", "fake.domain"))
.SetNotAfter(DateTimeOffset.UtcNow.AddMonths(1))
.Create();
Or alternatively using object initializers (other examples will use fluent style from now on though):
var builder = new CertificateBuilder() {
FriendlyName = "Example self-signed web-server certificate",
Usage = CertificateUsage.Server,
Subject = new X500NameBuilder().SetCommonName("*.fake.domain"),
SubjectAlternativeNames = new GeneralNameListBuilder().AddDnsNames("*.fake.domain", "fake.domain"),
NotAfter = DateTimeOffset.UtcNow.AddMonths(1)
};
using var webCert = builder.Create();
//A CA's expiry date must be later than that of any certificates it will issue
using var issuer = new CertificateBuilder()
.SetFriendlyName("Example root CA")
.SetUsage(CertificateUsage.CA)
.SetSubject(b => b.SetCommonName("Example root CA"))
.SetNotAfter(DateTimeOffset.UtcNow.AddYears(100))
.Create();
//Note: the 'issuer' certificate used must have a private-key attached in order to sign this new certificate
using var clientAuthCert = new CertificateBuilder()
.SetFriendlyName("Example client-auth certificate")
.SetUsage(CertificateUsage.Client)
.SetSubject(b => b.SetCommonName("User: Michael"))
.SetNotAfter(DateTimeOffset.UtcNow.AddYears(1))
.SetIssuer(issuer)
.Create();
using var customCert = new CertificateBuilder()
.SetFriendlyName("Example certificate with customized extensions")
.SetSubject(b => b.SetCommonName("Example certificate with customized extensions"))
.AddExtension(new X509BasicConstraintsExtension(false, false, 0, true))
.AddExtension(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DataEncipherment, true))
.AddExtension(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(Oids.AnyExtendedKeyUsage) }, false))
.SetIssuer(issuer)
.Create();
//Permit the CA cert to issue certificates for specific names and IP addresses
var permittedNames = new GeneralNameListBuilder()
.AddDnsName(".mydomain.local")
.AddEmailAddress("@mydomain.local")
.AddIPAddress(ipAddress: "192.168.0.0", subnetMask: "255.255.255.0")
.Create();
using var issuer = new CertificateBuilder()
.SetFriendlyName("Example constrained root CA")
.SetUsage(CertificateUsage.CA)
.SetSubject(b => b.SetCommonName("Example constrained root CA"))
.SetNotAfter(DateTimeOffset.UtcNow.AddMonths(1))
.SetPathLength(1)
.AddExtension(new X509NameConstraintExtension(permittedNames, null))
.Create();
using var webCert = new CertificateBuilder()
.SetFriendlyName("Example certificate with a CRL distribution point")
.SetUsage(CertificateUsage.Server)
.SetIssuer(issuer)
.SetSubject(b => b.SetCommonName("*.mydomain.local"))
.SetSubjectAlternativeNames(x => x.AddDnsName("*.mydomain.local"))
//Extension specifies CRL URLs
.AddExtension(CertificateRevocationListBuilder.BuildCrlDistributionPointExtension([$"http://crl.mydomain.local/"]))
.Create();
CertificateFinder
requires the FluentCertificates.Finder package and is found under the FluentCertificates
namespace.
The CertificateFinder class allows you to configure, add, and query certificate sources (stores and directories) in a fluent and immutable manner. It supports LINQ queries for flexible certificate searching.
The "common stores" include the CurrentUser and LocalMachine certificate stores, such as "My", "Root", "CA", etc. You can also add custom directories or other X509 stores to search for certificates.
const string thumbprint = "622A2B8374D9BBE3969B91EDBC8F5152783AFC78";
var cert = new CertificateFinder()
.AddCommonStores()
.FirstOrDefault(x => x.Certificate.Thumbprint.Equals(thumbprint, StringComparison.OrdinalIgnoreCase));
var subject = new X500NameBuilder()
.SetOrganization("My Org")
.SetCountry("AU")
.SetCommonName("fake.domain");
var cert = new CertificateFinder()
.AddCommonStores()
.Select(x => x.Certificate)
.Where(x => x.IsValidNow())
.OrderBy(x => !x.HasPrivateKey) //Ensure certs with private keys are listed before those without
.FirstOrDefault(x => subject.EquivalentTo(x.SubjectName, false));
X500NameBuilder
requires the FluentCertificates.Builder package and is found under the FluentCertificates
namespace.
TODO: document this; see unit tests for more examples
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
TODO: document these; see unit tests for more examples
Extension-Method | Description |
---|---|
BuildChain |
|
ExportAsCert |
|
ExportAsPkcs12 |
|
ExportAsPkcs7 |
|
ExportAsPem |
|
ToPemString |
|
ToBase64String |
|
GetPrivateKey |
|
GetSignatureData |
|
GetToBeSignedData |
|
IsValidNow |
|
IsValidAt |
|
IsSelfSigned |
|
IsIssuedBy |
|
VerifyChain |
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
TODO: document these
Extension-Method | Description |
---|---|
ToCollection |
|
ToEnumerable |
|
ExportAsPkcs7 |
|
ExportAsPkcs12 |
|
ExportAsPem |
|
ToPemString |
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
TODO: document these
Extension-Method | Description |
---|---|
ToEnumerable |
|
ExportAsPkcs7 |
|
ExportAsPkcs12 |
|
ExportAsPem |
|
ToPemString |
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
TODO: document these
Extension-Method | Description |
---|---|
ToCollection |
|
FilterPrivateKeys |
|
ExportAsPkcs7 |
|
ExportAsPkcs12 |
|
ExportAsPem |
|
ToPemString |
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
TODO: document these
Extension-Method | Description |
---|---|
ToPrivateKeyPemString |
|
ToPublicKeyPemString |
|
ExportAsPrivateKeyPem |
|
ExportAsPublicKeyPem |
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
Extension-Method | Description |
---|---|
ToPemString() |
Exports the CertificateRequest to a PEM string. |
ExportAsPem(string path) |
Exports the CertificateRequest to the specified PEM file. |
ExportAsPem(TextWriter writer) |
Exports the CertificateRequest in PEM format to the given TextWriter . |
ConvertToBouncyCastle() |
Converts the CertificateRequest to a BouncyCastle Pkcs10CertificationRequest |
These extension methods require the FluentCertificates.Builder package and are found under the FluentCertificates
namespace.
Extension-Method | Description |
---|---|
dnExtension.ConvertToBouncyCastle() |
Converts a DotNet X509Extension to a BouncyCastle X509Extension . |
bcExtension.ConvertToDotNet(string oid) |
Converts a BouncyCastle X509Extension to a DotNet X509Extension . A DotNet X509Extension includes an OID, but a BouncyCastle one doesn't, therefore one must be supplied in the parameters here. |
bcExtension.ConvertToDotNet(DerObjectIdentifier oid) |
Converts a BouncyCastle X509Extension to a DotNet X509Extension . A DotNet X509Extension includes an OID, but a BouncyCastle one doesn't, therefore one must be supplied in the parameters here. |