Esempio n. 1
0
        public async Task IssueWildCardCertificate()
        {
            var restApi = new AcmeRestApi(ProtoacmeContants.LETSENCRYPT_STAGING_ENDPOINT);
            var client  = new ProtoacmeClient(restApi);

            //1. Load up the account and challenge data.
            AcmeAccount account = AcmeAccount.FromFile(@"c:\temp\account.dat");
            AcmeCertificateFulfillmentPromise promise = AcmeCertificateFulfillmentPromise.FromFile(@"c:\temp\promise.dat");
            ChallengeCollection challenges            = ChallengeCollection.FromFile <DnsChallenge>(@"c:\temp\challenge.dat");

            //2. Tell Lets Encrypt to verify our challenge.
            var startVerifyResult = await client.Challenge.ExecuteChallengeVerification(challenges[0]);

            AcmeChallengeStatus challengeStatus = null;

            while (challengeStatus == null || challengeStatus.Status == "pending")
            {
                challengeStatus = await client.Challenge.GetChallengeVerificationStatus(challenges[0]);

                await Task.Delay(3000);
            }
            if (challengeStatus.Status != "valid")
            {
                throw new Exception($"Failed to validate challenge token");
            }

            //3. Create the CSR
            CSR csr = CertificateUtility.GenerateCsr(wildCardDns);

            SaveCRTPrivateKey(csr);

            //4. Download the certificate
            var cert = await client.Certificate.DownloadCertificateAsync(account, promise, csr, CertificateType.Cert);

            //5. Save the certificate
            using (FileStream fs = new FileStream(@"c:\temp\mycert.cer", FileMode.Create))
            {
                byte[] buffer = cert.Array;
                fs.Write(buffer, 0, buffer.Length);
            }
        }
Esempio n. 2
0
        private void LoadAccountAndChallengeData <TChallengeType>(out AcmeAccount account, out List <TChallengeType> challenges, out AcmeCertificateFulfillmentPromise promise)
            where TChallengeType : IAcmeChallengeContent
        {
            string baseFolder = @"c:\temp";

            using (FileStream fs = new FileStream(Path.Combine(baseFolder, "myaccount.acc"), FileMode.Open))
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    var sAccount = sr.ReadToEnd();
                    var bAccount = Convert.FromBase64String(sAccount);
                    sAccount = Encoding.UTF8.GetString(bAccount);
                    account  = JsonConvert.DeserializeObject <AcmeAccount>(sAccount);
                }
            }

            using (FileStream fs = new FileStream(Path.Combine(baseFolder, "challenges.dat"), FileMode.Open))
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    var sChallenges = sr.ReadToEnd();
                    var bChallenges = Convert.FromBase64String(sChallenges);
                    sChallenges = Encoding.UTF8.GetString(bChallenges);
                    challenges  = JsonConvert.DeserializeObject <List <TChallengeType> >(sChallenges);
                }
            }

            using (FileStream fs = new FileStream(Path.Combine(baseFolder, "challengepromise.dat"), FileMode.Open))
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    var sPromise = sr.ReadToEnd();
                    var bPromise = Convert.FromBase64String(sPromise);
                    sPromise = Encoding.UTF8.GetString(bPromise);
                    promise  = JsonConvert.DeserializeObject <AcmeCertificateFulfillmentPromise>(sPromise);
                }
            }
        }
Esempio n. 3
0
        private void SaveAccountAndChallengeData(AcmeAccount account, IEnumerable <IAcmeChallengeContent> challenges, AcmeCertificateFulfillmentPromise promise)
        {
            string baseFolder = @"c:\temp";

            var serializedAccount = JsonConvert.SerializeObject(account, Formatting.None);

            serializedAccount = Convert.ToBase64String(Encoding.UTF8.GetBytes(serializedAccount));
            using (FileStream fs = new FileStream(Path.Combine(baseFolder, "myaccount.acc"), FileMode.Create))
            {
                byte[] buffer = Encoding.UTF8.GetBytes(serializedAccount);
                fs.Write(buffer, 0, buffer.Length);
            }

            var sChallenges = JsonConvert.SerializeObject(challenges, Formatting.None);

            sChallenges = Convert.ToBase64String(Encoding.UTF8.GetBytes(sChallenges));
            using (FileStream fs = new FileStream(Path.Combine(baseFolder, "challenges.dat"), FileMode.Create))
            {
                byte[] buffer = Encoding.UTF8.GetBytes(sChallenges);
                fs.Write(buffer, 0, buffer.Length);
            }

            var sCertificatePromise = JsonConvert.SerializeObject(promise);

            sCertificatePromise = Convert.ToBase64String(Encoding.UTF8.GetBytes(sCertificatePromise));
            using (FileStream fs = new FileStream(Path.Combine(baseFolder, "challengepromise.dat"), FileMode.Create))
            {
                byte[] buffer = Encoding.UTF8.GetBytes(sCertificatePromise);
                fs.Write(buffer, 0, buffer.Length);
            }

            foreach (var challenge in challenges)
            {
                challenge.SaveToFile(Path.Combine(baseFolder, challenge.Token));
            }

            //Create Account Private Key
            using (TextWriter writer = new StreamWriter(Path.Combine(baseFolder, "account.key")))
            {
                CertificateUtility.ExportRSAPrivateKey(account.SecurityInfo, writer);
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Gets challenges used to verify domain ownership.
        /// </summary>
        /// <param name="account">Existing account.</param>
        /// <param name="acmeCertificateFulfillmentPromise">The certificate fulfillment promise retrieved from the RequestCertificate call.</param>
        /// <param name="challengeType">The challenge type expected back.</param>
        /// <returns>Challenge used to verify domain ownership</returns>
        /// <remarks>If requesting a challenge for a wildcard domain, only dns challenge is supported.</remarks>
        /// <exception cref="NotSupportedException">If the challenge type is not supported.</exception>
        /// <exception cref="AcmeProtocolException">On all other Acme related exceptions</exception>
        public async Task <ChallengeCollection> GetChallengesAsync(AcmeAccount account, AcmeCertificateFulfillmentPromise acmeCertificateFulfillmentPromise, ChallengeType challengeType)
        {
            var response = await _acmeApi.GetChallengesAsync(acmeCertificateFulfillmentPromise);

            var errorResponse = response.Where(t => t.Status == AcmeApiResponseStatus.Error);

            if (errorResponse.Any())
            {
                throw new AcmeProtocolException(string.Join(" | ", errorResponse.Select(t => t.Message)));
            }

            ChallengeCollection challenges = new ChallengeCollection();

            foreach (var resp in response)
            {
                AcmeChallenge sChallenge = resp.Data.Challenges.FirstOrDefault(t => t.Type.Equals(challengeType.Value));
                if (sChallenge == null)
                {
                    throw new NotSupportedException($"{challengeType.Value} challenge type not supported in this context.");
                }
                IAcmeChallengeContent challengeContent = null;
                switch (challengeType.Value)
                {
                case ProtoacmeContants.CHALLENGE_HTTP:
                    challengeContent = new HttpChallenge(account, sChallenge, resp.Data.Identifier?.Value);
                    challenges.Add(challengeContent);
                    break;

                case ProtoacmeContants.CHALLENGE_DNS:
                    challengeContent = new DnsChallenge(account, sChallenge, resp.Data.Identifier?.Value);
                    challenges.Add(challengeContent);
                    break;

                case ProtoacmeContants.CHALLENGE_TLS:
                    challengeContent = new TlsChallenge(account, sChallenge, resp.Data.Identifier?.Value);
                    challenges.Add(challengeContent);
                    break;

                default:
                    break;
                }
            }

            return(challenges);
        }
Esempio n. 5
0
        /// <summary>
        /// Downloads the SSL Certificate.
        /// </summary>
        /// <param name="completedPromise">The completed certificate fulfillment promise retreived from the FinalizeChallenge call.</param>
        /// <param name="certificateType">The type of certificate you are requesting.</param>
        /// <returns>The certificate.</returns>
        public async Task <AcmeApiResponse <ArraySegment <byte> > > GetCertificateAsync(AcmeCertificateFulfillmentPromise completedPromise, CertificateType certificateType)
        {
            if (completedPromise == null)
            {
                throw new ArgumentNullException("completedPromise");
            }
            if (string.IsNullOrEmpty(completedPromise.Certificate))
            {
                throw new ArgumentException("Certificate url is not valid");
            }
            if (certificateType == null)
            {
                throw new ArgumentNullException("certificateType");
            }

            HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, completedPromise.Certificate);

            message.Headers.Accept.ParseAdd($"application/{certificateType.Value}");
            var apiResp = await _httpClient.SendAsync(message);

            string apiRespString = await apiResp.Content?.ReadAsStringAsync();

            if (apiResp.StatusCode != HttpStatusCode.OK)
            {
                return(ErrorResponse <ArraySegment <byte> >(apiRespString));
            }

            byte[] bContent = await apiResp.Content.ReadAsByteArrayAsync();

            return(new AcmeApiResponse <ArraySegment <byte> >()
            {
                Status = AcmeApiResponseStatus.Success,
                Data = new ArraySegment <byte>(bContent)
            });
        }
Esempio n. 6
0
        /// <summary>
        /// Finalize certificate request. This allows the certificate to be downloaded by using the GetCertificate request.
        /// </summary>
        /// <param name="account">Must be existing account.</param>
        /// <param name="nonce">Nonce</param>
        /// <param name="acmeCertificateFulfillmentPromise">The original Certificate Fulfillment Promise used in the RequestCertificate request.</param>
        /// <param name="csr">The certificate CSR. This can be generated using helpers through this api or by an external source such as IIS.</param>
        /// <returns>A completed Certificate Fulfillment Promise used to Download the certificate using the GetCertificate call. Wrapped by a response object.</returns>
        public async Task <AcmeApiResponse <AcmeCertificateFulfillmentPromise> > FinalizeCertificatePromiseAsync(AcmeAccount account, string nonce, AcmeCertificateFulfillmentPromise acmeCertificateFulfillmentPromise, string csr)
        {
            if (string.IsNullOrEmpty(nonce))
            {
                throw new ArgumentNullException("nonce");
            }
            if (account == null)
            {
                throw new ArgumentNullException("account");
            }
            if (acmeCertificateFulfillmentPromise == null)
            {
                throw new ArgumentNullException("acmeCertificateFulfillmentPromise");
            }
            if (string.IsNullOrEmpty(csr))
            {
                throw new ArgumentNullException("csr");
            }

            JwsContainer <CSR> jwsObject = new JwsContainer <CSR>(account.SecurityInfo, nonce, acmeCertificateFulfillmentPromise.Finalize, account.KID, new CSR()
            {
                csr = csr
            });

            string jwsToken = jwsObject.SerializeSignedToken();

            var apiResp = await SendPostData(
                url : acmeCertificateFulfillmentPromise.Finalize,
                data : jwsToken);

            string apiRespString = await apiResp.Content?.ReadAsStringAsync();

            if (apiResp.StatusCode != HttpStatusCode.OK)
            {
                return(ErrorResponse <AcmeCertificateFulfillmentPromise>(apiRespString));
            }

            if (!apiResp.Headers.TryGetValues(ProtoacmeContants.HEADER_NONCE, out IEnumerable <string> nonces))
            {
                return(ErrorResponse <AcmeCertificateFulfillmentPromise>("Missing Replay-Nonce Header on FinalizeChallenge Response."));
            }

            return(new AcmeApiResponse <AcmeCertificateFulfillmentPromise>()
            {
                Status = AcmeApiResponseStatus.Success,
                Nonce = nonces.FirstOrDefault(),
                Data = JsonConvert.DeserializeObject <AcmeCertificateFulfillmentPromise>(apiRespString)
            });
        }
Esempio n. 7
0
        /// <summary>
        /// Gets challenges used to verify domain ownership.
        /// </summary>
        /// <param name="acmeCertificateFulfillmentPromise">The certificate fulfillment promise retrieved from the RequestCertificate call.</param>
        /// <returns>An authorization object containing the available challenge types. Wrapped by a response object.</returns>
        public async Task <List <AcmeApiResponse <AcmeAuthorization> > > GetChallengesAsync(AcmeCertificateFulfillmentPromise acmeCertificateFulfillmentPromise)
        {
            List <AcmeApiResponse <AcmeAuthorization> > response = new List <AcmeApiResponse <AcmeAuthorization> >();

            if (acmeCertificateFulfillmentPromise == null)
            {
                throw new ArgumentNullException("acmeCertificateFulfillmentPromise");
            }
            if (acmeCertificateFulfillmentPromise.Authorizations == null || !acmeCertificateFulfillmentPromise.Authorizations.Any())
            {
                throw new ArgumentException("No Authorizations exist in the Acme Certification Fulfillment Promise");
            }

            foreach (string authUrl in acmeCertificateFulfillmentPromise.Authorizations)
            {
                AcmeApiResponse <AcmeAuthorization> result = new AcmeApiResponse <AcmeAuthorization>();

                var apiResp = await _httpClient.GetAsync(authUrl);

                string apiRespString = await apiResp.Content?.ReadAsStringAsync();

                if (!apiResp.IsSuccessStatusCode)
                {
                    result.Status  = AcmeApiResponseStatus.Error;
                    result.Message = apiRespString;
                }
                else
                {
                    result.Status = AcmeApiResponseStatus.Success;
                    result.Data   = JsonConvert.DeserializeObject <AcmeAuthorization>(apiRespString);
                }
                response.Add(result);
            }

            return(response);
        }
        /// <summary>
        /// Downloads the SSL Certificate.
        /// </summary>
        /// <param name="account">Existing account</param>
        /// <param name="completedPromise">The completed certificate fulfillment promise</param>
        /// <param name="csr">Certificate request</param>
        /// <param name="certificateType">The type of certificate.</param>
        /// <returns>The certificate.</returns>
        public async Task <ArraySegment <byte> > DownloadCertificateAsync(AcmeAccount account, AcmeCertificateFulfillmentPromise completedPromise, Protoacme.Utility.Certificates.CSR csr, CertificateType certificateType)
        {
            var directory = await _directoryCache.GetAsync();

            var nonce = await _nonceCache.GetAsync();

            var finalizeResponse = await _acmeApi.FinalizeCertificatePromiseAsync(account, nonce, completedPromise, csr.Base64UrlEncoded);

            if (finalizeResponse.Status == AcmeApiResponseStatus.Error)
            {
                throw new AcmeProtocolException(finalizeResponse.Message);
            }

            var response = await _acmeApi.GetCertificateAsync(finalizeResponse.Data, certificateType);

            if (response.Status == AcmeApiResponseStatus.Error)
            {
                throw new AcmeProtocolException(response.Message);
            }

            _nonceCache.Update(finalizeResponse.Nonce);

            return(response.Data);
        }