Exemple #1
0
        public async Task <IActionResult> OnGetAsync(long?id)
        {
            if (id == null)
            {
                return(NotFound());
            }

            AcmeCertificate = await _context.AcmeCertificates
                              .Include(a => a.AcmeAccount)
                              .Include(a => a.Key)
                              .FirstOrDefaultAsync(m => m.AcmeCertificateId == id);

            if (AcmeCertificate == null)
            {
                return(NotFound());
            }

            ViewData["AcmeAccountId"] = new SelectList(
                _context.AcmeAccounts.Select(x => new
            {
                Id   = x.AcmeAccountId,
                Name = x.AcmeContactEmail + (x.IsAcmeStaging ? " (staging)" : string.Empty)
            })
                , "Id", "Name");

            return(Page());
        }
Exemple #2
0
        /// <summary>
        /// Creates a new certificate.
        /// </summary>
        /// <param name="csrBytes">The certificate signing request data.</param>
        /// <returns>The certificate issued.</returns>
        public async Task <AcmeCertificate> NewCertificate(byte[] csrBytes)
        {
            var payload = new Certificate
            {
                Csr      = JwsConvert.ToBase64String(csrBytes),
                Resource = ResourceTypes.NewCertificate
            };

            var uri = await this.handler.GetResourceUri(ResourceTypes.NewCertificate);

            var result = await this.handler.Post(uri, payload, key);

            ThrowIfError(result);

            byte[] pem;
            using (var buffer = new MemoryStream())
            {
                pem = buffer.ToArray();
            }

            var cert = new AcmeCertificate
            {
                Raw         = result.Raw,
                Links       = result.Links,
                Location    = result.Location,
                ContentType = result.ContentType
            };

            var currentCert = cert;

            while (true)
            {
                var upLink = currentCert.Links?.Where(l => l.Rel == "up").FirstOrDefault();
                if (upLink == null)
                {
                    break;
                }
                else
                {
                    var issuerResult = await this.handler.Get <AcmeCertificate>(upLink.Uri);

                    currentCert.Issuer = new AcmeCertificate
                    {
                        Raw         = issuerResult.Raw,
                        Links       = issuerResult.Links,
                        Location    = issuerResult.Location,
                        ContentType = issuerResult.ContentType
                    };

                    currentCert = currentCert.Issuer;
                }
            }

            return(cert);
        }
Exemple #3
0
        public static TrackedCertificate FromAcmeCertificate(AcmeCertificate cert)
        {
            if (cert.LatestValidAcmeOrder?.DomainCertificate == null)
            {
                return(null);
            }
            var trackedCert = FromDomainCertificate(cert.LatestValidAcmeOrder.DomainCertificate);

            trackedCert.Id           = cert.AcmeCertificateId;
            trackedCert.Source       = CertificateSource.AcmeCertificate;
            trackedCert.AcmeCertType = cert.AcmeAccount.IsAcmeStaging
                ? AcmeCertType.Staging
                : AcmeCertType.Production;

            return(trackedCert);
        }
Exemple #4
0
        /// <summary>
        /// Get ACME certificate including metadata
        /// </summary>
        /// <param name="order"></param>
        /// <param name="cancel"></param>
        /// <returns></returns>
        public async Task <AcmeCertificate> GetOrderCertificateExAsync(
            OrderDetails order,
            CancellationToken cancel = default)
        {
            using (var resp = await GetAsync(order.Payload.Certificate, cancel))
            {
                var ret = new AcmeCertificate();
                if (resp.Headers.TryGetValues("Link", out var linkValues))
                {
                    ret.Links = new HTTP.LinkCollection(linkValues);
                }
                ret.Certificate = await resp.Content.ReadAsByteArrayAsync();

                return(ret);
            }
        }
Exemple #5
0
        /// <summary>
        /// Revokes the certificate.
        /// </summary>
        /// <param name="certificate">The certificate.</param>
        /// <returns>The certificate revoked.</returns>
        public async Task <AcmeCertificate> RevokeCertificate(AcmeCertificate certificate)
        {
            var payload = new RevokeCertificate
            {
                Certificate = JwsConvert.ToBase64String(certificate.Raw),
                Resource    = ResourceTypes.RevokeCertificate
            };

            var uri = await this.handler.GetResourceUri(ResourceTypes.RevokeCertificate);

            var result = await this.handler.Post(uri, payload, key);

            ThrowIfError(result);

            certificate.Revoked = true;
            return(certificate);
        }
Exemple #6
0
        public async Task <IActionResult> OnGet(long?id)
        {
            if (id == null)
            {
                return(NotFound());
            }

            AcmeCertificate = await _context.AcmeCertificates
                              .Include(a => a.AcmeOrders)
                              .ThenInclude(o => o.DomainCertificate)
                              .FirstOrDefaultAsync(m => m.AcmeCertificateId == id);

            if (AcmeCertificate == null)
            {
                return(NotFound());
            }

            return(Page());
        }
Exemple #7
0
        public async Task <IActionResult> OnPostAsync(long id, string action, string key)
        {
            AcmeCertificate = await _context.AcmeCertificates
                              .Include(a => a.AcmeAccount)
                              .ThenInclude(a => a.Key)
                              .Include(a => a.AcmeOrders)
                              .ThenInclude(o => o.DomainCertificate)
                              .FirstOrDefaultAsync(m => m.AcmeCertificateId == id);

            if (AcmeCertificate == null)
            {
                return(NotFound());
            }

            switch (action.ToLower())
            {
            case "keychange":
                switch (key)
                {
                case "apikey1":
                    AcmeCertificate.ApiKey1 = ApiKeyGenerator.CreateApiKey();
                    break;

                case "apikey2":
                    AcmeCertificate.ApiKey2 = ApiKeyGenerator.CreateApiKey();
                    break;
                }

                await _context.SaveChangesAsync();

                break;

            case "ocspcheck":
                try
                {
                    var order = AcmeCertificate.GetLatestValidAcmeOrder();
                    if (order?.Certificate != null)
                    {
                        var client = new OcspClient();
                        var status = client.GetOcspStatus(order.Certificate);
                        OcspStatus = status.ToString();
                    }
                    else
                    {
                        OcspStatus = "No certificate";
                    }
                }
                catch (Exception e)
                {
                    _logger.LogWarning($"Error obtaining OCSP status:{e.Message}");
                    OcspStatus = "Error";
                }
                break;

            case "revoke":
            {
                var order = AcmeCertificate.GetLatestValidAcmeOrder();
                if (order?.RawDataPem != null)
                {
                    _certesAcmeProvider.Initialize(AcmeCertificate);

                    var cert   = new Certes.Acme.CertificateChain(order.RawDataPem);
                    var reason = (RevocationReason)Enum.Parse(typeof(RevocationReason), RevocationReason, true);
                    await _certesAcmeProvider.Revoke(cert.Certificate.ToDer(), reason);

                    StatusMessage = "Certificate revocation submitted";
                }
                break;
            }
            }

            return(Page());
        }
Exemple #8
0
        /// <summary>
        /// Gets a certificate in pfx format for the specified domain names.  If there is an error, an exception is thrown.
        /// </summary>
        /// <param name="emailAddress">The email address to use for authorization.</param>
        /// <param name="domains">The domain names to authorize.  If any domain fails authoriation, an exception is thrown and the certificate request is not completed.</param>
        /// <param name="pfx_password">The password to use for the pfx (certificate) file.  You will need to use this to open the pfx file later.</param>
        /// <param name="prepareForChallenge">This is called by the GetCertificate method once for each domain, specifying the challenge that will be sent and the expected response.  It is the caller's responsibility to ensure that when the challenge is received, the expected response is sent.</param>
        public static async Task <byte[]> GetCertificate(string emailAddress, string[] domains, string pfx_password, Action <CertificateChallenge> prepareForChallenge, Action <string> statusUpdate)
        {
            // Remove duplicates from domains array, preserving order.
            HashSet <string> domainSet  = new HashSet <string>();
            List <string>    domainList = new List <string>();

            foreach (string domain in domains)
            {
                if (domainSet.Contains(domain))
                {
                    continue;
                }
                else
                {
                    domainSet.Add(domain);
                    domainList.Add(domain);
                }
            }
            domains = domainList.ToArray();
            if (domains.Length == 0)
            {
                throw new ArgumentException("No domains specified", "domains");
            }

            statusUpdate("Starting certificate renewal for domains \"" + string.Join("\", \"", domains));

            using (AcmeClient client = new AcmeClient(WellKnownServers.LetsEncrypt))
            {
                // Create new registration
                AcmeAccount account = await client.NewRegistraton("mailto:" + emailAddress);

                // Accept terms of services
                account.Data.Agreement = account.GetTermsOfServiceUri();
                account = await client.UpdateRegistration(account);


                foreach (string domain in domains)
                {
                    statusUpdate("Authorizing domain " + domain);
                    // Initialize authorization
                    AuthorizationIdentifier authorizationIdentifier = new AuthorizationIdentifier();
                    authorizationIdentifier.Type  = AuthorizationIdentifierTypes.Dns;
                    authorizationIdentifier.Value = domain;
                    AcmeResult <Authorization> authz = await client.NewAuthorization(authorizationIdentifier);

                    // Compute key authorization for http-01
                    Challenge httpChallengeInfo = authz.Data.Challenges.Where(c => c.Type == ChallengeTypes.Http01).First();
                    string    keyAuthString     = client.ComputeKeyAuthorization(httpChallengeInfo);

                    // Do something to fullfill the challenge,
                    // e.g. upload key auth string to well known path, or make changes to DNS
                    prepareForChallenge(new CertificateChallenge(domain, httpChallengeInfo.Token, keyAuthString));

                    // Invite ACME server to validate the identifier
                    AcmeResult <Challenge> httpChallenge = await client.CompleteChallenge(httpChallengeInfo);

                    // Check authorization status
                    authz = await client.GetAuthorization(httpChallenge.Location);

                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    while (authz.Data.Status == EntityStatus.Pending)
                    {
                        if (sw.Elapsed > TimeSpan.FromMinutes(5))
                        {
                            throw new Exception("Timed out waiting for domain \"" + domain + "\" authorization");
                        }
                        // Wait for ACME server to validate the identifier
                        await Task.Delay(10000);

                        authz = await client.GetAuthorization(httpChallenge.Location);
                    }

                    if (authz.Data.Status != EntityStatus.Valid)
                    {
                        throw new Exception("Failed to authorize domain \"" + domain + "\"");
                    }
                }
                statusUpdate("Authorization complete. Creating certificate.");

                // Create certificate
                CertificationRequestBuilder csr = new CertificationRequestBuilder();
                for (int i = 0; i < domains.Length; i++)
                {
                    if (i == 0)
                    {
                        csr.AddName("CN", domains[i]);
                    }
                    else
                    {
                        csr.SubjectAlternativeNames.Add(domains[i]);
                    }
                }

                AcmeCertificate cert = await client.NewCertificate(csr);

                // Export Pfx
                PfxBuilder pfxBuilder = cert.ToPfx();
                byte[]     pfx        = pfxBuilder.Build("LetsEncryptAuto", pfx_password);
                return(pfx);
            }
        }