예제 #1
0
        /// <summary>
        /// Wraps controller action
        /// </summary>
        /// <param name="action">Action</param>
        /// <param name="request">ACME request. If presents wrapper validates JWS</param>
        /// <returns></returns>
        public AcmeResponse WrapAction(Action <AcmeResponse> action, AcmeRequest request, bool UseJwk = false)
        {
            var response = CreateResponse();

            try
            {
                Logger.Info("Request {method} {path} {token}", request.Method, request.Path, request.Token);

                if (request.Method == "POST")
                {
                    #region Check JWS
                    IAccount account = null;

                    // Parse JWT
                    var token  = request.Token;
                    var header = token.GetProtected();
                    try
                    {
                        if (token == null)
                        {
                            throw new Exception("JSON Web Token is empty");
                        }
                    }
                    catch (Exception e)
                    {
                        throw new AcmeException(ErrorType.Unauthorized, "Cannot parse JSON Web Token", System.Net.HttpStatusCode.Unauthorized, e);
                    }

                    if (header.Url == null)
                    {
                        throw new MalformedException("The JWS header MUST have 'url' field");
                    }

                    /// The "jwk" and "kid" fields are mutually exclusive. Servers MUST
                    /// reject requests that contain both.
                    if (header.Key != null && header.KeyID != null)
                    {
                        throw new MalformedException("The JWS header contains both mutually exclusive fields 'jwk' and 'kid'");
                    }

                    if (UseJwk)
                    {
                        if (header.Key == null)
                        {
                            throw new AcmeException(ErrorType.IncorrectResponse, "JWS MUST contain 'jwk' field", System.Net.HttpStatusCode.BadRequest);
                        }
                        if (!token.Verify())
                        {
                            throw new AcmeException(ErrorType.Unauthorized, "JWS signature is invalid", System.Net.HttpStatusCode.Unauthorized);
                        }

                        account = AccountService.FindByPublicKey(header.Key);
                        // If a server receives a POST or POST-as-GET from a deactivated account, it MUST return an error response with status
                        // code 401(Unauthorized) and type "urn:ietf:params:acme:error:unauthorized"
                    }
                    else
                    {
                        if (header.KeyID == null)
                        {
                            throw new AcmeException(ErrorType.IncorrectResponse, "JWS MUST contain 'kid' field", System.Net.HttpStatusCode.BadRequest);
                        }

                        account = AccountService.GetById(GetIdFromLink(header.KeyID));

                        if (!token.Verify(account.Key.GetPublicKey()))
                        {
                            throw new AcmeException(ErrorType.Unauthorized, "JWS signature is invalid", System.Net.HttpStatusCode.Unauthorized);
                        }

                        // Once an account is deactivated, the server MUST NOT accept further
                        // requests authorized by that account's key
                        // https://tools.ietf.org/html/rfc8555#section-7.3.6
                    }
                    if (account != null)
                    {
                        AssertAccountStatus(account);
                    }
                    #endregion

                    #region Check Nonce

                    // The "nonce" header parameter MUST be carried in the protected header of the JWS.
                    var nonce = request.Token.GetProtected().Nonce
                                ?? throw new MalformedException("The 'nonce' header parameter doesn't present in the protected header of the JWS");
                    NonceService.Validate(nonce);

                    #endregion
                }

                // Invoke action
                action.Invoke(response);
            }
            catch (AcmeException e)
            {
                response.StatusCode = (int)e.StatusCode;
                Error error = e;
                response.Content = error;
                Logger.Error(e);
            }
            catch (Exception e)
            {
                response.StatusCode = 500; // Internal Server Error
                Error error = e;
                response.Content = error;
                Logger.Error(e);
            }

            Logger.Info("Response {@response}", response);

            return(response);
        }