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
                });
            }
        }
Exemple #2
0
        /// <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
                });
            }
        }
Exemple #4
0
        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);
        }
Exemple #8
0
        /// <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);
            }
        }
Exemple #9
0
        /// <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
            });
        }
Exemple #10
0
        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
            });
        }
Exemple #12
0
        /// <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
            });
        }
Exemple #13
0
        /// <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()));
            }
        }