Создание HTTP-подписи для интеграции CyberSource

Я пытаюсь подключить свой продукт к CyberSource с помощью C#, но у меня возникли проблемы.

Короче говоря, я пытаюсь воспроизвести Java-код здесь и по существу перевести его в C#,

Я получаю другие результаты, чем они.

Вещи, которые я прокомментировал, я тоже попробовал.

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();
        }

    }

Я должен получить ответ от CyberSource, но я продолжаю получать 401 unauthorized error,

Я также заметил, что самый первый файл console.writeline получает совсем другой дайджест SHA-256=lJooQmwcasZC4okGe61dGdcdlE672vGi5x0D/vmcZx8=,
Я должен получить SHA-256=YljtibTei+du4xVIDxMr3HBsyLAEDuiYaag9TcU9jHA=,

ОБНОВИТЬ

Я также должен добавить, что мы не можем использовать CyberSource SDK, поскольку мы не можем использовать ILMerge для слияния с нашей DLL.

Спасибо!

1 ответ

Если я опоздаю к игре для OP, я надеюсь, что мой ответ будет полезен для других, кто сталкивается с подобной проблемой. Хотя в моем примере используется метод POST для метода PAYMENT, применима та же проблема: я не смог получить хеш-коды Digest и Signature в заголовке запроса, чтобы соответствовать примерам, и, таким образом, получить только 401 несанкционированный ответ от CyberSource.

Простой ответ - использовать "официальные" хеширующие функции C#, которые можно найти здесь: https://developer.cybersource.com/api/developer-guides/dita-gettingstarted/GenerateHeader/httpSignatureAuthentication.html

Тем не менее, вы можете не получить тот же хэш, когда сравниваете свой код с вашим примером, если только полезная нагрузка тела сравниваемого объекта не уменьшена, а полезная нагрузка тела вашего примера также сведена к минимуму. Я подозреваю, что это связано с различиями в окончательном хеше при сравнении хешей с двумя телами сообщений, которые могут иметь одинаковое содержимое, но различаются отступами, пробелами, переносами строк и т. Д. Одно небольшое дополнительное пространство отбрасывает окончательный хеш, Но я уверяю вас, что не имеет значения, что ваш хеш отличается от примера, с которым вы сравниваете, если ваш код правильно хэширует тело сообщения. Поскольку хэш дайджеста, скорее всего, используется для проверки того, что тело запроса не было изменено.

У меня есть мой рабочий пример кода здесь: https://github.com/sarn1/example-cybersource-csharp-api-rest и ссылка с подробным объяснением там.

Я бы добавил это как комментарий, но у меня пока недостаточно репутации. Я не знаю, что не так с вашим кодом, но вот предварительно встроенный клиент CyberSource C#, который можно использовать для запроса ключа Flex - https://github.com/CyberSource/cybersource-rest-samples-csharp

Другие вопросы по тегам