public async Task <ICertificate> CreateCertificate() { try { _logger.Write("Generating certificate for {0}", domain.ZoneName); IKey privateKey = KeyFactory.NewKey(KeyAlgorithm.ES256); domain.Csr.CommonName = domain.DomainName; CertificateChain cert = await _orderContext.Generate(domain.Csr, privateKey); PfxBuilder pfxBuilder = cert.ToPfx(privateKey); byte[] pfx = pfxBuilder.Build(domain.ZoneName, "cert_password"); FileWorker.WriteFile(pfx, string.Format(@"{0}{1}.pfx", _certDir, domain.ZoneName)); FileWorker.WriteFile(cert.ToPem(certKey: privateKey), string.Format(@"{0}{1}.pem", _certDir, domain.ZoneName)); FileWorker.WriteFile(cert.Certificate.ToDer( ), string.Format(@"{0}{1}.der", _certDir, domain.ZoneName)); return(new Certificate { status = true, Cert = this.GetX509Certificate2(pfx), isExpired = false }); } catch (Exception e) { _logger.Write("Error occured while generating certificate for {0} :: error==>{1}", domain.ZoneName, e.Message); _logger.Write(e.StackTrace); return(new Certificate { status = false, errorDescription = e.Message }); } }
/// <summary> /// Retrieves the certificate from the ACME service. This method also generates the key and the CSR. /// </summary> /// <param name="commonName">the CN of the certificate to be requested</param> /// <param name="pathForPfx">Path where the resulting PFX/PKCS#12 file will be generated</param> /// <param name="pfxFriendlyName">Friendly name for the resulting PFX/PKCS#12</param> /// <returns>The name of the generated PFX/PKCS#12 file, or null in case of error</returns> public async Task <AuthenticatedPFX> RetrieveCertificate(IList <string> domains, string pathForPfx, string pfxFriendlyName) { try { if (_orderCtx == null) { throw new Exception("Do not call RetrieveCertificate before RegisterNewOrderAndVerify"); } if (!System.IO.Directory.Exists(pathForPfx)) { throw new Exception("Directory for PFX writing do not exists"); } InitCertes(); // Let's generate a new key (RSA is good enough IMHO) IKey certKey = KeyFactory.FromPem(Utils.GenerateRSAKeyAsPEM(_keySize)); // Then let's generate the CSR var csr = await _orderCtx.CreateCsr(certKey); csr.AddName("CN", domains[0]); csr.SubjectAlternativeNames = domains; // and finalize the ACME order var finalOrder = await _orderCtx.Finalize(csr.Generate()); // Now we can fetch the certificate CertificateChain cert = await _orderCtx.Download(); // We build the PFX/PKCS#12 and the cert/key as PEM var pfx = cert.ToPfx(certKey); var cer = cert.ToPem(); var key = certKey.ToPem(); pfx.AddIssuers(GetCACertChainFromStore()); var pfxBytes = pfx.Build(pfxFriendlyName, PfxPassword); var fileName = pathForPfx + "\\" + Guid.NewGuid().ToString(); var pfxName = fileName + ".pfx"; var cerPath = fileName + ".cer"; var keyPath = fileName + ".key"; // We write the PFX/PKCS#12 to file System.IO.File.WriteAllBytes(pfxName, pfxBytes); logger.Info($"Retrieved certificate from the CA. The certificate is in {pfxName}"); // We write the PEMs to corresponding files System.IO.File.WriteAllText(cerPath, cer); System.IO.File.WriteAllText(keyPath, key); AuthenticatedPFX authPFX = new AuthenticatedPFX(pfxName, PfxPassword, cerPath, keyPath); return(authPFX); } catch (Exception exp) { logger.Error($"Failed to retrieve certificate from CA: {ProcessCertesException(exp)}"); return(null); } }
public IActionResult Index(string name, string pfxPassword, bool staging = false, string format = "pem", bool chain = true) { if (string.Equals(format, "pfx", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(pfxPassword)) { return(BadRequest("pfxPassword must be specified")); } var acmeCert = _dataContext.GetAcmeCertificate(name, staging); if (acmeCert == null) { return(NotFound("Certificate with that name does not exist")); } if (acmeCert.LatestValidAcmeOrder == null) { return(NotFound("Certificate does not yet exist")); } // Ensure cert matches the one used during authentication var id = User.FindFirst(x => x.Type == ClaimTypes.NameIdentifier)?.Value; if (!string.Equals(acmeCert.AcmeCertificateId.ToString(), id)) { return(StatusCode(403, "Status Code: 403; Forbidden")); } switch (format?.ToLower() ?? "pem") { case "pfx": var certChain = new CertificateChain(acmeCert.LatestValidAcmeOrder.RawDataPem); var key = KeyFactory.FromPem(acmeCert.Key.RawData); var pfxBuilder = certChain.ToPfx(key); var pfx = pfxBuilder.Build(acmeCert.Subject, pfxPassword); return(new ContentResult { Content = Convert.ToBase64String(pfx), ContentType = "text/plain", StatusCode = 200 }); case "pem": default: var content = chain ? acmeCert.LatestValidAcmeOrder.RawDataPem : new CertificateChain(acmeCert.LatestValidAcmeOrder.RawDataPem).Certificate.ToPem(); return(new ContentResult { Content = content, ContentType = "text/plain", StatusCode = 200 }); } }
private async Task <byte[]> CreateCertificate(IOrderContext order) { IKey privateKey = KeyFactory.NewKey(KeyAlgorithm.RS256); CertificateChain certificateChain = await order.Generate(new CsrInfo { CommonName = Hostname }, privateKey); PfxBuilder pfxBuilder = certificateChain.ToPfx(privateKey); byte[] pfx = pfxBuilder.Build(Hostname, string.Empty); return(pfx); }
public IActionResult Index(string name, string pfxPassword, bool staging = false, string format = "pem") { if (string.Equals(format, "pfx", StringComparison.OrdinalIgnoreCase) && string.IsNullOrWhiteSpace(pfxPassword)) { return(BadRequest("pfxPassword must be specified")); } var acmeCert = _dataContext.GetAcmeCertificate(name, staging); if (acmeCert == null) { return(NotFound("Certificate with that name does not exist")); } if (acmeCert.LatestValidAcmeOrder == null) { return(NotFound("Certificate does not yet exist")); } switch (format?.ToLower() ?? "pem") { case "pfx": var certChain = new CertificateChain(acmeCert.LatestValidAcmeOrder.RawDataPem); var key = KeyFactory.FromPem(acmeCert.Key.RawData); var pfxBuilder = certChain.ToPfx(key); var pfx = pfxBuilder.Build(acmeCert.Subject, pfxPassword); return(new ContentResult { Content = Convert.ToBase64String(pfx), ContentType = "text/plain", StatusCode = 200 }); case "pem": default: return(new ContentResult { Content = acmeCert.LatestValidAcmeOrder.RawDataPem, ContentType = "text/plain", StatusCode = 200 }); } }
private string ExportFullCertPFX(string certFriendlyName, IKey csrKey, CertificateChain certificateChain, string pfxFile) { var pxfFolderPath = _settingsFolder + "\\assets\\pfx"; if (!System.IO.Directory.Exists(pxfFolderPath)) { System.IO.Directory.CreateDirectory(pxfFolderPath); } var pfxPath = pxfFolderPath + "\\" + pfxFile; var pfx = certificateChain.ToPfx(csrKey); var pfxBytes = pfx.Build(certFriendlyName, ""); System.IO.File.WriteAllBytes(pfxPath, pfxBytes); return(pfxPath); }
private string ExportFullCertPFX(string certFriendlyName, IKey csrKey, CertificateChain certificateChain, string certId, string primaryDomainPath) { var storePath = Path.GetFullPath(Path.Combine(new string[] { _settingsFolder, "..\\assets", primaryDomainPath })); if (!System.IO.Directory.Exists(storePath)) { System.IO.Directory.CreateDirectory(storePath); } var pfxFile = certId + ".pfx"; var pfxPath = Path.Combine(storePath, pfxFile); var pfx = certificateChain.ToPfx(csrKey); var pfxBytes = pfx.Build(certFriendlyName, ""); System.IO.File.WriteAllBytes(pfxPath, pfxBytes); return(pfxPath); }
/// <summary> /// Retrieves the certificate from the ACME service. This method also generates the key and the CSR. /// </summary> /// <param name="commonName">the CN of the certificate to be requested</param> /// <param name="pathForPfx">Path where the resulting PFX/PKCS#12 file will be generated</param> /// <param name="pfxFriendlyName">Friendly name for the resulting PFX/PKCS#12</param> /// <returns>The name of the generated PFX/PKCS#12 file, or null in case of error</returns> public async Task <string> RetrieveCertificate(IList <string> domains, string pathForPfx, string pfxFriendlyName) { try { if (_orderCtx == null) { throw new Exception("Do not call RetrieveCertificate before RegisterNewOrderAndVerify"); } if (!System.IO.Directory.Exists(pathForPfx)) { throw new Exception("Directory for PFX writing do not exists"); } InitCertes(); // Let's generate a new key (RSA is good enough IMHO) IKey certKey = KeyFactory.NewKey(KeyAlgorithm.RS256); // Then let's generate the CSR var csr = await _orderCtx.CreateCsr(certKey); csr.AddName("CN", domains[0]); csr.SubjectAlternativeNames = domains; // and finalize the ACME order var finalOrder = await _orderCtx.Finalize(csr.Generate()); // Now we can fetch the certificate CertificateChain cert = await _orderCtx.Download(); // We build the PFX/PKCS#12 var pfx = cert.ToPfx(certKey); pfx.AddIssuers(GetCACertChainFromStore()); var pfxBytes = pfx.Build(pfxFriendlyName, PfxPassword); var pfxName = Guid.NewGuid().ToString() + ".pfx"; // We write the PFX/PKCS#12 to file System.IO.File.WriteAllBytes(pathForPfx + "\\" + pfxName, pfxBytes); logger.Info($"Retrieved certificate from the CA. The certificate is in {pfxName}"); return(pfxName); } catch (Exception exp) { logger.Error($"Failed to retrieve certificate from CA: {ProcessCertesException(exp)}"); return(null); } }
/// <summary> /// Once validation has completed for our requested domains we can complete the certificate /// request by submitting a Certificate Signing Request (CSR) to the CA /// </summary> /// <param name="log"> </param> /// <param name="primaryDnsIdentifier"> </param> /// <param name="alternativeDnsIdentifiers"> </param> /// <param name="config"> </param> /// <returns> </returns> public async Task <ProcessStepResult> CompleteCertificateRequest(ILog log, CertRequestConfig config, string orderId) { var orderContext = _currentOrders[orderId]; // generate temp keypair for signing CSR var keyAlg = KeyAlgorithm.RS256; if (!string.IsNullOrEmpty(config.CSRKeyAlg)) { if (config.CSRKeyAlg == "RS256") { keyAlg = KeyAlgorithm.RS256; } if (config.CSRKeyAlg == "ECDSA256") { keyAlg = KeyAlgorithm.ES256; } if (config.CSRKeyAlg == "ECDSA384") { keyAlg = KeyAlgorithm.ES384; } } var csrKey = KeyFactory.NewKey(keyAlg); var certFriendlyName = $"{config.PrimaryDomain} [Certify] "; // generate cert CertificateChain certificateChain = null; try { certificateChain = await orderContext.Generate(new CsrInfo { CommonName = _idnMapping.GetAscii(config.PrimaryDomain) }, csrKey); X509Certificate2 cert = new X509Certificate2(certificateChain.Certificate.ToDer()); certFriendlyName += $"{cert.GetEffectiveDateString()} to {cert.GetExpirationDateString()}"; } catch (AcmeRequestException exp) { var msg = $"Failed to finalize certificate order: {exp.Error?.Detail}"; log.Error(msg); return(new ProcessStepResult { ErrorMessage = msg, IsSuccess = false, Result = exp.Error }); } var certFolderPath = _settingsFolder + "\\assets\\pfx"; if (!System.IO.Directory.Exists(certFolderPath)) { System.IO.Directory.CreateDirectory(certFolderPath); } string certFile = Guid.NewGuid().ToString() + ".pfx"; string pfxPath = certFolderPath + "\\" + certFile; var pfx = certificateChain.ToPfx(csrKey); var pfxBytes = pfx.Build(certFriendlyName, ""); System.IO.File.WriteAllBytes(pfxPath, pfxBytes); return(new ProcessStepResult { IsSuccess = true, Result = pfxPath }); }
private static X509Certificate2 GetHttpsCertificate(ConnectionContext connectionContext, KestrelServerOptions options, string name) { // Bail early if we're connecting locally if (connectionContext.IsLocal()) { return(_localCert); } using (var scope = options.ApplicationServices.CreateScope()) { var logger = scope.ServiceProvider.GetService <ILogger <Program> >(); var httpServerOptions = scope.ServiceProvider.GetService <IOptionsSnapshot <HttpServer> >(); var host = httpServerOptions.Value.SiteHostname; // If setup hasn't completed yet, serve a cert with the hostname being requested if (string.IsNullOrWhiteSpace(host)) { // This server could be on a VPS or cloud (i.e. not locally accessible), // create and serve a temporary, self-signed cert for this hostname. if (_tempCert == null) { _tempCert = GenerateSelfSignedCertificate(name); } logger.LogDebug($"Serve self-signed certificate for {name}"); return(_tempCert); } // A certificate is being requested for some other domain. Ignore it. if (!string.Equals(name, host)) { logger.LogWarning($"Cert requested for {name}, which differs from {host}. Will only attempt to locate certificate for {host}."); return(null); } var dataContext = scope.ServiceProvider.GetService <DataContext>(); var acmeCert = dataContext.GetAcmeCertificate(host); // Build the PFX to be used var order = acmeCert?.LatestValidAcmeOrder; if (order != null) { if (_lastCertId == order.AcmeOrderId) { return(_lastCert); } if (order.RawDataPem != null) { var certChain = new CertificateChain(order.RawDataPem); var key = KeyFactory.FromPem(acmeCert.Key.RawData); var pfxBuilder = certChain.ToPfx(key); var pfx = pfxBuilder.Build(host, string.Empty); _lastCertId = order.AcmeOrderId; _lastCert = new X509Certificate2(pfx, string.Empty); return(_lastCert); } } logger.LogWarning($"No certificate found for {host}."); } return(null); }
/// <summary> /// Once validation has completed for our requested domains we can complete the certificate /// request by submitting a Certificate Signing Request (CSR) to the CA /// </summary> /// <param name="log"> </param> /// <param name="primaryDnsIdentifier"> </param> /// <param name="alternativeDnsIdentifiers"> </param> /// <param name="config"> </param> /// <returns> </returns> public async Task <ProcessStepResult> CompleteCertificateRequest(ILog log, CertRequestConfig config, string orderId) { var orderContext = _currentOrders[orderId]; // check order status, if it's not 'ready' then try a few more times before giving up var order = await orderContext.Resource(); var attempts = 5; while (attempts > 0 && order?.Status != OrderStatus.Ready) { await Task.Delay(2000); order = await orderContext.Resource(); attempts--; } if (order?.Status != OrderStatus.Ready) { return(new ProcessStepResult { IsSuccess = false, ErrorMessage = "Certificate Request did not complete. Order did not reach Ready status in the time allowed.", Result = order }); } // generate temp keypair for signing CSR var keyAlg = KeyAlgorithm.RS256; if (!string.IsNullOrEmpty(config.CSRKeyAlg)) { if (config.CSRKeyAlg == "RS256") { keyAlg = KeyAlgorithm.RS256; } if (config.CSRKeyAlg == "ECDSA256") { keyAlg = KeyAlgorithm.ES256; } if (config.CSRKeyAlg == "ECDSA384") { keyAlg = KeyAlgorithm.ES384; } if (config.CSRKeyAlg == "ECDSA521") { keyAlg = KeyAlgorithm.ES512; } } var csrKey = KeyFactory.NewKey(keyAlg); var certFriendlyName = $"{config.PrimaryDomain} [Certify] "; // generate cert CertificateChain certificateChain = null; try { certificateChain = await orderContext.Generate(new CsrInfo { CommonName = _idnMapping.GetAscii(config.PrimaryDomain) }, csrKey); var cert = new X509Certificate2(certificateChain.Certificate.ToDer()); certFriendlyName += $"{cert.GetEffectiveDateString()} to {cert.GetExpirationDateString()}"; } catch (AcmeRequestException exp) { var msg = $"Failed to finalize certificate order: {exp.Error?.Detail}"; log.Error(msg); return(new ProcessStepResult { ErrorMessage = msg, IsSuccess = false, Result = exp.Error }); } var certFolderPath = _settingsFolder + "\\assets\\pfx"; if (!System.IO.Directory.Exists(certFolderPath)) { System.IO.Directory.CreateDirectory(certFolderPath); } string certFile = Guid.NewGuid().ToString() + ".pfx"; string pfxPath = certFolderPath + "\\" + certFile; var pfx = certificateChain.ToPfx(csrKey); var pfxBytes = pfx.Build(certFriendlyName, ""); System.IO.File.WriteAllBytes(pfxPath, pfxBytes); return(new ProcessStepResult { IsSuccess = true, Result = pfxPath }); }
/// <summary> /// Generate a certificate request /// </summary> /// <param name="certificatename"></param> /// <param name="mainhost">Main host: www.google.com</param> /// <param name="certificatePath">Path to store the generated certificates</param> /// <param name="alternatehosts">Alterante hosts: list of alterante hosts for this certificate</param> /// <returns></returns> public CertificatePaths DownloadCertificate( string certificatename, string mainhost, string certificatePath, List <string> alternatehosts = null) { if (this.OrderContext == null) { throw new Exception("Do not call RetrieveCertificate before RegisterNewOrderAndVerify"); } // Let's generate a new key (RSA is good enough IMHO) IKey certKey = KeyFactory.NewKey(KeyAlgorithm.RS256); // Then let's generate the CSR var csr = this.OrderContext.CreateCsr(certKey).Result; csr.AddName("CN", mainhost); if (alternatehosts != null) { csr.SubjectAlternativeNames = alternatehosts; } // and finalize the ACME order var finalOrder = this.OrderContext.Finalize(csr.Generate()).Result; // Now we can fetch the certificate CertificateChain cert = this.OrderContext.Download().Result; // We build the PFX/PKCS#12 and the cert/key as PEM var pfx = cert.ToPfx(certKey); var cer = cert.ToPem(); var key = certKey.ToPem(); // pfx.AddIssuers(this.GetCACertChainFromStore()); Remote certificate already has information var fileName = Path.Combine(certificatePath, Guid.NewGuid().ToString()); var pfxName = fileName + ".pfx"; var cerPath = fileName + ".cer"; var keyPath = fileName + ".key"; var authenticatedPfx = new AuthenticatedPFX(pfxName, cerPath, keyPath); var pfxBytes = pfx.Build(certificatename, authenticatedPfx.PfxPassword); // We write the PFX/PKCS#12 to file File.WriteAllBytes(pfxName, pfxBytes); this.Logger.LogInfo(true, $"Retrieved certificate from the CA. The certificate is in {pfxName}"); // We write the PEMs to corresponding files File.WriteAllText(cerPath, cer); File.WriteAllText(keyPath, key); return(new CertificatePaths() { chainPemFile = string.Empty, crtDerFile = string.Empty, crtPemFile = cerPath, csrGenFile = string.Empty, csrPemFile = string.Empty, keyGenFile = string.Empty, keyPemFile = keyPath, name = certificatename, pfxPemFile = pfxName }); }
/// <summary> /// HTTP-01 challenge. /// </summary> /// <exception cref="LetsEncryptMikroTikException"/> public async Task <LetsEncryptCert> GetCertAsync(string?accountPemKey = null) { AcmeContext acme; IAccountContext account; //Uri knownServer = _letsEncryptAddress; if (accountPemKey != null) { // Load the saved account key var accountKey = KeyFactory.FromPem(accountPemKey); acme = new AcmeContext(_letsEncryptAddress, accountKey); Log.Information("Авторизация в Let's Encrypt."); account = await acme.Account().ConfigureAwait(false); } else { acme = new AcmeContext(_letsEncryptAddress); Log.Information("Авторизация в Let's Encrypt."); account = await acme.NewAccount(_config.Email, termsOfServiceAgreed : true).ConfigureAwait(false); // Save the account key for later use //string pemKey = acme.AccountKey.ToPem(); } Log.Information("Заказываем новый сертификат."); IOrderContext order = await acme.NewOrder(new[] { _config.DomainName }).ConfigureAwait(false); // Get the token and key authorization string. Log.Information("Получаем способы валидации заказа."); var authz = (await order.Authorizations().ConfigureAwait(false)).First(); if (_config.UseAlpn) { Log.Information("Выбираем TLS-ALPN-01 способ валидации."); IChallengeContext challenge = await authz.TlsAlpn().ConfigureAwait(false); string keyAuthz = challenge.KeyAuthz; string token = challenge.Token; await ChallengeAlpnAsync(challenge, keyAuthz).ConfigureAwait(false); } else { Log.Information("Выбираем HTTP-01 способ валидации."); IChallengeContext challenge = await authz.Http().ConfigureAwait(false); string keyAuthz = challenge.KeyAuthz; await HttpChallengeAsync(challenge, keyAuthz).ConfigureAwait(false); } Log.Information("Загружаем сертификат."); // Download the certificate once validation is done IKey privateKey = KeyFactory.NewKey(KeyAlgorithm.RS256); CertificateChain cert = await order.Generate(new CsrInfo { CommonName = _config.DomainName, CountryName = "CA", State = "Ontario", Locality = "Toronto", Organization = "Certes", OrganizationUnit = "Dev", }, privateKey) .ConfigureAwait(false); // Export full chain certification. string certPem = cert.ToPem(); string keyPem = privateKey.ToPem(); // Export PFX PfxBuilder pfxBuilder = cert.ToPfx(privateKey); byte[] pfx = pfxBuilder.Build(friendlyName: _config.DomainName, password: ""); //await acme.RevokeCertificate(pfx, RevocationReason.Superseded, privateKey); using (var cert2 = new X509Certificate2(pfx)) { return(new LetsEncryptCert(cert2.NotAfter, certPem, keyPem, cert2.GetCommonName(), cert2.GetSha2Thumbprint())); } }