private void ValidateRequestHeader(AcmeHeader header, string requestUrl)
        {
            if (header is null)
            {
                throw new ArgumentNullException(nameof(header));
            }

            _logger.LogDebug("Attempting to validate AcmeHeader ...");

            if (!Uri.IsWellFormedUriString(header.Url, UriKind.RelativeOrAbsolute))
            {
                throw new MalformedRequestException("Header Url is not well-formed.");
            }

            if (header.Url != requestUrl)
            {
                throw new NotAuthorizedException();
            }

            if (!_supportedAlgs.Contains(header.Alg))
            {
                throw new BadSignatureAlgorithmException();
            }

            if (header.Jwk != null && header.Kid != null)
            {
                throw new MalformedRequestException("Do not provide both Jwk and Kid.");
            }
            if (header.Jwk == null && header.Kid == null)
            {
                throw new MalformedRequestException("Provide either Jwk or Kid.");
            }

            _logger.LogDebug("successfully validated AcmeHeader.");
        }
Example #2
0
        private async Task <TResult> SendAsync <TResult>(HttpMethod method, Uri uri, object message) where TResult : class
        {
            Verbose($"{method} {uri} {message?.GetType()}");
            var nonceHeader = new AcmeHeader {
                Nonce = nonce
            };

            Verbose($"sending nonce {nonce}");

            HttpContent content = null;

            if (message != null)
            {
                var encodedMessage = jws.Encode(message, nonceHeader);
                var json           = JsonConvert.SerializeObject(encodedMessage, new JsonSerializerSettings()
                {
                    NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented
                });
                content = new StringContent(json, Encoding.UTF8, "application/json");
            }

            var request = new HttpRequestMessage(method, uri)
            {
                Content = content
            };

            var response = await client.SendAsync(request).ConfigureAwait(false);

            Verbose($"response status: {(int)response.StatusCode} {response.ReasonPhrase}");

            RememberNonce(response);


            if (response.Content.Headers.ContentType.MediaType == "application/problem+json")
            {
                var problemJson = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                var problem = JsonConvert.DeserializeObject <Problem>(problemJson);
                Verbose($"error response from server: {problem.Type}: {problem.Detail}");
                throw new AcmeException(problem, response);
            }

            if (typeof(TResult) == typeof(CertificateResponse) && response.Content.Headers.ContentType.MediaType == "application/pkix-cert")
            {
                var certificateBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                var certificateResponse = new CertificateResponse()
                {
                    Certificate = certificateBytes
                };
                GetHeaderValues(response, certificateResponse);
                return(certificateResponse as TResult);
            }

            var responseContent = await response.Content.ReadAsAsync <TResult>().ConfigureAwait(false);

            GetHeaderValues(response, responseContent);

            return(responseContent);
        }
Example #3
0
            private async Task <TResult> SendAsync <TResult>(HttpMethod method, Uri uri, object message, CancellationToken token) where TResult : class
            {
                var nonceHeader = new AcmeHeader
                {
                    Nonce = _nonce
                };

                var request = new HttpRequestMessage(method, uri);

                if (message != null)
                {
                    var encodedMessage = _jws.Encode(message, nonceHeader);
                    var json           = JsonConvert.SerializeObject(encodedMessage, jsonSettings);

                    request.Content = new StringContent(json, Encoding.UTF8, "application/json");
                }

                var response = await _client.SendAsync(request, token).ConfigureAwait(false);

                RememberNonce(response);

                if (response.Content.Headers.ContentType.MediaType == "application/problem+json")
                {
                    var problemJson = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    var problem = JsonConvert.DeserializeObject <Problem>(problemJson);
                    throw new AcmeException(problem, response);
                }

                if (typeof(TResult) == typeof(CertificateResponse) &&
                    response.Content.Headers.ContentType.MediaType == "application/pkix-cert")
                {
                    var certificateBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    var certificateResponse = new CertificateResponse
                    {
                        Certificate = certificateBytes
                    };

                    GetHeaderValues(response, certificateResponse);

                    return(certificateResponse as TResult);
                }

                var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                var responseContent = JObject.Parse(responseText).ToObject <TResult>();

                GetHeaderValues(response, responseContent);

                return(responseContent);
            }
        private async Task ValidateSignatureAsync(AcmeRawPostRequest request, AcmeHeader header, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (header is null)
            {
                throw new ArgumentNullException(nameof(header));
            }

            _logger.LogDebug("Attempting to validate signature ...");

            var jwk = header.Jwk;

            if (jwk == null)
            {
                try
                {
                    var accountId = header.GetAccountId();
                    var account   = await _accountService.LoadAcountAsync(accountId, cancellationToken);

                    jwk = account?.Jwk;
                }
                catch (InvalidOperationException)
                {
                    throw new MalformedRequestException("KID could not be found.");
                }
            }

            if (jwk == null)
            {
                throw new MalformedRequestException("Could not load JWK.");
            }

            var securityKey = jwk.SecurityKey;

            using var signatureProvider = new AsymmetricSignatureProvider(securityKey, header.Alg);
            var plainText = System.Text.Encoding.UTF8.GetBytes($"{request.Header}.{request.Payload ?? ""}");
            var signature = Base64UrlEncoder.DecodeBytes(request.Signature);

            if (!signatureProvider.Verify(plainText, signature))
            {
                throw new MalformedRequestException("The signature could not be verified");
            }

            _logger.LogDebug("successfully validated signature.");
        }
        public async Task ValidateRequestAsync(AcmeRawPostRequest request, AcmeHeader header,
                                               string requestUrl, CancellationToken cancellationToken)
        {
            if (request is null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (header is null)
            {
                throw new ArgumentNullException(nameof(header));
            }
            if (string.IsNullOrWhiteSpace(requestUrl))
            {
                throw new ArgumentNullException(nameof(requestUrl));
            }

            ValidateRequestHeader(header, requestUrl);
            await ValidateNonceAsync(header.Nonce, cancellationToken);
            await ValidateSignatureAsync(request, header, cancellationToken);
        }
Example #6
0
            private async Task <TResult> SendAsync <TResult>(HttpMethod method, Uri uri, object message, CancellationToken token) where TResult : class
            {
                var hasNonce = _nonce != null;
                HttpResponseMessage response;

                do
                {
                    var nonceHeader = new AcmeHeader
                    {
                        Nonce = _nonce
                    };

                    var request = new HttpRequestMessage(method, uri);

                    if (message != null)
                    {
                        var encodedMessage = _jws.Encode(message, nonceHeader);
                        var json           = JsonConvert.SerializeObject(encodedMessage, jsonSettings);

                        request.Content = new StringContent(json, Encoding.UTF8, "application/json");
                    }

                    response = await _client.SendAsync(request, token).ConfigureAwait(false);

                    if (response.Headers.TryGetValues("Replay-Nonce", out var vals))
                    {
                        _nonce = vals.FirstOrDefault();
                    }
                    else
                    {
                        _nonce = null;
                    }

                    if (response.IsSuccessStatusCode || hasNonce || _nonce == null)
                    {
                        break; // either successful or no point in retry
                    }

                    hasNonce = true; // we only allow it once
                } while (true);

                if (response.Content.Headers.ContentType.MediaType == "application/problem+json")
                {
                    var problemJson = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    var problem = JsonConvert.DeserializeObject <Problem>(problemJson);
                    throw new AcmeException(problem, response);
                }

                if (typeof(TResult) == typeof(CertificateResponse) &&
                    response.Content.Headers.ContentType.MediaType == "application/pkix-cert")
                {
                    var certificateBytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    var certificateResponse = new CertificateResponse
                    {
                        Certificate = certificateBytes
                    };

                    GetHeaderValues(response, certificateResponse);

                    return(certificateResponse as TResult);
                }

                var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                var responseContent = JObject.Parse(responseText).ToObject <TResult>();

                GetHeaderValues(response, responseContent);

                return(responseContent);
            }
Example #7
0
        private async Task <ActionResult <Protocol.HttpModel.Account> > CreateAccountAsync(AcmeHeader header, AcmePayload <CreateOrGetAccount> payload)
        {
            if (payload == null)
            {
                throw new MalformedRequestException("Payload was empty or could not be read.");
            }

            var account = await _accountService.CreateAccountAsync(
                header.Jwk !, //Post requests are validated, JWK exists.
                payload.Value.Contact,
                payload.Value.TermsOfServiceAgreed,
                HttpContext.RequestAborted);

            var ordersUrl       = Url.RouteUrl("OrderList", new { accountId = account.AccountId }, "https");
            var accountResponse = new Protocol.HttpModel.Account(account, ordersUrl);

            var accountUrl = Url.RouteUrl("Account", new { accountId = account.AccountId }, "https");

            return(new CreatedResult(accountUrl, accountResponse));
        }
Example #8
0
        public async Task <ActionResult <Protocol.HttpModel.Account> > CreateOrGetAccount(AcmeHeader header, AcmePayload <CreateOrGetAccount> payload)
        {
            if (payload.Value.OnlyReturnExisting)
            {
                return(await FindAccountAsync(payload));
            }

            return(await CreateAccountAsync(header, payload));
        }