Jump to content
billyb

Convert C# function to delphi

Recommended Posts

I have a C# function for SHA256 hashing that I need to convert to Delphi function. I have tried converting it, but the hash values returned do not match the values the vendor says I should get

 

 

public static string GenerateDigest() {
     var digest = "";
     var bodyText = "{ your JSON payload }";
     using (var sha256hash = SHA256.Create()) {
         byte[] payloadBytes = sha256hash
             .ComputeHash(Encoding.UTF8.GetBytes(bodyText));
         digest = Convert.ToBase64String(payloadBytes);
         digest = "SHA-256=" + digest;
     }
     return digest;
}

 

 

Share this post


Link to post
uses
  System.Hash,
  System.NetEncoding,
  System.SysUtils;

function GenerateDigest: string;
var
  bodyText: string;
  payloadBytes: TBytes;
  sha256Hash: THashSHA2;
begin
  bodyText := '{ your JSON payload }';
  sha256Hash := THashSHA2.Create();
  sha256Hash.Update(TEncoding.UTF8.GetBytes(bodyText));
  payloadBytes := sha256Hash.HashAsBytes;
  Result := 'SHA-256=' + TNetEncoding.Base64.EncodeBytesToString(payloadBytes);
end;

 

Share this post


Link to post

Good question - probably because I did not look into the implementation as the API does not tell it automatically does UTF8 conversion which it usually does not.

But as far as I can see GetHashString encodes as hex and not as base64

  • Like 1

Share this post


Link to post

Thank you, but it still does not return what the vendor says the link to the example test code i am trying to deplicate is https://developer.cybersource.com/api/developer-guides/dita-gettingstarted/authentication/GenerateHeader/httpSignatureAuthentication/MessageTesting.html

Below is the string to Hash it should return SHA-256=a/goIo1XUCr80rnKFCWp7yRpwVL50E9RaunuEHh11XM=

{
  "clientReferenceInformation": {
    "code": "TC50171_3"
  },
  "processingInformation": {
    "commerceIndicator": "internet"
  },
  "paymentInformation": {
    "card": {
      "number": "4111111111111111",
      "expirationMonth": "12",
      "expirationYear": "2031",
      "securityCode": "123"
    }
  },
  "orderInformation": {
    "amountDetails": {
      "totalAmount": "102.21",
      "currency": "USD"
    },
    "billTo": {
      "firstName": "John",
      "lastName": "Doe",
      "company": "Visa",
      "address1": "1 Market St",
      "address2": "Address 2",
      "locality": "san francisco",
      "administrativeArea": "CA",
      "postalCode": "94105",
      "country": "US",
      "email": "test@cybs.com",
      "phoneNumber": "4158880000"
    }
  }
}
 

Share this post


Link to post
1 hour ago, billyb said:

the link to the example test code i am trying to deplicate is https://developer.cybersource.com/api/developer-guides/dita-gettingstarted/authentication/GenerateHeader/httpSignatureAuthentication/MessageTesting.html

Below is the string to Hash it should return SHA-256=a/goIo1XUCr80rnKFCWp7yRpwVL50E9RaunuEHh11XM=

When I base64-decode that example digest, the SHA256 hash bytes are:

6b f8 28 22 8d 57 50 2a fc d2 b9 ca 14 25 a9 ef 24 69 c1 52 f9 d0 4f 51 6a e9 ee 10 78 75 d5 73

 

When I SHA256-encode the example JSON, I get varying results depending on how line breaks, whitespace, etc are handled:

61 4d f9 fe dd 28 e7 02 82 04 3b d1 62 fd 52 e8 7d 7c 55 16 87 a9 92 c7 65 08 ed b0 23 82 81 15

75 ca 3d a4 9c d7 c5 d1 c0 85 a4 b9 70 f5 9d 27 0c 7d 34 bd ac a1 92 ef b2 02 2f 6d a1 00 57 15

e0 71 09 2d 7c e3 49 be d3 5e 88 f8 ca 1b 94 c7 1a 49 f8 17 47 a4 a6 16 3e 0f 0f fd e7 f9 34 30

ce ea 16 24 4c 32 66 d3 30 4d 9b 23 a0 b4 0d cc 4a 9f 13 35 13 52 a3 c8 50 9e d1 52 ca a2 dd 8a

d6 ef 0e 0a f6 5e af fd 1c 87 4a b9 09 2a 0c 6b 2e cf df 08 6c 5a 79 a7 ad 3a bd 1b c2 73 c2 c9

89 dd 42 34 69 e6 ec 47 a7 bb 9d ae d8 8a 9b 68 ce f1 06 7a bc 50 27 fd ae df f6 89 f7 56 ad f8

 

No matter what I do to the JSON, I can't get the same result as the decoded digest.  So, I think maybe the website's example is wrong to begin with?

Edited by Remy Lebeau

Share this post


Link to post

Remy,

 

I agree, I will reachout and see if they can confirm the test digest values,

 

Thank you. 

 

Bill B

 

Share this post


Link to post

strange i try tomorrow at work, with c#, see what comes out of that.

maybe ts can get a dump of the sha256 digest as well.

so we can figure out if it's the hash or the base64 function which differs

 

Edited by mvanrijnen

Share this post


Link to post

mvanrijnen,

 

Thank you it would be a big help.

 

I looked at the raw page in Cybesources test page and the page is full of   for spaces.

I tested using the raw data (Below) from right afer "body:" and still not matching digest hash result.

Since Delphi is not a supported language, they offer no help.

 

 


              Resource: /pts/v2/payments/
Host: apitest.cybersource.com
v-c-merchant-id: testmid
Date: Fri, 14 Dec 2018 00:00:00 GMT
secretKey: 0123k20MBbIB2t012345678993gHCIZsQKFpf7dR0hY=
keyID: 01234567-0123-0123-0123-012345678912
Body:
{
  "clientReferenceInformation": {
    "code": "TC50171_3"
  },
  "processingInformation": {
    "commerceIndicator": "internet"
  },
  "paymentInformation": {
    "card": {
      "number": "4111111111111111",
      "expirationMonth": "12",
      "expirationYear": "2031",
      "securityCode": "123"
    }
  },
  "orderInformation": {
    "amountDetails": {
      "totalAmount": "102.21",
      "currency": "USD"
    },
    "billTo": {
      "firstName": "John",
      "lastName": "Doe",
      "company": "Visa",
      "address1": "1 Market St",
      "address2": "Address 2",
      "locality": "san francisco",
      "administrativeArea": "CA",
      "postalCode": "94105",
      "country": "US",
      "email": "test@cybs.com",
      "phoneNumber": "4158880000"
    }
  }
}
             

Share this post


Link to post

When i put the content (the given json in a file), and read it with ReadAllText, i get

 

d6 ef 0e 0a f6 5e af fd 1c 87 4a b9 09 2a 0c 6b 2e cf df 08 6c 5a 79 a7 ad 3a bd 1b c2 73 c2 c9

SHA-256=1u8OCvZer/0ch0q5CSoMay7P3whsWnmnrTq9G8Jzwsk=

 

i believe thats what lebeau  also got (one of his results)

 

quickie c# and the testfile attached also:

 

using System;
using System.Security.Cryptography;
using System.Text;

namespace DigestTEst2
{

    class Program
    {
        static void Main(string[] args)
        {
            string bytesRes = "";
            string digestRes = "";
            string inputtext2 = "";

            static string ByteArrayToString(byte[] ba)
            {
                StringBuilder hex = new StringBuilder(ba.Length * 2);
                foreach (byte b in ba)
                    hex.AppendFormat("{0:x2} ", b);
                return hex.ToString();
            }

            static string GenerateDigest(string bodyText, out string bytetext)
            {
                var digest = "";
                bytetext = "";
                //var bodyText = "{ your JSON payload }";
                using (var sha256hash = SHA256.Create())
                {
                    byte[] payloadBytes = sha256hash
                        .ComputeHash(Encoding.UTF8.GetBytes(bodyText));
                    bytetext = ByteArrayToString(payloadBytes);
                    digest = Convert.ToBase64String(payloadBytes);
                    digest = "SHA-256=" + digest;
                }
                return digest;
            }


            inputtext2 = System.IO.File.ReadAllText(@"C:\Users\myname\Desktop\testfile.txt");

            digestRes = GenerateDigest(inputtext2, out bytesRes);
            // Console.WriteLine(GenerateDigest(inputtext));
            Console.WriteLine(bytesRes);
            Console.WriteLine(digestRes);
        }
    }
}

testfile.txt

Edited by mvanrijnen

Share this post


Link to post
Guest

 

9 hours ago, billyb said:

I looked at the raw page in Cybesources test page and the page is full of   for spaces.

I tested using the raw data (Below) from right afer "body:" and still not matching digest hash result.

Since Delphi is not a supported language, they offer no help.

The input is JSON, and a hash need to be calculated for this JSON, i want to point the following as rule everyone need to know and remember.

 

The standard JSON format does not have line breaks or spaces, while it does allow it, in many cases encouraged for easier human reading, but when it comes to hash and security, the standard format should be used, to achieve that easily, just load the formatted one then save, loading then saving it we have this

Quote

{"clientReferenceInformation":{"code":"TC50171_3"},"processingInformation":{"commerceIndicator":"internet"},"paymentInformation":{"card":{"number":"4111111111111111","expirationMonth":"12","expirationYear":"2031","securityCode":"123"}},"orderInformation":{"amountDetails":{"totalAmount":"102.21","currency":"USD"},"billTo":{"firstName":"John","lastName":"Doe","company":"Visa","address1":"1 Market St","address2":"Address 2","locality":"san francisco","administrativeArea":"CA","postalCode":"94105","country":"US","email":"test@cybs.com","phoneNumber":"4158880000"}}}

One line, and that is the format you should be passing to SHA256.

 

also i tried to calculate the Base64 from SHA256 of the above the result is

Quote

1xBL0CLO3vOTbdszL/I7OZF3hoUbR9sElb9UYOimgR8=

 

I think this is the right result, if you are getting this result then you are doing it right.

The formatted JSON is easier to read in documentation so it is widely showed in easy to read format, but many of these documentation miss mentioning what format you should use as it is should not be formatted to begin with.

Share this post


Link to post

Got the same result as @Kas Ob. when building a JSON object and using my custom hasher:

procedure TForm1.FormCreate(Sender: TObject);
Var
 s256: TAESHA256Hasher;
 tb: TBytes;
 json: TJSONObject;
begin
 s256 := TAESHA256Hasher.Create;
 Try
  json := TJSONObject(TJSONObject.ParseJSONValue(Memo1.Text));
  SetLength(tb, json.EstimatedByteSize);
  SetLength(tb, json.ToBytes(tb, 0));

  tb := s256.HashOf(tb);

  ShowMessage(TNetEncoding.Base64.EncodeBytesToString(tb));
 Finally
  FreeAndNil(s256);
 End;
end;

Ps: I know I'm leaking json.

Quote

 

[Window Title]
Project2

[Content]
1xBL0CLO3vOTbdszL/I7OZF3hoUbR9sElb9UYOimgR8=

[OK]

 

So either the example is wrong or they are not parsing the document as we think (I also tried removing the opening and closing braces so only the body is processed - no luck).

Share this post


Link to post
26 minutes ago, Kas Ob. said:

 

Thats also the output of the c# also, with the json "minified"


		

		d7 10 4b d0 22 ce de f3 93 6d db 33 2f f2 3b 39 91 77 86 85 1b 47 db 04 95 bf 54 60 e8 a6 81 1f
		SHA-256=1xBL0CLO3vOTbdszL/I7OZF3hoUbR9sElb9UYOimgR8=		

		

 

Edited by mvanrijnen

Share this post


Link to post
7 hours ago, Kas Ob. said:

The standard JSON format does not have line breaks or spaces

That was one of the formats I had tested, but it didn't get the same hash as the decoded base64 from the site's example.

 

Share this post


Link to post

Wow, Thank you all for your help on this. I have been told that since my account is a test account, I would have to have a signed contract (ie MONEY $$$) in order to get any support. I am turning this over to the customer so they can fight the battle with CyberSource. Cybersource only offiers a TEST and PROD enviroments and I need a development enviroment so I created a testing account, but cybersource will not allow my dev enviroment to be linked to the actual customers account which has a valid contract. 

 

Again, thank you everyone for your support.

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×