Search code examples
phplaravelphp-8

PHP Laravel - Validating webhook signature


I have an API who sends a signature in the request header (X-Signature):

The value of the header is SHA256withRSA signature of request's body.

The documentation around the webhook have an example on how to validate:

bool Verify(string publicKey, string payload, string signature)
{
    var key = (ICipherParameters)new PemReader(new StringReader(publicKey)).ReadObject();
    var buffer = Encoding.UTF8.GetBytes(payload);
    var signer = SignerUtilities.GetSigner("SHA256withRSA");
    signer.Init(false, key);
    signer.BlockUpdate(buffer, 0, buffer.Length);
    return signer.VerifySignature(Convert.FromBase64String(signature));
} 

I am trying to validate the webhook using PHP/Laravel though. This is my code:

public function isValid(Request $request) : bool
{
    $signature = $request->header('X-Signature');

    if (! $signature) {
        return false;
    }

    $signingSecret = ""-----BEGIN PUBLIC KEY-----\r\nMIIBIjAN......";


    $computedSignature = hash_hmac('sha256', $request->getContent(), $signingSecret);

    return hash_equals($signature, $computedSignature);
}

The above isValid() function returns false on every request I try to make.

This is the output $request->all():

"webhookType": "test" ,
"companyId": "8628731d-ddb5-4c8f-82b1-823734962c1e" ,
"documentId": "3807f10a-9eeb-4818-a935-c8990bd50f71"

And the X-Signature is indeed in the request:

"X-signature": "IY/QvzSDyV...."

Can anyone help me with what I'm doing wrong?


Solution

  • I ended up using openssl:

    public function isValid(Request $request, WebhookConfig $config): bool
    {
    
        $signature = $request->header('X-Signature');
        if (! $signature) {
            return false;
        }
    
    
    
        $publicKey = "...";
    
        if (!$publicKey) {
            return false;
        }
    
        $payload = $request->getContent();
    
        $signature = base64_decode($signature);
    
        $publicKey = openssl_pkey_get_public($publicKey);
    
        $result = openssl_verify($payload, $signature, $publicKey, OPENSSL_ALGO_SHA256);
    
        if ($result === 1) {
            return true;
        }
    
        return false;
    }