Skip to content

Instantly share code, notes, and snippets.

Last active January 15, 2017 17:14
Show Gist options
  • Save ridercz/ce6473b9693402882a7ec56fb722ea0c to your computer and use it in GitHub Desktop.
Save ridercz/ce6473b9693402882a7ec56fb722ea0c to your computer and use it in GitHub Desktop.
Certes ACME client POC
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Certes;
using Certes.Acme;
using Certes.Pkcs;
namespace AcmePOC {
class Program {
private const string HOST_NAME = "";
private const string CONTACT_EMAIL = "";
private const string PFX_PASSWORD = "password";
static void Main(string[] args) {
// Most methods
static async Task MainAsync() {
using (var client = new AcmeClient(WellKnownServers.LetsEncryptStaging)) {
// Create new registration
Console.Write("Creating new registration...");
var account = await client.NewRegistraton(CONTACT_EMAIL);
// Accept terms of services
Console.Write("Accepting TOS...");
account.Data.Agreement = account.GetTermsOfServiceUri();
account = await client.UpdateRegistration(account);
// Initialize authorization
Console.Write("Sending authorization request...");
var authz = await client.NewAuthorization(new AuthorizationIdentifier {
Type = AuthorizationIdentifierTypes.Dns,
// Comptue key authorization for http-01
var httpChallengeInfo = authz.Data.Challenges.Where(c => c.Type == ChallengeTypes.Http01).First();
var keyAuthString = client.ComputeKeyAuthorization(httpChallengeInfo);
Console.WriteLine($" Token: {httpChallengeInfo.Token}");
Console.WriteLine($" File: /.well-known/acme-challenge/{httpChallengeInfo.Token}");
Console.WriteLine($" Auth: {keyAuthString}");
// Do something to fullfill the challenge,
// e.g. upload key auth string to well known path, or make changes to DNS
Console.WriteLine("Prepare challenge and press ENTER");
// Info ACME server to validate the identifier
Console.Write("Completing challenge...");
var httpChallenge = await client.CompleteChallenge(httpChallengeInfo);
// Check authorization status
Console.Write("Waiting for authorization..");
while (true) {
authz = await client.GetAuthorization(httpChallenge.Location);
if (authz.Data.Status != EntityStatus.Pending) break;
await Task.Delay(10000);
Console.WriteLine($"OK, result: {authz.Data.Status}");
if (authz.Data.Status == EntityStatus.Valid) {
// Create certificate
Console.Write("Requesting certificate...");
var csr = new CertificationRequestBuilder();
var cert = await client.NewCertificate(csr);
// Display certificate info
var xc = new X509Certificate2(cert.Raw);
Console.WriteLine($" Issuer: {xc.Issuer}");
Console.WriteLine($" Subject: {xc.Subject}");
Console.WriteLine($" Serial number: {xc.SerialNumber}");
Console.WriteLine($" Not before: {xc.NotBefore:o}");
Console.WriteLine($" Not before: {xc.NotAfter:o}");
var fileNameBase = $"{HOST_NAME}_{xc.NotAfter:yyyyMMdd}-{xc.NotAfter:HHmmss}";
// Export CRT
Console.Write("Exporting CRT...");
File.WriteAllBytes($"{fileNameBase}.crt", cert.Raw);
// Export PFX
Console.Write("Exporting PFX...");
var pfxBuilder = cert.ToPfx();
var pfx = pfxBuilder.Build(HOST_NAME, PFX_PASSWORD);
File.WriteAllBytes($"{fileNameBase}.pfx", pfx);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment