public void TestSignFlagJson() { Func <byte[], byte[]> sigFunc = (x) => { using (var rsa = new System.Security.Cryptography.RSACryptoServiceProvider()) { rsa.ImportParameters(JwsTests.GetRsaParamsForRfc7515Example_A_2_1()); using (var sha256 = new System.Security.Cryptography.SHA256CryptoServiceProvider()) { return(rsa.SignData(x, sha256)); } } }; object protectedSample = new // From the RFC example { alg = "RS256" }; object headerSample = new // From the RFC example { kid = "2010-12-29" }; string payloadSample = // From the RFC example "{\"iss\":\"joe\",\r\n" + " \"exp\":1300819380,\r\n" + " \"http://example.com/is_root\":true}"; var wsRegex = new Regex("\\s+"); var sigExpected = // Derived from the RFC example in A.6.4 wsRegex.Replace(@"{ ""header"":{""kid"":""2010-12-29""}, ""protected"":""eyJhbGciOiJSUzI1NiJ9"", ""payload"":""eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"", ""signature"": ""cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"" }", ""); var sigActual = wsRegex.Replace(JwsHelper.SignFlatJson( sigFunc, payloadSample, protectedSample, headerSample), ""); Assert.AreEqual(sigExpected, sigActual); }
public static IssuedCertificate RequestCertificate(AcmeClient client, CertificateProvider certProvider, string hostName, List <string> alternativeNames = null) { if (alternativeNames == null) { alternativeNames = new List <string>(); } PrivateKey pk; var csr = GenerateCSR(certProvider, hostName, alternativeNames, out pk); var certRequest = client.RequestCertificate(JwsHelper.Base64UrlEncode(csr)); if (certRequest.StatusCode != HttpStatusCode.Created) { throw new CertificateApplicationException(certRequest, hostName, alternativeNames.ToArray()); } Crt crt; using (var ms = new MemoryStream()) { certRequest.SaveCertificate(ms); ms.Seek(0, SeekOrigin.Begin); crt = certProvider.ImportCertificate(EncodingFormat.DER, ms); } var caCrt = GetCACertificate(certProvider, certRequest); return(new IssuedCertificate { PublicKey = crt, PrivateKey = pk, CAPublicKey = caCrt }); }
static void RetrieveCertificates(AcmeClient client, string api, string domain, string certificatesFolder, int certBits) { var cp = CertificateProvider.GetProvider(); var rsaPkp = new RsaPrivateKeyParams() { NumBits = certBits }; var rsaKeys = cp.GeneratePrivateKey(rsaPkp); var csrParams = new CsrParams { Details = new CsrDetails { CommonName = domain } }; var derRaw = default(byte[]); var csr = cp.GenerateCsr(csrParams, rsaKeys, Crt.MessageDigest.SHA256); using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64U = JwsHelper.Base64UrlEncode(derRaw); var certRequ = client.RequestCertificate(derB64U); if (certRequ.StatusCode == HttpStatusCode.Created) { GenerateCertFiles(cp, rsaKeys, csr, certRequ, certificatesFolder, domain); } }
public Challenge Decode(IdentifierPart ip, ChallengePart cp, ISigner signer) { if (cp.Type != AcmeProtocol.CHALLENGE_TYPE_DNS) { throw new InvalidDataException("unsupported Challenge type") .With("challengeType", cp.Type) .With("supportedChallengeTypes", AcmeProtocol.CHALLENGE_TYPE_DNS); } //var token = (string)cp["token"]; var token = cp.Token; // This response calculation is described in: // https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.5 var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, token); var keyAuthzDig = JwsHelper.ComputeKeyAuthorizationDigest(signer, token); var ca = new DnsChallengeAnswer { KeyAuthorization = keyAuthz, }; var c = new DnsChallenge(cp.Type, ca) { Token = token, RecordName = $"{AcmeProtocol.DNS_CHALLENGE_NAMEPREFIX}{ip.Value}", RecordValue = keyAuthzDig, }; return(c); }
/// <summary> /// Computes the JWS-signed ACME request body for the given message object /// and signer instance. /// </summary> /// <param name="message"></param> /// <param name="signer"></param> /// <returns></returns> private string ComputeAcmeSigned(object message, ISigner signer) { var protectedHeader = new { nonce = NextNonce }; var unprotectedHeader = new { alg = Signer.JwsAlg, jwk = Signer.ExportJwk() }; var payload = string.Empty; if (message is JObject) { payload = ((JObject)message).ToString(Formatting.None); } else { payload = JsonConvert.SerializeObject(message, Formatting.None); } var acmeSigned = JwsHelper.SignFlatJson(Signer.Sign, payload, protectedHeader, unprotectedHeader); return(acmeSigned); }
public Challenge Decode(IdentifierPart ip, ChallengePart cp, ISigner signer) { if (cp.Type != AcmeProtocol.CHALLENGE_TYPE_SNI) { throw new InvalidDataException("unsupported Challenge type") .With("challengeType", cp.Type) .With("supportedChallengeTypes", AcmeProtocol.CHALLENGE_TYPE_SNI); } var token = cp.Token; // This response calculation is described in: // https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3 var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, token); var keyAuthzDig = JwsHelper.ComputeKeyAuthorizationDigest(signer, token); LOG.Debug("Computed key authorization {0} and digest {1}", keyAuthz, keyAuthzDig); var ca = new TlsSniChallengeAnswer { KeyAuthorization = keyAuthz, }; var c = new TlsSniChallenge(cp.Type, ca) { Token = token, IterationCount = 1 // see: https://github.com/ietf-wg-acme/acme/pull/22 for reason n=1 }; return(c); }
public Challenge Decode(IdentifierPart ip, ChallengePart cp, ISigner signer) { if (cp.Type != AcmeProtocol.CHALLENGE_TYPE_HTTP) { throw new InvalidDataException("unsupported Challenge type") .With("challengeType", cp.Type) .With("supportedChallengeTypes", AcmeProtocol.CHALLENGE_TYPE_HTTP); } //var token = (string)cp["token"]; var token = cp.Token; // This response calculation is described in: // https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.2 var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, token); var path = $"{AcmeProtocol.HTTP_CHALLENGE_PATHPREFIX}{token}"; var url = $"http://{ip.Value}/{path}"; var ca = new HttpChallengeAnswer { KeyAuthorization = keyAuthz, }; var c = new HttpChallenge(cp.Type, ca) { Token = token, FileUrl = url, FilePath = path, FileContent = keyAuthz, }; return(c); }
/// <summary> /// Returns a key-value pair that represents the HTTP resource path that /// needs to be configured (the key) and the resource content that should be returned /// for an HTTP request for this path on a server that the target DNS resolve to. /// </summary> /// <param name="dnsId"></param> /// <param name="signer"></param> /// <returns></returns> public KeyValuePair <string, string> GenerateHttpChallengeAnswer(string dnsId, ISigner signer) { var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, Token); return(new KeyValuePair <string, string>( $"{AcmeProtocol.HTTP_CHALLENGE_PATHPREFIX}{Token}", keyAuthz)); }
public byte[] GetCertificateContent() { if (string.IsNullOrEmpty(CertificateContent)) { return(null); } return(JwsHelper.Base64UrlDecode(CertificateContent)); }
/// <summary> /// </summary> /// <remarks> /// https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05 /// </remarks> public static TlsAlpn01ChallengeValidationDetails ResolveChallengeForTlsAlpn01( Authorization authz, Challenge challenge, IJwsTool signer) { var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, challenge.Token); return(new TlsAlpn01ChallengeValidationDetails { TokenValue = keyAuthz, }); }
public void SaveCertificate(Stream s) { if (string.IsNullOrEmpty(CertificateContent)) { throw new InvalidOperationException("Certificate content is missing or empty"); } var raw = JwsHelper.Base64UrlDecode(CertificateContent); s.Write(raw, 0, raw.Length); }
public void SetCertificateContent(byte[] raw) { if (raw != null && raw.Length > 0) { CertificateContent = JwsHelper.Base64UrlEncode(raw); } else { CertificateContent = null; } }
public static void GetCertificateUseCSR(CertificateGeneratorParam param) { if (param == null) { log.Info($" {nameof(param)} is null"); return; } var dnsIdentifier = DateTime.Now.ToString("yyyyMMddHHmmss"); var cp = CertificateProvider.GetProvider(); MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(param.csr)); var csr = cp.ImportCsr(EncodingFormat.PEM, ms); ms.Dispose(); byte[] derRaw; using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64u = JwsHelper.Base64UrlEncode(derRaw); log.Info($"\nRequesting Certificate"); var certRequ = param.client.RequestCertificate(derB64u); log.Info($" Request Status: {certRequ.StatusCode}"); if (certRequ.StatusCode == System.Net.HttpStatusCode.Created) { var csrPemFile = Path.Combine(param.path, $"{dnsIdentifier}-csr.pem"); var crtDerFile = Path.Combine(param.path, $"{dnsIdentifier}-crt.der"); var crtPemFile = Path.Combine(param.path, $"{dnsIdentifier}-crt.pem"); using (var fs = new FileStream(csrPemFile, FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); log.Info($" Saving Certificate to {crtDerFile}"); using (var file = File.Create(crtDerFile)) certRequ.SaveCertificate(file); Crt crt; using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.PEM, target); } cp.Dispose(); return; } throw new Exception($"Request status = {certRequ.StatusCode}"); }
public JwsSignedPayload Payload() { var protectedHeader = new Dictionary <string, object> { ["alg"] = Algorithm, ["kid"] = KeyIdentifier, ["url"] = Url }; return(JwsHelper.SignFlatJsonAsObject(Sign, AccountKey, protectedHeader, null)); }
[Ignore] // *Normally*, we skip this test because it depends on a local Boulder setup to accessible public void TestNewRegRequest() { var requ = WebRequest.Create(_rootUrl); var resp = requ.GetResponse(); Assert.IsNotNull(resp); var nonceKey = resp.Headers.AllKeys.FirstOrDefault( x => x.Equals("Replay-nonce", StringComparison.OrdinalIgnoreCase)); Assert.IsFalse(string.IsNullOrEmpty(nonceKey)); var nonceValue = resp.Headers[nonceKey]; var newReg = new { resource = "new-reg", contact = new string[] { "mailto:[email protected]", // Tel contact method is no longer supported by Boulder //"tel:+12025551212" }, }; var newRegSer = JsonConvert.SerializeObject(newReg); var algSigner = new RS256Signer(); algSigner.Init(); var unprotectedHeader = new { alg = "RS256", jwk = algSigner.ExportJwk() }; var protectedHeader = new { nonce = nonceValue, }; var acmeJson = JwsHelper.SignFlatJson(algSigner.Sign, newRegSer, protectedHeader, unprotectedHeader); var acmeJsonBytes = Encoding.ASCII.GetBytes(acmeJson); requ = WebRequest.Create(new Uri(_rootUrl, "/acme/new-reg")); requ.Method = "POST"; requ.ContentType = "application/json"; requ.ContentLength = acmeJsonBytes.Length; using (var s = requ.GetRequestStream()) { s.Write(acmeJsonBytes, 0, acmeJsonBytes.Length); } resp = requ.GetResponse(); }
/// <summary> /// </summary> /// <remarks> /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-8.4 /// </remarks> public static Dns01ChallengeValidationDetails ResolveChallengeForDns01( Authorization authz, Challenge challenge, IJwsTool signer) { var keyAuthzDigested = JwsHelper.ComputeKeyAuthorizationDigest( signer, challenge.Token); return(new Dns01ChallengeValidationDetails { DnsRecordName = $@"{Dns01ChallengeValidationDetails.DnsRecordNamePrefix}.{ authz.Identifier.Value}", DnsRecordType = Dns01ChallengeValidationDetails.DnsRecordTypeDefault, DnsRecordValue = keyAuthzDigested, }); }
public AuthorizeChallenge GenerateAuthorizeChallengeAnswer(AuthorizationState authzState, string type) { AssertInit(); AssertRegistration(); var c = authzState.Challenges.FirstOrDefault(x => x.Type == type); if (c == null) { throw new ArgumentOutOfRangeException("no challenge found matching requested type"); } switch (type) { case AcmeProtocol.CHALLENGE_TYPE_DNS: c.ChallengeAnswer = c.GenerateDnsChallengeAnswer(authzState.Identifier, Signer); c.ChallengeAnswerMessage = new AnswerDnsChallengeRequest { ClientPublicKey = Signer.ExportJwk(), Validation = new { header = new { alg = Signer.JwsAlg }, payload = JwsHelper.Base64UrlEncode(JsonConvert.SerializeObject(new { type = type, token = c.Token })), signature = c.ChallengeAnswer.Value, } }; break; case AcmeProtocol.CHALLENGE_TYPE_HTTP: c.ChallengeAnswer = c.GenerateHttpChallengeAnswer(authzState.Identifier, Signer); c.ChallengeAnswerMessage = new AnswerHttpChallengeRequest { KeyAuthorization = c.ChallengeAnswer.Value, }; break; case AcmeProtocol.CHALLENGE_TYPE_SNI: case AcmeProtocol.CHALLENGE_TYPE_PRIORKEY: throw new ArgumentException("unimplemented or unsupported challenge type", nameof(type)); default: throw new ArgumentException("unknown challenge type", nameof(type)); } return(c); }
/// <summary> /// Revoke previously issued certificate /// </summary> /// <param name="binding"></param> public void RevokeCertificate(Target binding) { var fi = new FileInfo(GetPath(binding, "-crt.der")); if (!fi.Exists) { _log.Warning("Unable to find file {fi}", fi.FullName); return; } var der = File.ReadAllBytes(fi.FullName); var base64 = JwsHelper.Base64UrlEncode(der); _client.Acme.RevokeCertificate(base64); _log.Warning("Certificate for {target} revoked, you should renew immediately", binding); }
public AuthorizeChallenge GenerateAuthorizeChallengeAnswer(AuthorizationState authzState, string type) { AssertInit(); AssertRegistration(); var c = authzState.Challenges.FirstOrDefault(x => x.Type == type); if (c == null) { throw new ArgumentOutOfRangeException("no challenge found matching requested type"); } switch (type) { case "dns": c.ChallengeAnswer = c.GenerateDnsChallengeAnswer(authzState.Identifier, Signer); c.ChallengeAnswerMessage = new AnswerDnsChallengeRequest { ClientPublicKey = Signer.ExportJwk(), Validation = new { header = new { alg = Signer.JwsAlg }, payload = JwsHelper.Base64UrlEncode(JsonConvert.SerializeObject(new { type = "dns", token = c.Token })), signature = c.ChallengeAnswer.Value } }; break; case "simpleHttp": var tls = c.Tls.GetValueOrDefault(true); c.ChallengeAnswer = c.GenerateHttpChallengeAnswer(authzState.Identifier, Signer, tls); c.ChallengeAnswerMessage = new AnswerHttpChallengeRequest { Tls = tls }; break; default: throw new ArgumentException("unsupported challenge type", nameof(type)); } return(c); }
/// <summary> /// </summary> /// <remarks> /// https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-8.3 /// </remarks> public static Http01ChallengeValidationDetails ResolveChallengeForHttp01( Authorization authz, Challenge challenge, IJwsTool signer) { var keyAuthz = JwsHelper.ComputeKeyAuthorization( signer, challenge.Token); return(new Http01ChallengeValidationDetails { HttpResourceUrl = $@"http://{authz.Identifier.Value}/{ Http01ChallengeValidationDetails.HttpPathPrefix}/{ challenge.Token}", HttpResourcePath = $@"{Http01ChallengeValidationDetails.HttpPathPrefix}/{ challenge.Token}", HttpResourceContentType = Http01ChallengeValidationDetails.HttpResourceContentTypeDefault, HttpResourceValue = keyAuthz, }); }
/// <summary> /// Returns a key-value pair that represents the Simple HTTP resource path that /// needs to be configured (the key) and the resource content that should be returned /// for an HTTP request for this path on a server that the target DNS resolve to. /// </summary> /// <param name="dnsId"></param> /// <param name="signer"></param> /// <param name="tls"></param> /// <returns></returns> public KeyValuePair<string, string> GenerateHttpChallengeAnswer(string dnsId, ISigner signer, bool tls) { var resp = new { type = "simpleHttp", token = Token, tls = tls }; var json = JsonConvert.SerializeObject(resp); var hdrs = new { alg = signer.JwsAlg, jwk = signer.ExportJwk() }; var signed = JwsHelper.SignFlatJsonAsObject( signer.Sign, json, unprotectedHeaders: hdrs); return new KeyValuePair<string, string>( $"{HTTP_CHALLENGE_PATHPREFIX}{Token}", JsonConvert.SerializeObject(signed, Formatting.Indented)); }
/// <summary> /// Returns a key-value pair that represents the DNS domain name that needs /// to be configured (the key) and the value that should be returned (the value) /// for a query against that domain name for a record of type TXT. /// </summary> /// <param name="dnsId"></param> /// <param name="signer"></param> /// <returns></returns> public KeyValuePair<string, string> GenerateDnsChallengeAnswer(string dnsId, ISigner signer) { var resp = new { type = "dns", token = Token }; var json = JsonConvert.SerializeObject(resp); var hdrs = new { alg = signer.JwsAlg, jwk = signer.ExportJwk() }; var signed = JwsHelper.SignFlatJsonAsObject( signer.Sign, json, unprotectedHeaders: hdrs); /* // We format it as a set of lines broken on 100-character boundaries to make it // easier to copy and put into a DNS TXT RR which normally have a 255-char limit // so this result may need to be broken up into multiple smaller TXT RR entries var sigFormatted = Regex.Replace(signed.signature, "(.{100,100})", "$1\r\n"); */ return new KeyValuePair<string, string>( $"{DNS_CHALLENGE_NAMEPREFIX}{dnsId}", signed.signature); /*sigFormatted);*/ }
public string RequestCertificateAndConvertToPfx(List <Binding> bindings) { var keyGenFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-gen-key.json"); var keyPemFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-key.pem"); var csrGenFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-gen-csr.json"); var csrPemFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-csr.pem"); var crtDerFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-crt.der"); var crtPemFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-crt.pem"); var chainPemFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-chain.pem"); var crtPfxFile = Path.Combine(Config.Path, $"{bindings[0].IPAddress}-all.pfx"); if (Globals.ShouldCreateCertificate()) { // TODOX Should check if the requested certificate (lowercase and sort hostnames) was issued in previous 10 days var cp = CertificateProvider.GetProvider(); var rsaPkp = new RsaPrivateKeyParams(); try { if (Properties.Settings.Default.RSAKeyBits >= 2048) { rsaPkp.NumBits = Properties.Settings.Default.RSAKeyBits; } else { Globals.Log($"Requested key size of {Properties.Settings.Default.RSAKeyBits} is not secure. Using 2048."); rsaPkp.NumBits = 2048; } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Globals.Log($"Unable to set RSA key size, letting ACMESharp select default. Error: {ex}"); Console.ResetColor(); } var rsaKeys = cp.GeneratePrivateKey(rsaPkp); var csrDetails = new CsrDetails { CommonName = bindings[0].Hostname, AlternativeNames = bindings.Select(x => x.Hostname).ToList(), }; var csrParams = new CsrParams { Details = csrDetails, }; var csr = cp.GenerateCsr(csrParams, rsaKeys, Crt.MessageDigest.SHA256); byte[] derRaw; using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64U = JwsHelper.Base64UrlEncode(derRaw); Globals.Log(); Globals.Log("Requesting Certificate"); var certRequ = _Client.RequestCertificate(derB64U); Globals.Log($" - Request Status: {certRequ.StatusCode}"); if (certRequ.StatusCode == System.Net.HttpStatusCode.Created) { using (var fs = new FileStream(keyGenFile, FileMode.Create)) cp.SavePrivateKey(rsaKeys, fs); using (var fs = new FileStream(keyPemFile, FileMode.Create)) cp.ExportPrivateKey(rsaKeys, EncodingFormat.PEM, fs); using (var fs = new FileStream(csrGenFile, FileMode.Create)) cp.SaveCsr(csr, fs); using (var fs = new FileStream(csrPemFile, FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); Globals.Log($" - Saving DER certificate to {crtDerFile}"); using (var file = File.Create(crtDerFile)) certRequ.SaveCertificate(file); Crt crt; using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.PEM, target); } // To generate a PKCS#12 (.PFX) file, we need the issuer's public certificate var isuPemFile = GetIssuerCertificate(certRequ, cp); using (FileStream intermediate = new FileStream(isuPemFile, FileMode.Open), certificate = new FileStream(crtPemFile, FileMode.Open), chain = new FileStream(chainPemFile, FileMode.Create)) { certificate.CopyTo(chain); intermediate.CopyTo(chain); } Globals.Log($" - Saving PFX certificate to {crtPfxFile}"); using (FileStream source = new FileStream(isuPemFile, FileMode.Open), target = new FileStream(crtPfxFile, FileMode.Create)) { var isuCrt = cp.ImportCertificate(EncodingFormat.PEM, source); cp.ExportArchive(rsaKeys, new[] { crt, isuCrt }, ArchiveFormat.PKCS12, target, ""); } cp.Dispose(); // TODOX Should store that the requested certificate (lowercase and sort hostnames) was issued return(crtPfxFile); } else { throw new Exception($"Certificate request status = {certRequ.StatusCode}, uri = {certRequ.Uri}"); } } else { // Don't want to request a new cert, so return the filename to the last requested cert return(crtPfxFile); } }
public static void GetCertificateAutoGen(CertificateGeneratorParam param) { if (param == null) { log.Info($" {nameof(param)} is null"); return; } param.domains = param.domains.Where(o => o.valid).ToList(); if (param.domains.Count == 0) { log.Info($" can't find a valid domain name."); return; } var dnsIdentifier = string.IsNullOrWhiteSpace(param.common_name) ? param.domains.FirstOrDefault().domain : param.common_name.Trim(); var cp = CertificateProvider.GetProvider(); var rsaPkp = new RsaPrivateKeyParams(); var rsaKeys = cp.GeneratePrivateKey(rsaPkp); var csrDetails = new CsrDetails { CommonName = dnsIdentifier, AlternativeNames = param.domains.Select(o => o.domain).ToList(), }; var csrParams = new CsrParams { Details = csrDetails, }; var csr = cp.GenerateCsr(csrParams, rsaKeys, Crt.MessageDigest.SHA256); byte[] derRaw; using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64u = JwsHelper.Base64UrlEncode(derRaw); log.Info($"\nRequesting Certificate"); var certRequ = param.client.RequestCertificate(derB64u); log.Info($" Request Status: {certRequ.StatusCode}"); //log.Info($"Refreshing Cert Request"); //client.RefreshCertificateRequest(certRequ); if (certRequ.StatusCode == System.Net.HttpStatusCode.Created) { var keyGenFile = Path.Combine(param.path, $"{dnsIdentifier}-gen-key.json"); var keyPemFile = Path.Combine(param.path, $"{dnsIdentifier}-key.pem"); var csrGenFile = Path.Combine(param.path, $"{dnsIdentifier}-gen-csr.json"); var csrPemFile = Path.Combine(param.path, $"{dnsIdentifier}-csr.pem"); var crtDerFile = Path.Combine(param.path, $"{dnsIdentifier}-crt.der"); var crtPemFile = Path.Combine(param.path, $"{dnsIdentifier}-crt.pem"); string crtPfxFile = null; crtPfxFile = Path.Combine(param.path, $"{dnsIdentifier}-all.pfx"); using (var fs = new FileStream(keyGenFile, FileMode.Create)) cp.SavePrivateKey(rsaKeys, fs); using (var fs = new FileStream(keyPemFile, FileMode.Create)) cp.ExportPrivateKey(rsaKeys, EncodingFormat.PEM, fs); using (var fs = new FileStream(csrGenFile, FileMode.Create)) cp.SaveCsr(csr, fs); using (var fs = new FileStream(csrPemFile, FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); log.Info($" Saving Certificate to {crtDerFile}"); using (var file = File.Create(crtDerFile)) certRequ.SaveCertificate(file); Crt crt; using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.PEM, target); } // To generate a PKCS#12 (.PFX) file, we need the issuer's public certificate var isuPemFile = GetIssuerCertificate(certRequ, cp, param.account); log.Info($" Saving Certificate to {crtPfxFile} (with no password set)"); using (FileStream source = new FileStream(isuPemFile, FileMode.Open), target = new FileStream(crtPfxFile, FileMode.Create)) { var isuCrt = cp.ImportCertificate(EncodingFormat.PEM, source); cp.ExportArchive(rsaKeys, new[] { crt, isuCrt }, ArchiveFormat.PKCS12, target); } cp.Dispose(); return; } throw new Exception($"Request status = {certRequ.StatusCode}"); }
private void GetCertificate() { Log.Information("Requesting Certificate"); using (CertificateProvider cp = CertificateProvider.GetProvider()) { RsaPrivateKeyParams rsaPkp = new RsaPrivateKeyParams(); PrivateKey rsaKeys = cp.GeneratePrivateKey(rsaPkp); CsrParams csrParams = new CsrParams { Details = new CsrDetails { CommonName = _options.HostName, }, }; Csr csr = cp.GenerateCsr(csrParams, rsaKeys, Crt.MessageDigest.SHA256); byte[] derRaw; using (MemoryStream bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } string derB64U = JwsHelper.Base64UrlEncode(derRaw); CertificateRequest certReq = _acmeClient.RequestCertificate(derB64U); if (certReq.StatusCode != HttpStatusCode.Created) { throw new Exception($"Request status = {certReq.StatusCode}"); } using (FileStream fs = new FileStream(_options.WellKnownFilePaths[WellKnownFile.KeyGen], FileMode.Create)) cp.SavePrivateKey(rsaKeys, fs); using (FileStream fs = new FileStream(_options.WellKnownFilePaths[WellKnownFile.KeyPem], FileMode.Create)) cp.ExportPrivateKey(rsaKeys, EncodingFormat.PEM, fs); using (FileStream fs = new FileStream(_options.WellKnownFilePaths[WellKnownFile.CsrGen], FileMode.Create)) cp.SaveCsr(csr, fs); using (FileStream fs = new FileStream(_options.WellKnownFilePaths[WellKnownFile.CsrPem], FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); Log.Information($"Saving Certificate to {_options.WellKnownFilePaths[WellKnownFile.CrtDer]}"); using (FileStream file = File.Create(_options.WellKnownFilePaths[WellKnownFile.CrtDer])) certReq.SaveCertificate(file); Crt crt; using (FileStream source = new FileStream(_options.WellKnownFilePaths[WellKnownFile.CrtDer], FileMode.Open), target = new FileStream(_options.WellKnownFilePaths[WellKnownFile.CrtPem], FileMode.Create)) { crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.PEM, target); } // To generate a PKCS#12 (.PFX) file, we need the issuer's public certificate string isuPemFile = GetIssuerCertificate(certReq, cp); using (FileStream intermediate = new FileStream(isuPemFile, FileMode.Open), certificate = new FileStream(_options.WellKnownFilePaths[WellKnownFile.CrtPem], FileMode.Open), chain = new FileStream(_options.WellKnownFilePaths[WellKnownFile.ChainPem], FileMode.Create)) { certificate.CopyTo(chain); intermediate.CopyTo(chain); } Log.Information($"Saving Certificate to {_options.WellKnownFilePaths[WellKnownFile.CrtPfx]}"); using (FileStream source = new FileStream(isuPemFile, FileMode.Open), target = new FileStream(_options.WellKnownFilePaths[WellKnownFile.CrtPfx], FileMode.Create)) { Crt isuCrt = cp.ImportCertificate(EncodingFormat.PEM, source); cp.ExportArchive(rsaKeys, new[] { crt, isuCrt }, ArchiveFormat.PKCS12, target, _options.PfxPassword); } } }
protected override void ProcessRecord() { using (var vlt = Util.VaultHelper.GetVault(VaultProfile)) { vlt.OpenStorage(); var v = vlt.LoadVault(); if (v.Registrations == null || v.Registrations.Count < 1) { throw new InvalidOperationException("No registrations found"); } var ri = v.Registrations[0]; var r = ri.Registration; if (v.Certificates == null || v.Certificates.Count < 1) { throw new InvalidOperationException("No certificates found"); } var ci = v.Certificates.GetByRef(CertificateRef); if (ci == null) { throw new Exception("Unable to find a Certificate for the given reference"); } using (var cp = CertificateProvider.GetProvider()) { if (!string.IsNullOrEmpty(ci.GenerateDetailsFile)) { // Generate a private key and CSR: // Key: RSA 2048-bit // MD: SHA 256 // CSR: Details pulled from CSR Details JSON file CsrDetails csrDetails; var csrDetailsAsset = vlt.GetAsset(VaultAssetType.CsrDetails, ci.GenerateDetailsFile); using (var s = vlt.LoadAsset(csrDetailsAsset)) { csrDetails = JsonHelper.Load <CsrDetails>(s); } var keyGenFile = $"{ci.Id}-gen-key.json"; var keyPemFile = $"{ci.Id}-key.pem"; var csrGenFile = $"{ci.Id}-gen-csr.json"; var csrPemFile = $"{ci.Id}-csr.pem"; var keyGenAsset = vlt.CreateAsset(VaultAssetType.KeyGen, keyGenFile, getOrCreate: Force); var keyPemAsset = vlt.CreateAsset(VaultAssetType.KeyPem, keyPemFile, isSensitive: true, getOrCreate: Force); var csrGenAsset = vlt.CreateAsset(VaultAssetType.CsrGen, csrGenFile, getOrCreate: Force); var csrPemAsset = vlt.CreateAsset(VaultAssetType.CsrPem, csrPemFile, getOrCreate: Force); var genKeyParams = new RsaPrivateKeyParams(); var genKey = cp.GeneratePrivateKey(genKeyParams); using (var s = vlt.SaveAsset(keyGenAsset)) { cp.SavePrivateKey(genKey, s); } using (var s = vlt.SaveAsset(keyPemAsset)) { cp.ExportPrivateKey(genKey, EncodingFormat.PEM, s); } // TODO: need to surface details of the CSR params up higher var csrParams = new CsrParams { Details = csrDetails }; var genCsr = cp.GenerateCsr(csrParams, genKey, Crt.MessageDigest.SHA256); using (var s = vlt.SaveAsset(csrGenAsset)) { cp.SaveCsr(genCsr, s); } using (var s = vlt.SaveAsset(csrPemAsset)) { cp.ExportCsr(genCsr, EncodingFormat.PEM, s); } ci.KeyPemFile = keyPemFile; ci.CsrPemFile = csrPemFile; } byte[] derRaw; var asset = vlt.GetAsset(VaultAssetType.CsrPem, ci.CsrPemFile); // Convert the stored CSR in PEM format to DER using (var source = vlt.LoadAsset(asset)) { var csr = cp.ImportCsr(EncodingFormat.PEM, source); using (var target = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, target); derRaw = target.ToArray(); } } var derB64u = JwsHelper.Base64UrlEncode(derRaw); try { using (var c = ClientHelper.GetClient(v, ri)) { c.Init(); c.GetDirectory(true); ci.CertificateRequest = c.RequestCertificate(derB64u); } } catch (AcmeClient.AcmeWebException ex) { ThrowTerminatingError(PoshHelper.CreateErrorRecord(ex, ci)); return; } if (!string.IsNullOrEmpty(ci.CertificateRequest.CertificateContent)) { var crtDerFile = $"{ci.Id}-crt.der"; var crtPemFile = $"{ci.Id}-crt.pem"; var crtDerBytes = ci.CertificateRequest.GetCertificateContent(); var crtDerAsset = vlt.CreateAsset(VaultAssetType.CrtDer, crtDerFile); var crtPemAsset = vlt.CreateAsset(VaultAssetType.CrtPem, crtPemFile); using (Stream source = new MemoryStream(crtDerBytes), derTarget = vlt.SaveAsset(crtDerAsset), pemTarget = vlt.SaveAsset(crtPemAsset)) { var crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.DER, derTarget); ci.CrtDerFile = crtDerFile; cp.ExportCertificate(crt, EncodingFormat.PEM, pemTarget); ci.CrtPemFile = crtPemFile; } // Extract a few pieces of info from the issued // cert that we like to have quick access to var x509 = new X509Certificate2(ci.CertificateRequest.GetCertificateContent()); ci.SerialNumber = x509.SerialNumber; ci.Thumbprint = x509.Thumbprint; ci.SignatureAlgorithm = x509.SignatureAlgorithm?.FriendlyName; ci.Signature = x509.GetCertHashString(); } } vlt.SaveVault(v); WriteObject(ci); } }
/// <summary> /// Request certificate from the ACME server /// </summary> /// <param name="binding"></param> /// <returns></returns> public CertificateInfo RequestCertificate(Target binding) { // What are we going to get? var identifiers = binding.GetHosts(false); var friendlyName = FriendlyName(binding); var pfxPassword = Properties.Settings.Default.PFXPassword; var pfxFileInfo = new FileInfo(PfxFilePath(binding)); // Try using cached certificate first to avoid rate limiting during // (initial?) deployment troubleshooting. Real certificate requests // will only be done once per day maximum. if (pfxFileInfo.Exists && pfxFileInfo.LastWriteTime > DateTime.Now.AddDays(-1)) { try { var cached = new CertificateInfo() { Certificate = ReadForUse(pfxFileInfo, pfxPassword), PfxFile = pfxFileInfo }; var idn = new IdnMapping(); if (cached.SubjectName == identifiers.First() && cached.HostNames.Count == identifiers.Count && cached.HostNames.All(h => identifiers.Contains(idn.GetAscii(h)))) { if (_options.ForceRenewal) { _log.Warning("Cached certificate available but not used with --forcerenewal. Use 'Renew specific' or 'Renew all' in the main menu to run unscheduled renewals without hitting rate limits."); } else { _log.Warning("Using cached certificate for {friendlyName}. To force issue of a new certificate within 24 hours, delete the .pfx file from the CertificatePath or run with the --forcerenewal switch. Be ware that you might run into rate limits doing so.", friendlyName); return(cached); } } } catch { // File corrupt or invalid password? _log.Warning("Unable to read from certificate cache"); } } using (var cp = CertificateProvider.GetProvider("BouncyCastle")) { // Generate the private key and CSR var rsaPkp = GetRsaKeyParameters(); var rsaKeys = cp.GeneratePrivateKey(rsaPkp); var csr = GetCsr(cp, identifiers, rsaKeys, binding.CommonName); byte[] derRaw; using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64U = JwsHelper.Base64UrlEncode(derRaw); // Save request parameters to disk using (var fs = new FileStream(GetPath(binding, "-gen-key.json"), FileMode.Create)) cp.SavePrivateKey(rsaKeys, fs); using (var fs = new FileStream(GetPath(binding, "-key.pem"), FileMode.Create)) cp.ExportPrivateKey(rsaKeys, EncodingFormat.PEM, fs); using (var fs = new FileStream(GetPath(binding, "-gen-csr.json"), FileMode.Create)) cp.SaveCsr(csr, fs); using (var fs = new FileStream(GetPath(binding, "-csr.pem"), FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); // Request the certificate from the ACME server _log.Information("Requesting certificate {friendlyName}", friendlyName); var certificateRequest = _client.Acme.RequestCertificate(derB64U); if (certificateRequest.StatusCode != HttpStatusCode.Created) { throw new Exception($"Request status {certificateRequest.StatusCode}"); } // Main certicate and issuer certificate Crt certificate; Crt issuerCertificate; // Certificate request was successful, save the certificate itself var crtDerFile = GetPath(binding, $"-crt.der"); _log.Information("Saving certificate to {crtDerFile}", _certificatePath); using (var file = File.Create(crtDerFile)) certificateRequest.SaveCertificate(file); // Save certificate in PEM format too var crtPemFile = GetPath(binding, $"-crt.pem"); using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { certificate = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(certificate, EncodingFormat.PEM, target); } // Get issuer certificate and save in DER and PEM formats issuerCertificate = GetIssuerCertificate(certificateRequest, cp); using (var target = new FileStream(GetPath(binding, "-crt.der", "ca-"), FileMode.Create)) cp.ExportCertificate(issuerCertificate, EncodingFormat.DER, target); var issuerPemFile = GetPath(binding, "-crt.pem", "ca-"); using (var target = new FileStream(issuerPemFile, FileMode.Create)) cp.ExportCertificate(issuerCertificate, EncodingFormat.PEM, target); // Save chain in PEM format using (FileStream intermediate = new FileStream(issuerPemFile, FileMode.Open), certificateStrean = new FileStream(crtPemFile, FileMode.Open), chain = new FileStream(GetPath(binding, "-chain.pem"), FileMode.Create)) { certificateStrean.CopyTo(chain); intermediate.CopyTo(chain); } // All raw data has been saved, now generate the PFX file using (var target = new FileStream(pfxFileInfo.FullName, FileMode.Create)) { try { cp.ExportArchive(rsaKeys, new[] { certificate, issuerCertificate }, ArchiveFormat.PKCS12, target, pfxPassword); } catch (Exception ex) { _log.Error("Error exporting archive {@ex}", ex); } } // Flags used for the internally cached certificate var internalFlags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; // See http://paulstovell.com/blog/x509certificate2 try { // Convert Private Key to different CryptoProvider _log.Verbose("Converting private key..."); var res = new X509Certificate2(pfxFileInfo.FullName, pfxPassword, internalFlags); var privateKey = (RSACryptoServiceProvider)res.PrivateKey; res.PrivateKey = Convert(privateKey); res.FriendlyName = friendlyName; File.WriteAllBytes(pfxFileInfo.FullName, res.Export(X509ContentType.Pfx, pfxPassword)); pfxFileInfo.Refresh(); } catch (Exception ex) { // If we couldn't convert the private key that // means we're left with a pfx generated with the // 'wrong' Crypto provider therefor delete it to // make sure it's retried on the next run. _log.Warning("Error converting private key to Microsoft RSA SChannel Cryptographic Provider, which means it might not be usable for Exchange."); _log.Verbose("{ex}", ex); } // Recreate X509Certificate2 with correct flags for Store/Install return(new CertificateInfo() { Certificate = ReadForUse(pfxFileInfo, pfxPassword), PfxFile = pfxFileInfo }); } }
protected override void ProcessRecord() { using (var vp = InitializeVault.GetVaultProvider(VaultProfile)) { vp.OpenStorage(); var v = vp.LoadVault(); if (v.Registrations == null || v.Registrations.Count < 1) { throw new InvalidOperationException("No registrations found"); } var ri = v.Registrations[0]; var r = ri.Registration; if (v.Certificates == null || v.Certificates.Count < 1) { throw new InvalidOperationException("No certificates found"); } var ci = v.Certificates.GetByRef(Ref); if (ci == null) { throw new Exception("Unable to find a Certificate for the given reference"); } if (!string.IsNullOrEmpty(ci.GenerateDetailsFile)) { // Generate a private key and CSR: // Key: RSA 2048-bit // MD: SHA 256 // CSR: Details pulled from CSR Details JSON file CsrHelper.CsrDetails csrDetails; var csrDetailsAsset = vp.GetAsset(VaultAssetType.CsrDetails, ci.GenerateDetailsFile); using (var s = vp.LoadAsset(csrDetailsAsset)) { csrDetails = JsonHelper.Load <CsrHelper.CsrDetails>(s); } var keyGenFile = $"{ci.Id}-gen-key.json"; var keyPemFile = $"{ci.Id}-key.pem"; var csrGenFile = $"{ci.Id}-gen-csr.json"; var csrPemFile = $"{ci.Id}-csr.pem"; var keyGenAsset = vp.CreateAsset(VaultAssetType.KeyGen, keyGenFile); var keyPemAsset = vp.CreateAsset(VaultAssetType.KeyPem, keyPemFile); var csrGenAsset = vp.CreateAsset(VaultAssetType.CsrGen, csrGenFile); var csrPemAsset = vp.CreateAsset(VaultAssetType.CsrPem, csrPemFile); var genKey = CsrHelper.GenerateRsaPrivateKey(); using (var s = vp.SaveAsset(keyGenAsset)) { genKey.Save(s); } using (var w = new StreamWriter(vp.SaveAsset(keyPemAsset))) { w.Write(genKey.Pem); } var genCsr = CsrHelper.GenerateCsr(csrDetails, genKey); using (var s = vp.SaveAsset(csrGenAsset)) { genCsr.Save(s); } using (var w = new StreamWriter(vp.SaveAsset(csrPemAsset))) { w.Write(genCsr.Pem); } ci.KeyPemFile = keyPemFile; ci.CsrPemFile = csrPemFile; } var asset = vp.GetAsset(VaultAssetType.CsrPem, ci.CsrPemFile); byte[] derRaw; using (var s = vp.LoadAsset(asset)) { using (var ms = new MemoryStream()) { CsrHelper.Csr.ConvertPemToDer(s, ms); derRaw = ms.ToArray(); } } var derB64u = JwsHelper.Base64UrlEncode(derRaw); using (var c = ClientHelper.GetClient(v, ri)) { c.Init(); c.GetDirectory(true); ci.CertificateRequest = c.RequestCertificate(derB64u); } if (!string.IsNullOrEmpty(ci.CertificateRequest.CertificateContent)) { var crtDerFile = $"{ci.Id}-crt.der"; var crtPemFile = $"{ci.Id}-crt.pem"; var crtDerAsset = vp.CreateAsset(VaultAssetType.CrtDer, crtDerFile); var crtPemAsset = vp.CreateAsset(VaultAssetType.CrtPem, crtPemFile); using (var s = vp.SaveAsset(crtDerAsset)) { ci.CertificateRequest.SaveCertificate(s); ci.CrtDerFile = crtDerFile; } using (Stream source = vp.LoadAsset(crtDerAsset), target = vp.SaveAsset(crtPemAsset)) { CsrHelper.Crt.ConvertDerToPem(source, target); ci.CrtPemFile = crtPemFile; } var crt = new X509Certificate2(ci.CertificateRequest.GetCertificateContent()); ci.SerialNumber = crt.SerialNumber; ci.Thumbprint = crt.Thumbprint; ci.SignatureAlgorithm = crt.SignatureAlgorithm?.FriendlyName; ci.Signature = crt.GetCertHashString(); } vp.SaveVault(v); WriteObject(ci); } }
public string GetCertificate(AcmeClient client) { IdnMapping idn = new IdnMapping(); var dnsIdentifier = idn.GetAscii(config.Host); CertificateProvider.RegisterProvider <BouncyCastleProvider>(BouncyCastleProvider.PROVIDER_NAME); var cp = CertificateProvider.GetProvider(BouncyCastleProvider.PROVIDER_NAME); var rsaPkp = new RsaPrivateKeyParams(); try { if (config.RSAKeyLength >= 1024) { rsaPkp.NumBits = config.RSAKeyLength; Trace.TraceInformation("RSAKeyBits: {0}", config.RSAKeyLength); } else { Trace.TraceWarning("RSA Key Bits less than 1024 is not secure. Letting ACMESharp default key bits. http://openssl.org/docs/manmaster/crypto/RSA_generate_key_ex.html"); } } catch (Exception ex) { Trace.TraceWarning("Unable to set RSA Key Bits, Letting ACMESharp default key bits, Error: {0}", ex); Console.WriteLine($"Unable to set RSA Key Bits, Letting ACMESharp default key bits, Error: {ex.Message.ToString()}"); } var rsaKeys = cp.GeneratePrivateKey(rsaPkp); var csrDetails = new CsrDetails { CommonName = dnsIdentifier, }; if (config.AlternateNames != null) { if (config.AlternateNames.Count > 0) { csrDetails.AlternativeNames = config.AlternateNames.Select(s => idn.GetAscii(s)); } } var csrParams = new CsrParams { Details = csrDetails, }; var csr = cp.GenerateCsr(csrParams, rsaKeys, Crt.MessageDigest.SHA256); byte[] derRaw; using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64u = JwsHelper.Base64UrlEncode(derRaw); Console.WriteLine($"\nRequesting Certificate"); Trace.TraceInformation("Requesting Certificate"); CertificateRequest certRequ = client.RequestCertificate(derB64u); Trace.TraceInformation("certRequ {0}", JsonConvert.SerializeObject(certRequ)); Console.WriteLine($" Request Status: {certRequ.StatusCode}"); Trace.TraceInformation("Request Status: {0}", certRequ.StatusCode); if (certRequ.StatusCode == System.Net.HttpStatusCode.Created) { var keyGenFile = Path.Combine(this.configPath, $"{dnsIdentifier}-gen-key.json"); var keyPemFile = Path.Combine(configPath, $"{dnsIdentifier}-key.pem"); var csrGenFile = Path.Combine(configPath, $"{dnsIdentifier}-gen-csr.json"); var csrPemFile = Path.Combine(configPath, $"{dnsIdentifier}-csr.pem"); var crtDerFile = Path.Combine(configPath, $"{dnsIdentifier}-crt.der"); var crtPemFile = Path.Combine(configPath, $"{dnsIdentifier}-crt.pem"); string crtPfxFile = null; crtPfxFile = Path.Combine(configPath, $"{dnsIdentifier}-all.pfx"); using (var fs = new FileStream(keyGenFile, FileMode.Create)) cp.SavePrivateKey(rsaKeys, fs); using (var fs = new FileStream(keyPemFile, FileMode.Create)) cp.ExportPrivateKey(rsaKeys, EncodingFormat.PEM, fs); using (var fs = new FileStream(csrGenFile, FileMode.Create)) cp.SaveCsr(csr, fs); using (var fs = new FileStream(csrPemFile, FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); Console.WriteLine($" Saving Certificate to {crtDerFile}"); Trace.TraceInformation("Saving Certificate to {0}", crtDerFile); using (var file = File.Create(crtDerFile)) certRequ.SaveCertificate(file); Crt crt; using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.PEM, target); } // To generate a PKCS#12 (.PFX) file, we need the issuer's public certificate var isuPemFile = GetIssuerCertificate(certRequ, cp); Console.WriteLine($" Saving Certificate to {crtPfxFile}"); Trace.TraceInformation("Saving Certificate to {0}", crtPfxFile); using (FileStream source = new FileStream(isuPemFile, FileMode.Open), target = new FileStream(crtPfxFile, FileMode.Create)) { try { var isuCrt = cp.ImportCertificate(EncodingFormat.PEM, source); cp.ExportArchive(rsaKeys, new[] { crt, isuCrt }, ArchiveFormat.PKCS12, target, config.PFXPassword); } catch (Exception ex) { Trace.TraceError("Error exporting archive {0}", ex); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"Error exporting archive: {ex.Message.ToString()}"); Console.ResetColor(); } } cp.Dispose(); return(crtPfxFile); } if ((int)certRequ.StatusCode == 429) { Trace.TraceError("Unable to request certificate, too many certificate requests to Let's Encrypt certificate servers for the domain within the last 7 days. Please try again later. (If you are testing, please use the staging enviroment where you can request unlimited number of certificates. During the beta period only 5 certificate requests per domain per week are allowed to the production environment.)"); throw new Exception("Unable to request certificate, too many certificate requests to Let's Encrypt certificate servers for the domain within the last 7 days. Please try again later. (If you are testing, please use the staging enviroment where you can request unlimited number of certificates. During the beta period only 5 certificate requests per domain per week are allowed to the production environment.)"); } Trace.TraceError("Request status = {0}", certRequ.StatusCode); throw new Exception($"Request status = {certRequ.StatusCode}"); }
public static string GetCertificate(Target binding) { var dnsIdentifier = binding.Host; var cp = CertificateProvider.GetProvider(); var rsaPkp = new RsaPrivateKeyParams(); var rsaKeys = cp.GeneratePrivateKey(rsaPkp); var csrDetails = new CsrDetails { CommonName = dnsIdentifier, }; var csrParams = new CsrParams { Details = csrDetails, }; var csr = cp.GenerateCsr(csrParams, rsaKeys, Crt.MessageDigest.SHA256); byte[] derRaw; using (var bs = new MemoryStream()) { cp.ExportCsr(csr, EncodingFormat.DER, bs); derRaw = bs.ToArray(); } var derB64u = JwsHelper.Base64UrlEncode(derRaw); Console.WriteLine($"\nRequesting Certificate"); var certRequ = client.RequestCertificate(derB64u); Console.WriteLine($" Request Status: {certRequ.StatusCode}"); //Console.WriteLine($"Refreshing Cert Request"); //client.RefreshCertificateRequest(certRequ); if (certRequ.StatusCode == System.Net.HttpStatusCode.Created) { var keyGenFile = Path.Combine(configPath, $"{dnsIdentifier}-gen-key.json"); var keyPemFile = Path.Combine(configPath, $"{dnsIdentifier}-key.pem"); var csrGenFile = Path.Combine(configPath, $"{dnsIdentifier}-gen-csr.json"); var csrPemFile = Path.Combine(configPath, $"{dnsIdentifier}-csr.pem"); var crtDerFile = Path.Combine(configPath, $"{dnsIdentifier}-crt.der"); var crtPemFile = Path.Combine(configPath, $"{dnsIdentifier}-crt.pem"); string crtPfxFile = null; if (!CentralSSL) { crtPfxFile = Path.Combine(configPath, $"{dnsIdentifier}-all.pfx"); } else { crtPfxFile = Path.Combine(Options.CentralSSLStore, $"{dnsIdentifier}.pfx"); } using (var fs = new FileStream(keyGenFile, FileMode.Create)) cp.SavePrivateKey(rsaKeys, fs); using (var fs = new FileStream(keyPemFile, FileMode.Create)) cp.ExportPrivateKey(rsaKeys, EncodingFormat.PEM, fs); using (var fs = new FileStream(csrGenFile, FileMode.Create)) cp.SaveCsr(csr, fs); using (var fs = new FileStream(csrPemFile, FileMode.Create)) cp.ExportCsr(csr, EncodingFormat.PEM, fs); Console.WriteLine($" Saving Certificate to {crtDerFile}"); using (var file = File.Create(crtDerFile)) certRequ.SaveCertificate(file); Crt crt; using (FileStream source = new FileStream(crtDerFile, FileMode.Open), target = new FileStream(crtPemFile, FileMode.Create)) { crt = cp.ImportCertificate(EncodingFormat.DER, source); cp.ExportCertificate(crt, EncodingFormat.PEM, target); } // To generate a PKCS#12 (.PFX) file, we need the issuer's public certificate var isuPemFile = GetIssuerCertificate(certRequ, cp); Console.WriteLine($" Saving Certificate to {crtPfxFile} (with no password set)"); using (FileStream source = new FileStream(isuPemFile, FileMode.Open), target = new FileStream(crtPfxFile, FileMode.Create)) { var isuCrt = cp.ImportCertificate(EncodingFormat.PEM, source); cp.ExportArchive(rsaKeys, new[] { crt, isuCrt }, ArchiveFormat.PKCS12, target); } cp.Dispose(); return(crtPfxFile); } throw new Exception($"Request status = {certRequ.StatusCode}"); }