Создание 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