Skip to content

Instantly share code, notes, and snippets.

@ridercz
Last active January 15, 2017 17:14
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
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 = "www.example.com";
private const string CONTACT_EMAIL = "webmaster@example.com";
private const string PFX_PASSWORD = "password";
static void Main(string[] args) {
// Most methods
MainAsync().GetAwaiter().GetResult();
}
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);
Console.WriteLine("OK");
// Accept terms of services
Console.Write("Accepting TOS...");
account.Data.Agreement = account.GetTermsOfServiceUri();
account = await client.UpdateRegistration(account);
Console.WriteLine("OK");
// Initialize authorization
Console.Write("Sending authorization request...");
var authz = await client.NewAuthorization(new AuthorizationIdentifier {
Type = AuthorizationIdentifierTypes.Dns,
Value = HOST_NAME
});
// 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("OK");
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");
Console.ReadLine();
// Info ACME server to validate the identifier
Console.Write("Completing challenge...");
var httpChallenge = await client.CompleteChallenge(httpChallengeInfo);
Console.WriteLine("OK");
// Check authorization status
Console.Write("Waiting for authorization..");
while (true) {
Console.Write(".");
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();
csr.AddName($"CN={HOST_NAME}");
var cert = await client.NewCertificate(csr);
Console.WriteLine("OK");
// 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);
Console.WriteLine("OK");
// Export PFX
Console.Write("Exporting PFX...");
var pfxBuilder = cert.ToPfx();
var pfx = pfxBuilder.Build(HOST_NAME, PFX_PASSWORD);
File.WriteAllBytes($"{fileNameBase}.pfx", pfx);
Console.WriteLine("OK");
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment