I'm trying to get my product to connect to CyberSource with C#, however I'm having trouble.
In short, I'm trying to reproduce the Java code here and essentially translate it in C#.
I end up with different results than they do.
Things I have commented out I have also tried.
using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Security.Cryptography; using System.Text; namespace encoder { class Program { static void Main(string[] args) { string body = "{\n \"encryptionType\": \"RsaOaep\"," +"\n \"targetOrigin\": \"https://example.com\"\n}"; Console.WriteLine(body); string _merchantId = "merchant"; string keyID = "01dbbc88-0736-4d31-94ed-7b84579731b2"; string secret = "SXQgaXMgc2hhcmVkIHNlY3JldA=="; string url = "https://apitest.cybersource.com/flex/v1/keys"; //HashAlgorithm digester = new SHA256CryptoServiceProvider(); //byte[] digest = digester.ComputeHash(Encoding.UTF8.GetBytes(body)); //string value = string.Format("SHA-256={0}", System.Convert.ToBase64String(digest)); byte[] bytes = Encoding.UTF8.GetBytes(body); SHA256Managed hashstring = new SHA256Managed(); byte[] digest = hashstring.ComputeHash(bytes); string value = string.Format("SHA-256={0}", System.Convert.ToBase64String(digest)); Console.WriteLine("Digest: " + value); Console.ReadLine(); string todaysDate = DateTime.Now.ToString("ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"); //string todaysDate = "Mon, 01 Jan 2018 00:00:00 GMT"; Console.WriteLine("Current Time: " + todaysDate); Dictionary<string, string> signedHeaders = new Dictionary<string, string>(); signedHeaders.Add("host", "apitest.cybersource.com"); signedHeaders.Add("date", todaysDate); signedHeaders.Add("(request-target)", "post /flex/va/keys/"); signedHeaders.Add("digest", value); //signedHeaders.Add("digest", "SHA-256=fRDzptXm4RRRD3pC/eoIBoHShRzjRAf7Xkj18upMtI8="); //signedHeaders.Add("digest", "SHA-256=YljtibTei+du4xVIDxMr3HBsyLAEDuiYaag9TcU9jHA="); signedHeaders.Add("v-c-merchant-id", _merchantId); Console.WriteLine("Signed Headers: " + signedHeaders); Console.ReadLine(); StringBuilder signatureString = new StringBuilder(); StringBuilder headersString = new StringBuilder(); foreach (KeyValuePair<string, string> s in signedHeaders) { signatureString.Append('\n').Append(s.Key).Append(": ").Append(s.Value); headersString.Append(' ').Append(s.Key); } signatureString.Remove(0, 1); headersString.Remove(0, 1); HMACSHA256 sha256HMAC = new HMACSHA256(System.Convert.FromBase64String(secret)); sha256HMAC.Initialize(); StringBuilder signature = new StringBuilder(); byte[] hashBytes = sha256HMAC.ComputeHash(Encoding.UTF8.GetBytes(signatureString.ToString())); signature.Append("keyid=\"").Append(keyID) .Append("\", ").Append("algorithm=\"HmacSHA256\", ") .Append("headers=\"").Append(headersString).Append("\", ") .Append("digest: signature=\"").Append(System.Convert.ToBase64String(hashBytes)).Append("\""); Console.WriteLine("Signature: " + signature); Console.ReadLine(); System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url); myHttpWebRequest.Method = "POST"; myHttpWebRequest.ContentType = "application/json"; myHttpWebRequest.Headers["v-c-merchant-id"] = _merchantId; myHttpWebRequest.Host = "apitest.cybersource.com"; //myHttpWebRequest.Headers["v-c-date"] = DateTime.Now.ToString(); myHttpWebRequest.Timeout = 30000; //' 30 second timeout' myHttpWebRequest.KeepAlive = false; myHttpWebRequest.Date = DateTime.Now; //myHttpWebRequest.Date = new DateTime(2018, 1, 1, 0, 0, 0); string strResponse = string.Empty; byte[] myBytes; using (Stream myOutputStream = myHttpWebRequest.GetRequestStream()) { myBytes = System.Text.Encoding.ASCII.GetBytes(body); myOutputStream.Write(myBytes, 0, myBytes.Length); myOutputStream.Close(); } //http://msdn.microsoft.com/en-us/library/system.net.webresponse.getresponsestream%28v=vs.71%29.aspx using (WebResponse myWebResponse = myHttpWebRequest.GetResponse()) { Stream RecieveStream = myWebResponse.GetResponseStream(); Encoding encode = Encoding.UTF8; StreamReader readStream = new StreamReader(RecieveStream, encode); strResponse = readStream.ReadToEnd(); } Console.WriteLine("Response: " + strResponse); Console.ReadLine(); } }
I should get a response back from CyberSource, but I keep getting a 401 unauthorized error.
I also notice that the very first console.writeline gets a very different digest of SHA-256=lJooQmwcasZC4okGe61dGdcdlE672vGi5x0D/vmcZx8=.
I should get SHA-256=YljtibTei+du4xVIDxMr3HBsyLAEDuiYaag9TcU9jHA=.
UPDATE
I should also add that we are unable to use the CyberSource SDK as we cannot use ILMerge to merge with our DLL.
the same issue applies in that I could not get my Digest and Signature hashes in the request header to match the examples and thus getting nothing but a 401 Unauthorized response from CyberSource.
Simple answer is to use the "official" C# hashing functions that can found here: https://developer.cybersource.com/api/developer-guides/dita-gettingstarted/GenerateHeader/httpSignat.../developerbook /chatrandom
Still, you may not get the same hash when you compare your code to your example unless the body payload of what you're comparing is minified and the body payload of your example is minified as well. I suspect that this is due to the differences in the final hash when comparing hashes to the two message bodies that may have the same content but differ in indentations, spacing, line-breaks, etc.. One small extra space throws the final hash off. But I assure you it's irrelevant that your hash differs from the example you are comparing against as long as your code is hashing the message body correctly. Since the Digest hash is most likely being used to verify that the body of the request has not been tempered with.
I have my working sample code here: https://github.com/sarn1/example-cybersource-csharp-api-rest /omegle and a detailed explanation link there.
12-14-2022 04:49 AM