Search code examples
c#encryptionshapayment-processingadyen

Adyen, Unable to get correct SHA-256 encryption


I am using a signing string and a private key to generate a public key using SHA-256 encryption.

The hashing function is a standard C# SHA-256 hashing function:

string CalculateHMAC256(string hmacKey, string signingstring)
    {
        byte[] key = Encoding.UTF8.GetBytes(hmacKey);
        byte[] data = Encoding.UTF8.GetBytes(signingstring);
        using (HMACSHA256 hmac = new HMACSHA256(key))
        {
            byte[] result = hmac.ComputeHash(data);
            return Convert.ToBase64String(result);
        }
    }

The string signature looks like this: allowedMethods:blockedMethods:countryCode:currencyCode:merchantAccount:merchantReference:offset:orderData:paymentAmount:sessionValidity:shipBeforeDate:shopperEmail:shopperLocale:shopperReference:skinCode:::GB:GBP:MyTest:abc:::1:2017-04-28T15:07:10+01:00:2017-04-30::en_US::neDRF4H4

I have also tried this with escaped ':' as advised: allowedMethods:blockedMethods:countryCode:currencyCode:merchantAccount:merchantReference:offset:orderData:paymentAmount:sessionValidity:shipBeforeDate:shopperEmail:shopperLocale:shopperReference:skinCode:::GB:GBP:MyTest:abc:::1:2017-04-29T13:34:48+01:00:2017-05-01::en_US::neDRF4H4

And then my html form looks like this:

 <form ngNoForm name="frmsp" id="frmsp" target="_blank" 
    action="https://test.barclaycardsmartpay.com/hpp/pay.shtml"
    method="post">
    <input type="hidden" name="merchantSig" [value]="merchantSignature" />
    <input type="hidden" name="currencyCode" [value]="smartPayment?.currencyCode" />
    <input type="hidden" name="merchantAccount" [value]="smartPayment?.merchantAccount" />
    <input type="hidden" name="merchantReference" [value]="smartPayment?.merchantReference" />
    <input type="hidden" name="paymentAmount" [value]="smartPayment?.paymentAmount" />
    <input type="hidden" name="sessionValidity" [value]="smartPayment?.sessionValidity" />
    <input type="hidden" name="shipBeforeDate" [value]="smartPayment?.shipBeforeDate" />
    <input type="hidden" name="shopperLocale" [value]="smartPayment?.shopperLocale" />
    <input type="hidden" name="orderData" [value]="smartPayment?.orderData" />
    <input type="hidden" name="skinCode" [value]="smartPayment?.skinCode" />
    <input type="hidden" name="countryCode" [value]="smartPayment?.countryCode" />
    <input type="hidden" name="shopperEmail" [value]="smartPayment?.shopperEmail" />
    <input type="hidden" name="shopperReference" [value]="smartPayment?.shopperReference" />
    <input type="hidden" name="allowedMethods" [value]="smartPayment?.allowedMethods" />
    <input type="hidden" name="blockedMethods" [value]="smartPayment?.blockedMethods" />
    <input type="hidden" name="offset" [value]="smartPayment?.offset" />    
    <input type="submit" value="Process Payment" (click)="buttonClicked()">

Where the fields values are being calculated with angular2 - using the same ones used to build the signing string, so I am confident that the data matches.

When I send the form, I get an error stating that the merchant signature is incorrect.

Perhaps the format of the signing string could be incorrect? I am trying to implement a call to Barclaycard Smartpay using hosted payment. Documentation here: Hosted Payment Page Integration Guide

Note: that the barclays documentation is out of date (last updated 2012) and that the references to SHA-1 are now using SHA-256. It is built using Adyen payment systems and I have reused the string generation examples using the following example code: https://github.com/Adyen/adyen-asp.net-sample-code


Solution

  • There is a problem with your signing string. You need to replace "\" with "\" and ":" with ":" in all your vallues.

    I also suggest to use the code from this sample on GIT to generate create your encrypted signature.

    Using the code below I get the same signature as on the test page provided by Adyen.

     // Computes the Base64 encoded signature using the HMAC algorithm with the HMACSHA256 hashing function.
        string CalculateHMAC(string hmacKey, string signingstring)
    {
        byte[] key = PackH(hmacKey);
        byte[] data = Encoding.UTF8.GetBytes(signingstring);
    
        try
        {
            using (HMACSHA256 hmac = new HMACSHA256(key))
            {
                // Compute the hmac on input data bytes
                byte[] rawHmac = hmac.ComputeHash(data);
    
                // Base64-encode the hmac
                return Convert.ToBase64String(rawHmac);
            }
        }
        catch (Exception e)
        {
            throw new Exception("Failed to generate HMAC : " + e.Message);
        }
    }
    
    byte[] PackH(string hex)
    {
        if ((hex.Length % 2) == 1)
        {
            hex += '0';
        }
    
        byte[] bytes = new byte[hex.Length / 2];
        for (int i = 0; i < hex.Length; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        }
    
        return bytes;
    }
    

    Try to use only a limited amound of fields and see if you get any results. I used the fields below (also take their order into account!)

    currencyCode:merchantAccount:merchantReference:paymentAmount:sessionValidity:shipBeforeDate:shopperLocale:skinCode