mburm
mburm

Reputation: 1493

HMCA-SHA256 verification in Azure API Management

I want to verify the signature of an incoming request in Azure APIM. My policy looks like

<inbound>
    <base />
    <!-- Extract the signature from the Authorization header -->
    <set-variable name="receivedSignature" value="@(context.Request.Headers.GetValueOrDefault("signature", ""))" />

    <!-- Extract the data to be signed (e.g., request body) -->
    <set-variable name="messageId" value="@(context.Request.Headers.GetValueOrDefault("messageId", ""))" />
    <set-variable name="timestamp" value="@(context.Request.Headers.GetValueOrDefault("timestamp", ""))" />
    <set-variable name="body" value="@(context.Request.Body.As<string>(preserveContent: true))" />
    <set-variable name="dataToSign" value="@((string)context.Variables["messageId"] + (string)context.Variables["timestamp"] + (string)context.Variables["body"])" />

    <!-- Secret key for HMAC calculation (base64 encoded) -->
    <set-variable name="secretKey" value="{{secret}}" />
    <set-variable name="secretKeyEncoded" value="@{
        var key = (string)context.Variables["secretKey"];
        int NumberChars = key.Length;
        byte[] bytes = new byte[NumberChars / 2];

        for (int i = 0; i < NumberChars; i += 2) {
            bytes[i / 2] = Convert.ToByte(key.Substring(i, 2), 16);
        }

        return bytes;
    }" />

    <!-- Calculate the HMAC-SHA256 signature -->
    <set-variable name="calculatedSignature" value="@{
        var key = (byte[])context.Variables["secretKeyEncoded"];
        var data = (string)context.Variables["dataToSign"];
        var hmac = new System.Security.Cryptography.HMACSHA256(key);
        var hashBytes = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data));

        return System.BitConverter.ToString(hashBytes).ToLower().Replace("-", string.Empty);
    }" />

    <!-- Validate the signature -->
    <choose>
        <when condition="@(context.Variables["calculatedSignature"] != context.Variables["receivedSignature"])">
            <!-- Signature is invalid -->
            <return-response>
                <set-status code="401" reason="Unauthorized" />
                <set-body>@("Invalid HMAC signature")</set-body>
            </return-response>
        </when>
    </choose>
</inbound>

I'll get another hash code than the one from the sent signature. What can be the issue here? Suspected is the body. I have to use the raw body for that but am not sure if I'll get it with my call and in case it's wrong how to get it.

Upvotes: 0

Views: 109

Answers (1)

Ikhtesam Afrin
Ikhtesam Afrin

Reputation: 6497

I am using your policy with a few modification in choose block.

<policies>
    <inbound>
        <base />
        <!-- Extract the signature from the Authorization header -->
        <set-variable name="receivedSignature" value="@(context.Request.Headers.GetValueOrDefault("signature", ""))" />
        <!-- Extract the data to be signed (e.g., request body) -->
        <set-variable name="messageId" value="@(context.Request.Headers.GetValueOrDefault("messageId", ""))" />
        <set-variable name="timestamp" value="@(context.Request.Headers.GetValueOrDefault("timestamp", ""))" />
        <set-variable name="body" value="@(context.Request.Body.As<string>(preserveContent: true))" />
        <set-variable name="dataToSign" value="@((string)context.Variables["messageId"] + (string)context.Variables["timestamp"] + (string)context.Variables["body"])" />
        <!-- Secret key for HMAC calculation (base64 encoded) -->
        <set-variable name="secretKey" value="{{secret}}" />
        <set-variable name="secretKeyEncoded" value="@{
        var key = (string)context.Variables["secretKey"];
        int NumberChars = key.Length;
        byte[] bytes = new byte[NumberChars / 2];

        for (int i = 0; i < NumberChars; i += 2) {
            bytes[i / 2] = Convert.ToByte(key.Substring(i, 2), 16);
        }

        return bytes;
    }" />
        <!-- Calculate the HMAC-SHA256 signature -->
        <set-variable name="calculatedSignature" value="@{
        var key = (byte[])context.Variables["secretKeyEncoded"];
        var data = (string)context.Variables["dataToSign"];
        var hmac = new System.Security.Cryptography.HMACSHA256(key);
        var hashBytes = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data));

        return System.BitConverter.ToString(hashBytes).ToLower().Replace("-", string.Empty);
    }" />
        <!-- Validate the signature -->
        <choose>
            <when condition="@(((string)context.Variables["calculatedSignature"]).Trim().ToLower() != ((string)context.Variables["receivedSignature"]).Trim().ToLower())">
                <!-- Signature is invalid -->
                <return-response>
                    <set-status code="401" reason="Unauthorized" />
                    <set-body>@("Invalid HMAC signature")</set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
</policies>

I am passing the request body in JSON format along with the other input data.

enter image description here

Trace-

enter image description here enter image description here enter image description here enter image description here

While testing the policy, check if the policy is being evaluated correctly in trace and also you can check the errors.

Upvotes: 0

Related Questions