Last active
January 15, 2017 17:14
Star
You must be signed in to star a gist
Certes ACME client POC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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