billyb 1 Posted June 14, 2021 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
Stefan Glienke 2002 Posted June 14, 2021 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
mvanrijnen 123 Posted June 14, 2021 (edited) -nevermind, reado ver the base64 thing- Edited June 14, 2021 by mvanrijnen Share this post Link to post
Stefan Glienke 2002 Posted June 14, 2021 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 1 Share this post Link to post
billyb 1 Posted June 14, 2021 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
mvanrijnen 123 Posted June 14, 2021 maybe an EOL too many/too short ? (at the end of the file) Share this post Link to post
Remy Lebeau 1396 Posted June 14, 2021 (edited) 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 June 14, 2021 by Remy Lebeau Share this post Link to post
billyb 1 Posted June 14, 2021 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
mvanrijnen 123 Posted June 14, 2021 (edited) 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 June 14, 2021 by mvanrijnen Share this post Link to post
billyb 1 Posted June 14, 2021 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
mvanrijnen 123 Posted June 15, 2021 (edited) 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 June 15, 2021 by mvanrijnen Share this post Link to post
Guest Posted June 15, 2021 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
aehimself 396 Posted June 15, 2021 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
mvanrijnen 123 Posted June 15, 2021 (edited) 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 June 15, 2021 by mvanrijnen Share this post Link to post
Remy Lebeau 1396 Posted June 15, 2021 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
billyb 1 Posted June 15, 2021 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