public async Task ACME_Test_14_AcknowledgeDnsChallenges() { AcmeOrder Order = await this.OrderCertificate("example.com", "www.example.com"); AcmeAuthorization[] Authorizations = await Order.GetAuthorizations(); foreach (AcmeAuthorization Authorization in Authorizations) { foreach (AcmeChallenge Challenge in Authorization.Challenges) { if (Challenge is AcmeDnsChallenge DnsChallenge) { AcmeChallenge Challenge2 = await Challenge.AcknowledgeChallenge(); this.Print(Authorization.Value, Challenge2); } } } System.Threading.Thread.Sleep(5000); foreach (AcmeAuthorization Authorization in Authorizations) { AcmeAuthorization Authorization2 = await Authorization.Poll(); this.Print(Authorization2); } }
public DnsChallenge(AcmeAccount account, AcmeChallenge challenge, string identifier) { Account = account; Challenge = challenge; Identifier = identifier; Token = challenge.Token; AuthorizationKey = CertificateUtility.CreateAuthorizationKey(account, challenge.Token); }
public async Task VerifyChallenge_ShouldVerifyTheChallenge() { //SETUP AcmeRestApi api = new AcmeRestApi(ProtoacmeContants.LETSENCRYPT_STAGING_ENDPOINT); AcmeApiResponse <AcmeDirectory> directory; AcmeApiResponse nonceResponse = null; AcmeApiResponse <AcmeAccount> accountResponse = null; AcmeApiResponse <AcmeCertificateFulfillmentPromise> certificateFulfillmentPromise = null; List <AcmeApiResponse <AcmeAuthorization> > authorizations = null; AcmeApiResponse <AcmeChallengeStatus> challengeStatusResponse; AcmeCertificateRequest certifcateRequest = new AcmeCertificateRequest() { Identifiers = new List <DnsCertificateIdentifier>() { new DnsCertificateIdentifier() { Value = "taco.com" }, new DnsCertificateIdentifier() { Value = "www.taco.com" } } }; //EXECUTE directory = await api.GetDirectoryAsync(); nonceResponse = await api.GetNonceAsync(directory.Data); accountResponse = await api.CreateAccountAsync(directory.Data, nonceResponse.Nonce, new AcmeCreateAccount() { Contact = new List <string>() { "mailto:[email protected]" }, TermsOfServiceAgreed = true }); certificateFulfillmentPromise = await api.RequestCertificateAsync(directory.Data, accountResponse.Nonce, accountResponse.Data, certifcateRequest); authorizations = await api.GetChallengesAsync(certificateFulfillmentPromise.Data); AcmeChallenge httpChallenge = authorizations.First().Data.Challenges.First(t => t.Type.Equals("http-01")); string authKey = CreateAuthorizationKey(accountResponse.Data, httpChallenge.Token); challengeStatusResponse = await api.VerifyChallengeAsync(accountResponse.Data, httpChallenge, certificateFulfillmentPromise.Nonce, authKey); //ASSERT challengeStatusResponse.ShouldNotBeNull(); challengeStatusResponse.Status.ShouldBe(AcmeApiResponseStatus.Success); challengeStatusResponse.Data.ShouldNotBeNull(); challengeStatusResponse.Data.Status.ShouldBe("pending"); }
/// <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); }
/// <summary> /// Gets the status of the challenge verification. /// </summary> /// <param name="challenge">Single challenge from the AcmeAuthorization</param> /// <returns>Returns the status of the challenge. Wrapped by a response object.</returns> public async Task <AcmeApiResponse <AcmeChallengeVerificationStatus> > GetChallengeVerificationStatusAsync(AcmeChallenge challenge) { if (challenge == null) { throw new ArgumentNullException("challenge"); } var apiResp = await _httpClient.GetAsync(challenge.Url); string apiRespString = await apiResp.Content?.ReadAsStringAsync(); if (apiResp.StatusCode != HttpStatusCode.OK) { return(ErrorResponse <AcmeChallengeVerificationStatus>(apiRespString)); } return(new AcmeApiResponse <AcmeChallengeVerificationStatus>() { Status = AcmeApiResponseStatus.Success, Data = JsonConvert.DeserializeObject <AcmeChallengeVerificationStatus>(apiRespString) }); }
/// <summary> /// Start the challenge verification process. /// </summary> /// <param name="account">Must be existing account.</param> /// <param name="challenge">Single challenge from the AcmeAuthorization</param> /// <param name="nonce">Nonce</param> /// <param name="keyAuthorization">Authorization that identifies the domain and user.</param> /// <returns>The status of the challenge authorization. Wrapped by a response object.</returns> /// <remarks>This will need to be called on each domain that is used in the RequestCertificate call. You should not call this until the challenges are ready to verify. See https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.5 for more information.</remarks> public async Task <AcmeApiResponse <AcmeChallengeStatus> > VerifyChallengeAsync(AcmeAccount account, AcmeChallenge challenge, string nonce, string keyAuthorization) { if (string.IsNullOrEmpty(nonce)) { throw new ArgumentNullException("nonce"); } if (account == null) { throw new ArgumentNullException("account"); } if (challenge == null) { throw new ArgumentNullException("challenge"); } if (string.IsNullOrEmpty(keyAuthorization)) { throw new ArgumentNullException("keyAuthorization"); } JwsContainer <KEYAUTH> jwsObject = new JwsContainer <KEYAUTH>(account.SecurityInfo, nonce, challenge.Url, account.KID, new KEYAUTH() { keyAuthorization = keyAuthorization }); string jwsToken = jwsObject.SerializeSignedToken(); var apiResp = await SendPostData( url : challenge.Url, data : jwsToken); string apiRespString = await apiResp.Content?.ReadAsStringAsync(); if (apiResp.StatusCode != HttpStatusCode.OK) { return(ErrorResponse <AcmeChallengeStatus>(apiRespString)); } if (!apiResp.Headers.TryGetValues(ProtoacmeContants.HEADER_NONCE, out IEnumerable <string> nonces)) { return(ErrorResponse <AcmeChallengeStatus>("Missing Replay-Nonce Header on CompleteChallenge Response.")); } return(new AcmeApiResponse <AcmeChallengeStatus>() { Status = AcmeApiResponseStatus.Success, Nonce = nonces.FirstOrDefault(), Data = JsonConvert.DeserializeObject <AcmeChallengeStatus>(apiRespString) }); }
public async Task DownloadCertificate_ShouldComplete() { //SETUP AcmeRestApi api = new AcmeRestApi(ProtoacmeContants.LETSENCRYPT_STAGING_ENDPOINT); AcmeApiResponse <AcmeDirectory> directory; AcmeApiResponse nonceResponse = null; AcmeApiResponse <AcmeAccount> accountResponse = null; AcmeApiResponse <AcmeCertificateFulfillmentPromise> certificateFulfillmentPromise = null; List <AcmeApiResponse <AcmeAuthorization> > authorizations = null; AcmeApiResponse <AcmeChallengeStatus> challengeStatusResponse = null; AcmeApiResponse <AcmeChallengeVerificationStatus> challengeVerificationResponse = null; AcmeApiResponse <AcmeCertificateFulfillmentPromise> certificatePromiseResult = null; AcmeApiResponse <ArraySegment <byte> > certificateResult = null; AcmeCertificateRequest certifcateRequest = new AcmeCertificateRequest() { Identifiers = new List <DnsCertificateIdentifier>() { new DnsCertificateIdentifier() { Value = "test.com" } } }; //EXECUTE directory = await api.GetDirectoryAsync(); nonceResponse = await api.GetNonceAsync(directory.Data); accountResponse = await api.CreateAccountAsync(directory.Data, nonceResponse.Nonce, new AcmeCreateAccount() { Contact = new List <string>() { "mailto:[email protected]" }, TermsOfServiceAgreed = true }); certificateFulfillmentPromise = await api.RequestCertificateAsync(directory.Data, accountResponse.Nonce, accountResponse.Data, certifcateRequest); authorizations = await api.GetChallengesAsync(certificateFulfillmentPromise.Data); AcmeChallenge httpChallenge = authorizations.First().Data.Challenges.First(t => t.Type.Equals("http-01")); string authKey = CreateAuthorizationKey(accountResponse.Data, httpChallenge.Token); challengeStatusResponse = await api.VerifyChallengeAsync(accountResponse.Data, httpChallenge, certificateFulfillmentPromise.Nonce, authKey); while ( challengeVerificationResponse == null || challengeVerificationResponse.Data.Status == "pending") { challengeVerificationResponse = await api.GetChallengeVerificationStatusAsync(httpChallenge); await Task.Delay(3000); } string csr = GenerateCSR(accountResponse.Data, "test.com"); certificatePromiseResult = await api.FinalizeCertificatePromiseAsync(accountResponse.Data, challengeStatusResponse.Nonce, certificateFulfillmentPromise.Data, csr); certificateResult = await api.GetCertificateAsync(certificatePromiseResult.Data, CertificateType.Cert); //We will write the cert out to a temp directory if it exists. Otherwise, forget it. if (Directory.Exists(@"c:\temp")) { using (FileStream fs = new FileStream(@"c:\temp\mycert.cer", FileMode.Create)) { byte[] bytes = certificateResult.Data.Array; fs.Write(bytes, 0, bytes.Length); } } //ASSERT (Cant really assert anything here. This call will mostlikey fail. There is no way to validate the domain here) }
public async Task FinalizeChallenge_ShouldComplete() { //SETUP AcmeRestApi api = new AcmeRestApi(ProtoacmeContants.LETSENCRYPT_STAGING_ENDPOINT); AcmeApiResponse <AcmeDirectory> directory; AcmeApiResponse nonceResponse = null; AcmeApiResponse <AcmeAccount> accountResponse = null; AcmeApiResponse <AcmeCertificateFulfillmentPromise> certificateFulfillmentPromise = null; List <AcmeApiResponse <AcmeAuthorization> > authorizations = null; AcmeApiResponse <AcmeChallengeStatus> challengeStatusResponse = null; AcmeApiResponse <AcmeChallengeVerificationStatus> challengeVerificationResponse = null; AcmeApiResponse <AcmeCertificateFulfillmentPromise> certificatePromiseResult = null; AcmeCertificateRequest certifcateRequest = new AcmeCertificateRequest() { Identifiers = new List <DnsCertificateIdentifier>() { new DnsCertificateIdentifier() { Value = "test.com" } } }; //EXECUTE directory = await api.GetDirectoryAsync(); nonceResponse = await api.GetNonceAsync(directory.Data); accountResponse = await api.CreateAccountAsync(directory.Data, nonceResponse.Nonce, new AcmeCreateAccount() { Contact = new List <string>() { "mailto:[email protected]" }, TermsOfServiceAgreed = true }); certificateFulfillmentPromise = await api.RequestCertificateAsync(directory.Data, accountResponse.Nonce, accountResponse.Data, certifcateRequest); authorizations = await api.GetChallengesAsync(certificateFulfillmentPromise.Data); AcmeChallenge httpChallenge = authorizations.First().Data.Challenges.First(t => t.Type.Equals("http-01")); string authKey = CreateAuthorizationKey(accountResponse.Data, httpChallenge.Token); challengeStatusResponse = await api.VerifyChallengeAsync(accountResponse.Data, httpChallenge, certificateFulfillmentPromise.Nonce, authKey); while ( challengeVerificationResponse == null || challengeVerificationResponse.Data.Status == "pending") { challengeVerificationResponse = await api.GetChallengeVerificationStatusAsync(httpChallenge); await Task.Delay(3000); } string csr = GenerateCSR(accountResponse.Data, "test.com"); certificatePromiseResult = await api.FinalizeCertificatePromiseAsync(accountResponse.Data, challengeStatusResponse.Nonce, certificateFulfillmentPromise.Data, csr); //ASSERT (Cant really assert anything here. This call will mostlikey fail. There is no way to validate the domain here) certificatePromiseResult.ShouldNotBeNull(); }