Stripe web host signature error - Stripe.net

I am trying to implement striped webhook using the Stripe.net C # library from Jayme Davis. I set up the testing endpoint in the lane control panel and created a secret. The endpoint gets caught and generates StripeEvent using StripeEventUtility.ParseEvent. The problem is that with the ConstructEvent function I cannot sign the signatures. Any help or suggestions would be much appreciated.

isSignaturePresent returns false

//call to create event
stripeEvent = ConstructEvent(json, Request.Headers["Stripe-Signature"], 
SecretFromStripeDashBoard);


private StripeEvent ConstructEvent(string json, string 
stripeSignatureHeader, string secret, int tolerance = 300)
    {
        var signatureItems = parseStripeSignature(stripeSignatureHeader);

        var signature = computeSignature(secret, signatureItems["t"].FirstOrDefault(), json);

        if (!isSignaturePresent(signature, signatureItems["v1"]))
            throw new Exception("The signature for the webhook is not present in the Stripe-Signature header.");

        //var utcNow = EpochUtcNowOverride ?? DateTime.UtcNow.ConvertDateTimeToEpoch();
        //var webhookUtc = Convert.ToInt32(signatureItems["t"].FirstOrDefault());

        //if (utcNow - webhookUtc > tolerance)
        //    throw new Exception("The webhook cannot be processed because the current timestamp is above the allowed tolerance.");

        return Mapper<StripeEvent>.MapFromJson(json);
    }

    private ILookup<string, string> parseStripeSignature(string stripeSignatureHeader)
    {
        return stripeSignatureHeader.Trim()
            .Split(',')
            .Select(item => item.Trim().Split('='))
            .ToLookup(item => item[0], item => item[1]);
    }

    private bool isSignaturePresent(string signature, IEnumerable<string> signatures)
    {
        return signatures.Any(key => secureCompare(key, signature));
    }

    private string computeSignature(string secret, string timestamp, string payload)
    {
        var secretBytes = Encoding.UTF8.GetBytes(secret);
        var payloadBytes = Encoding.UTF8.GetBytes($"{timestamp}.{payload}");

        var cryptographer = new HMACSHA256(secretBytes);
        var hash = cryptographer.ComputeHash(payloadBytes);

        return BitConverter.ToString(hash).Replace("-", "").ToLower();
    }

    private bool secureCompare(string a, string b)
    {
        if (a.Length != b.Length) return false;

        var result = 0;

        for (var i = 0; i < a.Length; i++)
        {
            result |= a[i] ^ b[i];
        }

        return result == 0;
    }
}

      

+3


source to share


1 answer


I answered in the comments above, but to reiterate, the problem was that the string json

provided to the method ConstructEvent

did not contain the exact payload sent by Stripe.

Rather, you initialized the payload with:

var json = JsonSerializer.SerializeToString(request);

      

i.e. you have re-serialized the deserialized request body. However, it is highly unlikely that you will receive the same string as the original payload sent by Stripe. For example. Stripe can send this payload:

{
  "a_key": "a_value",
  "another_key": "another_value"
}

      



but after deserialization + reinitialization, your JSON string can contain this value:

{"another_key": "another_value","a_key": "a_value"}

      

since the order of the keys is not guaranteed and other formatting options (newlines, indentation) come into it.

Webhook signatures are generated using the exact payload (as the original string), so when validating signatures, you must also specify the exact payload sent by your web server or framework.

+2


source







All Articles