protected async Task <byte[]> GenerateCertificateAsync(CertificateInfo certificateInfo, IOrderContext orderCtx) { //Создаем CSR-builder и добавляем туда все домены и CN var csrBuilder = new CertificationRequestBuilder(); csrBuilder.AddName("CN", certificateInfo.CommonName); foreach (var domain in certificateInfo.Domains) { csrBuilder.SubjectAlternativeNames.Add(domain); } //Отправляем запрос на завершение Order'а (генерацию сертификата) var order = await orderCtx.Finalize(csrBuilder.Generate()); if (order.Status != OrderStatus.Valid) { throw new InvalidOperationException("Order finalization error!"); } //Получаем цепочку сертификатов и возвращаем ее виде PFX-файла var certChain = await orderCtx.Download(); LogNewCertificateInfo(certChain); return(certChain.ToPfx(csrBuilder.Key).Build(certificateInfo.CommonName, certificateInfo.PfxPassword)); }
/// <summary> /// Finalizes and download the certifcate for the order. /// </summary> /// <param name="context">The order context.</param> /// <param name="csr">The CSR.</param> /// <param name="key">The private key for the certificate.</param> /// <param name="retryCount">Number of retries when the Order is in 'processing' state. (default = 1)</param> /// <param name="preferredChain">The preferred Root Certificate.</param> /// <returns> /// The certificate generated. /// </returns> public static async Task <CertificateChain> Generate(this IOrderContext context, CsrInfo csr, IKey key, string preferredChain = null, int retryCount = 1) { var order = await context.Resource(); if (order.Status != OrderStatus.Ready && // draft-11 order.Status != OrderStatus.Pending) // pre draft-11 { throw new AcmeException(string.Format(Strings.ErrorInvalidOrderStatusForFinalize, order.Status)); } order = await context.Finalize(csr, key); while (order.Status == OrderStatus.Processing && retryCount-- > 0) { await Task.Delay(TimeSpan.FromSeconds(Math.Max(context.RetryAfter, 1))); order = await context.Resource(); } if (order.Status != OrderStatus.Valid) { throw new AcmeException(Strings.ErrorFinalizeFailed); } return(await context.Download(preferredChain)); }
/// <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); } }
/// <summary> /// Finalizes and download the certifcate for the order. /// </summary> /// <param name="context">The order context.</param> /// <param name="csr">The CSR.</param> /// <param name="key">The private key for the certificate.</param> /// <returns> /// The certificate generated. /// </returns> public static async Task <CertificateChain> Generate(this IOrderContext context, CsrInfo csr, IKey key) { var order = await context.Resource(); if (order.Status != OrderStatus.Pending) { throw new Exception($"Can not finalize order with status {order.Status}."); } order = await context.Finalize(csr, key); if (order.Status != OrderStatus.Valid) { throw new Exception("Failto finalize order."); } return(await context.Download()); }
/// <summary> /// Finalizes and download the certifcate for the order. /// </summary> /// <param name="context">The order context.</param> /// <param name="csr">The CSR.</param> /// <param name="key">The private key for the certificate.</param> /// <returns> /// The certificate generated. /// </returns> public static async Task <CertificateChain> Generate(this IOrderContext context, CsrInfo csr, IKey key) { var order = await context.Resource(); if (order.Status != OrderStatus.Ready && // draft-11 order.Status != OrderStatus.Pending) // pre draft-11 { throw new AcmeException(string.Format(Strings.ErrorInvalidOrderStatusForFinalize, order.Status)); } order = await context.Finalize(csr, key); if (order.Status != OrderStatus.Valid) { throw new AcmeException(Strings.ErrorFinalizeFailed); } return(await context.Download()); }
/// <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); } }
public async Task <(byte[] pfxBytes, string password)> BuildCertificateAsync( IOrderContext order, CertificateRenewalOptions cfg, CancellationToken cancellationToken) { var key = KeyFactory.NewKey(KeyAlgorithm.RS256); await order.Finalize(new CsrInfo(), key); var certChain = await order.Download(); var builder = certChain.ToPfx(key); builder.FullChain = true; var bytes = new byte[32]; _randomGenerator.GetNonZeroBytes(bytes); var password = Convert.ToBase64String(bytes); var pfxBytes = builder.Build(string.Join(";", cfg.HostNames), password); return(pfxBytes, password); }