When a .NET application communicates with an AWS service, authentication is an important part of the request. Many AWS SDKs handle authentication automatically, but there are situations where developers need to create custom HTTP requests, use a service-specific API, or integrate with an endpoint that does not have a convenient SDK method.

In these cases, AWS Signature Version 4, commonly called SigV4, provides a way to authenticate HTTP requests using AWS credentials. The signature is calculated from parts of the request, including the HTTP method, URI, headers, and payload hash.

Implementing SigV4 manually can be error-prone. Small differences in URL encoding, header formatting, timestamps, or payload hashing can cause AWS to reject a request.

This article explains how SigV4 works, how to sign custom HTTP requests in .NET, and which practices help prevent authentication failures in production.

What Is AWS Signature Version 4?

AWS Signature Version 4, or SigV4, is a request-signing process used to authenticate requests to AWS services. It uses cryptographic signing to demonstrate that a request was created using valid AWS credentials and that the signed parts of the request have not changed.

A SigV4 signature is calculated using several elements:

  • HTTP method, such as GET or POST.

  • Canonical URI and query parameters.

  • Headers included in the signature.

  • A hash of the request payload.

  • Request timestamp.

  • AWS service name and region.

  • AWS credentials used to calculate the signature.

The resulting signature is included in the HTTP Authorization header.

For example, an IAM-authorized API Gateway endpoint may require a SigV4-signed request. Sending a normal HTTP request without the required authentication information can result in an authorization error.

When Should You Use a Standalone SigV4 Signer?

The AWS SDK for .NET provides service-specific clients that automatically handle request signing. These clients should generally be your first choice when they support the operation you need.

A standalone signer is useful when you need to create custom HTTP requests and no suitable generated SDK operation is available.

Approach

Best use case

Main consideration

AWS service client

Calling a supported AWS API operation

Simplifies authentication, serialization, and service-specific handling

Standalone SigV4 signer

Signing a custom HTTP request

You manage the HTTP request and response

SigV4 signing handler

Automatically signing requests sent through HttpClient

Requires careful handler configuration for retries and redirects

Presigned URL

Allowing temporary access to a supported resource or operation

Anyone possessing the URL may be able to use it within its validity and permissions

The AWS SDK for .NET includes a public standalone signer in AWSSDK.Core version 4.0.101.0 and later. It supports header-based signing and presigned URLs without requiring you to implement the SigV4 algorithm yourself.

Setting Up SigV4 Signing in .NET

For this example, you will use C# with HttpClient to call an IAM-authorized AWS Security Token Service endpoint.

The example uses the GetCallerIdentity operation, which returns information about the AWS identity making the request.

Step 1: Install the AWS SDK Core Package

Create a console application:

Code
dotnet new console -n SigV4Demo
cd SigV4Demo

Install the AWS SDK Core package:

Code
dotnet add package AWSSDK.Core --version 4.0.101.0

If your project already references a compatible version of AWSSDK.Core, verify the installed version before adding another package reference.

Step 2: Configure AWS Credentials

The signer needs AWS credentials to calculate the signature.

For local development, you can configure credentials through the AWS CLI or another supported AWS credential provider.

For example, an existing AWS CLI profile can be configured using:

Code
aws configure --profile developer

The AWS SDK credential resolution chain can then obtain credentials from supported sources, including profiles and environment-based configuration.

For production applications, prefer IAM roles and temporary credentials where available. Applications running on AWS services can often obtain credentials through their assigned execution or instance role.

Avoid placing access keys directly in source code or committing credential files to a repository.

Signing and Sending a Custom HTTP Request

The following example creates a request, signs it, applies the generated authentication headers, and sends it using HttpClient.

C#
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Amazon;
using Amazon.Runtime.Credentials;
using Amazon.Runtime.Signing;

public class Program
{
    public static async Task Main()
    {
        var requestUri = new Uri(
            "https://sts.us-east-1.amazonaws.com/" +
            "?Action=GetCallerIdentity" +
            "&Version=2011-06-15");

        var signingRequest = new AWSSigningRequest
        {
            HttpMethod = HttpMethod.Get,
            RequestUri = requestUri
        };

        var signingParameters = new AWSSigV4Parameters
        {
            Credentials =
                DefaultAWSCredentialsIdentityResolver.GetCredentials(),
            Region = RegionEndpoint.USEast1,
            Service = "sts"
        };

        var signingResult = await AWSSigV4Signer.SignAsync(
            signingRequest,
            signingParameters,
            default);

        using var httpClient = new HttpClient();

        using var httpRequest = new HttpRequestMessage(
            HttpMethod.Get,
            requestUri);

        foreach (var header in signingResult.Headers)
        {
            httpRequest.Headers.TryAddWithoutValidation(
                header.Key,
                header.Value);
        }

        using var response = await httpClient.SendAsync(httpRequest);

        var responseBody = await response.Content.ReadAsStringAsync();

        response.EnsureSuccessStatusCode();

        Console.WriteLine(responseBody);
    }
}

This example uses the standalone signer API available in recent versions of the AWS SDK for .NET. AWS

Understanding the Important Parts

The AWSSigningRequest object describes the HTTP request that needs to be signed. It contains the HTTP method and the complete request URI, including the query string.

The AWSSigV4Parameters object identifies the credentials, AWS region, and service being called.

In this example:

  • RegionEndpoint.USEast1 identifies the AWS region.

  • Service = "sts" identifies the Security Token Service signing name.

  • DefaultAWSCredentialsIdentityResolver.GetCredentials() resolves credentials through the AWS SDK's credential-resolution mechanism.

  • AWSSigV4Signer.SignAsync() calculates the signature and returns the headers to apply to the outbound request.

The signing operation does not send the HTTP request. That responsibility remains with HttpClient.

This separation is useful when an application needs to control its HTTP transport, inspect responses, or integrate signing into an existing request pipeline.

The endpoint, region, and service name must match the operation being called. Using an incorrect service name or region can produce a signature mismatch.

Signing POST Requests With a JSON Payload

Custom AWS HTTP requests frequently contain JSON data. In these cases, the payload must be handled consistently during signing and transmission.

Consider an IAM-authorized API Gateway endpoint that accepts a JSON request.

C#
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Amazon;
using Amazon.Runtime.Credentials;
using Amazon.Runtime.Signing;

public class ApiClient
{
    public static async Task SendRequestAsync()
    {
        var requestUri = new Uri(
            "https://example.execute-api.us-east-1.amazonaws.com/prod/items");

        var json = """
        {
            "name": "Keyboard",
            "quantity": 2
        }
        """;

        var payload = Encoding.UTF8.GetBytes(json);

        var signingRequest = new AWSSigningRequest
        {
            HttpMethod = HttpMethod.Post,
            RequestUri = requestUri,
            Content = payload
        };

        var parameters = new AWSSigV4Parameters
        {
            Credentials =
                DefaultAWSCredentialsIdentityResolver.GetCredentials(),
            Region = RegionEndpoint.USEast1,
            Service = "execute-api"
        };

        var signingResult = await AWSSigV4Signer.SignAsync(
            signingRequest,
            parameters,
            default);

        using var httpClient = new HttpClient();

        using var request = new HttpRequestMessage(
            HttpMethod.Post,
            requestUri);

        request.Content = new ByteArrayContent(payload);

        request.Content.Headers.ContentType =
            new System.Net.Http.Headers.MediaTypeHeaderValue(
                "application/json");

        foreach (var header in signingResult.Headers)
        {
            request.Headers.TryAddWithoutValidation(
                header.Key,
                header.Value);
        }

        using var response = await httpClient.SendAsync(request);

        response.EnsureSuccessStatusCode();

        Console.WriteLine("Request completed successfully.");
    }
}

Replace the example endpoint with your actual API Gateway endpoint and ensure the IAM identity has permission to invoke it.

The Content property supplies the payload used during signing. The same payload bytes are then sent through HttpClient.

Using the same bytes is important because changing the payload after signing can cause the request to fail signature validation.

The execute-api service name is appropriate for API Gateway requests that use SigV4 authentication. Other AWS services require their respective signing service names.

Common SigV4 Errors and Troubleshooting

SigV4 failures often result from differences between the request that was signed and the request that AWS received.

Error or symptom

Possible cause

Recommended fix

SignatureDoesNotMatch

Request URI, payload, or signed headers changed

Verify the exact URI, headers, and payload

InvalidSignatureException

Incorrect signing configuration

Check the service name, region, and credential source

AccessDeniedException or HTTP 403

Missing IAM permissions or an authorization restriction

Review the identity policy and resource policy

Expired-token error

Temporary credentials have expired

Refresh credentials through the configured provider

Request rejected after a redirect

The redirected request was not signed for its new destination

Handle redirects explicitly and sign each destination

Request rejected after a delayed retry

The original signature was reused or became too old

Generate a fresh signature for each attempt

Check the AWS Region and Service Name

The region and service name are inputs to the signing process.

For example, an API Gateway request commonly uses execute-api, while an STS request uses sts.

If the endpoint points to one region but the signer uses another, AWS may reject the request.

Always derive the signing configuration from the actual service endpoint and operation.

Check the Request After Signing

Avoid modifying the URI, query string, signed headers, or payload after calculating the signature.

If a request needs to be changed, create or update the request and sign it again.

Be especially careful with HTTP middleware that changes URLs, adds headers, or retries requests after signing.

Handle Temporary Credentials Correctly

Temporary AWS credentials include a session token in addition to the access key ID and secret access key.

The signing implementation must use credentials that include the required session information. Using expired credentials or failing to include the session token can cause authentication failures.

Use a supported credential provider instead of manually assembling credential values whenever possible.

Best Practices for Production Applications

Use the AWS SDK Whenever Possible

If a generated AWS service client supports your operation, use it instead of creating and signing raw HTTP requests.

Service clients generally provide service-specific request models, response handling, retry behavior, and authentication integration.

Use the standalone signer when a custom HTTP integration genuinely requires it.

Reuse HttpClient

Avoid creating a new HttpClient for every request in a high-volume application.

Reuse a client or use IHttpClientFactory in ASP.NET Core applications. This helps manage connection lifetimes and avoids unnecessary socket consumption.

Sign Every Retry Separately

A retry must not blindly reuse a previously signed request.

SigV4 signatures include time-sensitive information, and the signed request must match the request being transmitted.

Configure retry handling so that every attempt passes through the signing process again.

Handle Redirects Carefully

Automatic redirects can cause a signed request to be sent to a different URI without recalculating the signature.

For signed HTTP clients, disable automatic redirects in the underlying transport and handle redirects explicitly when required. Validate the destination before sending credentials or signing a request for it.

Protect Credentials and Logs

Never log secret access keys, session tokens, or complete Authorization headers.

Log useful diagnostic information instead, such as the HTTP status code, AWS request identifier when available, target service, and a sanitized error message.

Use HTTPS

Always use HTTPS for AWS service requests. Transport encryption protects data in transit, while SigV4 authenticates the signed request.

These protections address different concerns and should be used together.

Advantages and Disadvantages of the Standalone SigV4 Signer

The standalone signer makes it easier to authenticate custom AWS HTTP requests without implementing the cryptographic signing algorithm manually.

Its main advantages include support for custom request workflows, integration with HttpClient, and the ability to generate presigned URLs.

However, it does not replace a service client. Developers remain responsible for request construction, response processing, retries, endpoint handling, and application-specific error recovery.

The signing process also requires careful attention to request consistency, timestamps, payloads, and credentials. An incorrect configuration can cause failures that are difficult to diagnose without good logging.

Summary

AWS Signature Version 4 allows .NET applications to authenticate custom HTTP requests to supported AWS endpoints. The standalone signer in AWSSDK.Core provides a public API for calculating signatures without requiring developers to implement the SigV4 algorithm themselves.

To use it correctly, construct the request first, configure the appropriate credentials, region, and service name, calculate the signature, and apply the returned headers before sending the request.

For production applications, prioritize supported AWS service clients when available. When custom HTTP requests are necessary, use HTTPS, preserve the signed request, refresh credentials correctly, and ensure every retry is signed independently.