Pour un fournisseur de paiement, je dois calculer un code d'authentification de message basé sur le hachage, en utilisant HMAC-SHA256. Cela me cause pas mal de problèmes.
Le fournisseur de paiement donne deux exemples de code d'authentification correctement calculé en pseudo-code. Toutes les clés sont en hexadécimal.
key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100¤cy=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
message = "amount=100¤cy=EUR"
Ki = 61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950
Ko = 0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a
MAC = SHA256( hexDecode(Ko) + SHA256( hexDecode(Ki) + message ) )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
J'ai essayé d'écrire le code pour ce faire, après avoir fait des recherches, mais je continue à trouver des résultats différents.
private static void Main(string[] args)
{
var key = "57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66";
var ki = "61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950";
var ko = "0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a";
var mm = "amount=100¤cy=EUR";
var result1 = CalcHMACSHA256Hash(HexDecode(key), mm);
var result2 = CalcSha256Hash(string.Format("{0}{1}", HexDecode(ko), CalcSha256Hash(HexDecode(ki) + mm)));
Console.WriteLine("Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905");
Console.WriteLine("Actual 1: " + result1);
Console.WriteLine("Actual 2: " + result2);
Console.WriteLine("------------------------------");
Console.ReadKey();
}
private static string HexDecode(string hex)
{
var sb = new StringBuilder();
for (int i = 0; i <= hex.Length - 2; i += 2)
{
sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber))));
}
return sb.ToString();
}
private static string CalcHMACSHA256Hash(string plaintext, string salt)
{
string result = "";
var enc = Encoding.Default;
byte[]
baText2BeHashed = enc.GetBytes(plaintext),
baSalt = enc.GetBytes(salt);
System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
return result;
}
public static string CalcSha256Hash(string input)
{
SHA256 sha256 = new SHA256Managed();
byte[] sha256Bytes = Encoding.Default.GetBytes(input);
byte[] cryString = sha256.ComputeHash(sha256Bytes);
string sha256Str = string.Empty;
for (int i = 0; i < cryString.Length; i++)
{
sha256Str += cryString[i].ToString("x2");
}
return sha256Str;
}
Et voici le résultat que j'obtiens:
Expected: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Actual 1: 421ce16f2036bb9f2a3770c16f01e9220f0232d45580584ca41768fd16c15fe6
Actual 2: 290f14398bf8c0959dfc963e2fd9c377534c6fec1983025d2ab192382f132b92
Donc, avec aucune des deux méthodes, je peux obtenir le résultat souhaité par l'exemple du fournisseur.
Qu'est-ce que je manque ici? Est-ce un encodage? Mon hexDecode est-il vissé?
Outil de test du fournisseur de paiement: http://tech.dibs.dk/dibs_api/other_features/hmac_tool/
Exemple de code PHP: http://tech.dibspayment.com/dibs_api/other_features/mac_calculation/
J'ai trouvé une solution complète à votre problème (car c'est probablement ce que vous cherchiez). Il calcule le hachage correct en utilisant à la fois vos méthodes 1 et 2.
Le programme peut être organisé en trois sections:
byte[]
Pour la saisiestring
-> byte[]
byte[]
-> hex string
string
-> byte[]
(merci @bobince!)Avant de commencer, assurez-vous que vous disposez des instructions suivantes afin de ne pas obtenir une tonne d'erreurs de ne pas les inclure.
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
Cela calculera le HMAC-SHA256 (votre méthode 1). Comme vous pouvez le voir, c'est beaucoup plus simple que la méthode 2 mais donne le même résultat.
private static byte[] HashHMAC(byte[] key, byte[] message)
{
var hash = new HMACSHA256(key);
return hash.ComputeHash(message);
}
Maintenant, pour calculer le hachage en utilisant une tonne de hachage SHA (votre méthode 2), c'est un peu plus compliqué. C'est fondamentalement la même chose que votre pseudo-code sans décodage hexadécimal et utilise byte[]
Pour la saisie à la place. Cela ressemblerait à:
MAC = SHA256( outerKey + SHA256( innerKey + message ) )
Au lieu de votre:
MAC = SHA256( hexDecode(outerKey) + SHA256( hexDecode(innerKey) + message ) )
Où outerKey
, innerKey
et message
sont tous des byte[]
S. Bien sûr, dans ce cas, toutes les clés ont déjà été décodées à partir de chaînes hexadécimales mais cela peut aussi bien être byte[]
.
Ainsi, le code peut être décomposé en ces étapes:
byte[] innerData
innerKey
et le message
dans le byte[] innerData
innerData
et stockez-le dans byte[] innerHash
byte[] data
outerKey
et innerHash
, le hachage précédemment calculé (à partir de # 3), dans le data
data
et stockez-le dans result
et retournez-le.Pour faire la copie d'octets, j'utilise la fonction Buffer.BlockCopy()
car elle est apparemment plus rapide que d'autres méthodes ( source ). Ces étapes peuvent ensuite être écrites en code comme ceci:
private static byte[] HashSHA(byte[] innerKey, byte[] outerKey, byte[] message)
{
var hash = new SHA256Managed();
// Compute the hash for the inner data first
byte[] innerData = new byte[innerKey.Length + message.Length];
Buffer.BlockCopy(innerKey, 0, innerData, 0, innerKey.Length);
Buffer.BlockCopy(message, 0, innerData, innerKey.Length, message.Length);
byte[] innerHash = hash.ComputeHash(innerData);
// Compute the entire hash
byte[] data = new byte[outerKey.Length + innerHash.Length];
Buffer.BlockCopy(outerKey, 0, data, 0, outerKey.Length);
Buffer.BlockCopy(innerHash, 0, data, outerKey.Length, innerHash.Length);
byte[] result = hash.ComputeHash(data);
return result;
}
Avant d'arriver à la fonction de hachage hexadécimal, vous avez besoin de quelques fonctions pour vous aider à convertir entre les choses comme indiqué dans l'aperçu.
string
-> byte[]
Le codage de chaîne suppose que le texte est clair ASCII et semble fonctionner (pour l'instant). Cependant, si vous devez coder avec des symboles fantaisistes, vous devrez probablement utiliser UTF8 à la place. Si c'est le cas, puis désactivez ASCIIEncoding
avec UTF8Encoding
ou tout autre encodage que vous utilisez.
private static byte[] StringEncode(string text)
{
var encoding = new ASCIIEncoding();
return encoding.GetBytes(text);
}
byte[]
-> hex string
Tout cela ne fait que prendre un tableau d'octets et le transformer en une chaîne hexadécimale minuscule. Assez simple.
private static string HashEncode(byte[] hash)
{
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
string
-> byte[]
Enfin, la conversion d'une chaîne hexadécimale en un tableau d'octets. Cela vient de la réponse de @ bobince donc ce n'est pas la mienne. Donner du crédit là où le crédit est dû.
private static byte[] HexDecode(string hex)
{
var bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = byte.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber);
}
return bytes;
}
Comme indiqué précédemment, ce sont les fonctions d'assistance qui fonctionnent avec les fonctions de hachage avec des données hexadécimales et des chaînes. Ils sont assez explicites:
private static string HashHMACHex(string keyHex, string message)
{
byte[] hash = HashHMAC(HexDecode(keyHex), StringEncode(message));
return HashEncode(hash);
}
private static string HashSHAHex(string innerKeyHex, string outerKeyHex, string message)
{
byte[] hash = HashSHA(HexDecode(innerKeyHex), HexDecode(outerKeyHex), StringEncode(message));
return HashEncode(hash);
}
Eh bien, pour regrouper toutes les fonctions, voici un programme de console qui appellera les fonctions pour montrer qu'elles fonctionnent réellement correctement.
static void Main(string[] args)
{
string message = "amount=100¤cy=EUR";
string expectedHex = "b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905";
Console.WriteLine("Ref : " + expectedHex);
// Test out the HMAC hash method
string key = "57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66";
string hashHMACHex = HashHMACHex(key, message);
Console.WriteLine("HMAC: " + hashHMACHex);
// Test out the SHA hash method
string innerKey = "61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950";
string outerKey = "0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a";
string hashSHAHex = HashSHAHex(innerKey, outerKey, message);
Console.WriteLine("SHA : " + hashSHAHex);
Console.ReadLine();
}
Si tout s'est déroulé correctement et s'est déroulé sans erreur, vous devriez obtenir la sortie suivante montrant que tous les hachages sont corrects (ref
est le hachage attendu):
Ref : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
HMAC: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
SHA : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
Enfin, juste pour vous assurer que tout a fonctionné, le code peut être trouvé à l'adresse suivante:
http://Pastebin.com/xAAuZrJX
Voici une méthode d'extension de chaîne pour obtenir un jeton HMAC assez standard SHA 256 pour une chaîne donnée:
tilisation:
myMessageString.HmacSha256Digest(mySecret)
méthode d'extension de chaîne:
public static string HmacSha256Digest(this string message, string secret)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
}
Vous pouvez utiliser cette méthode pour HMACSHA256.
string key = "your key";
string message = "your message";
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(key);
HMACSHA256 hmacsha256 = new HMACSHA256(keyByte);
byte[] messageBytes = encoding.GetBytes(message);
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return ByteToString(hashmessage);
Voici la méthode ByteToString:
public static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
Un SHA hachage est calculé sur une séquence d'octets. Les octets sont un type de données profondément différent des caractères. Vous ne devez pas utiliser des chaînes de caractères pour stocker des données binaires telles que des hachages.
sb.Append (Convert.ToString (Convert.ToChar (Int32.Parse (hex.Substring (i, 2) ...
Cela crée une chaîne de caractères en lisant chaque octet codé et en le transformant en un caractère du même numéro de point de code Unicode. Cela équivaut à décoder les octets 0 à 255 en utilisant le codage ISO-8859-1 (Latin1), en raison de la propriété de ce codage de faire correspondre les 256 premiers points de code en Unicode.
var enc = Encoding.Default; [...] baSalt = enc.GetBytes (sel);
byte [] sha256Bytes = Encoding.Default.GetBytes (entrée);
Ces deux convertissent les caractères en octets à l'aide du codage par défaut du système. Cet encodage varie selon les installations, mais il ne sera jamais ISO-8859-1 - même la page de codes similaire d'Europe occidentale 1252 a des caractères différents dans la plage 0x80-0x9F.
Par conséquent, le tableau d'octets que vous utilisez ne contient pas les octets impliqués par les exemples de séquences hexadécimales. Une solution bon marché consisterait à utiliser Encoding.GetEncoding("ISO-8859-1")
au lieu de l'encodage par défaut, mais vous devriez vraiment utiliser un tableau d'octets pour stocker les données en premier lieu au lieu d'une chaîne, par exemple:
byte[] key= new byte[] { 0x57, 0x61, 0x7b, 0x5d, 0x23, 0x49, ... };
et passez-le directement dans ComputeHash
.
Si vous devez initialiser des données à partir d'une chaîne hexadécimale, analysez-les directement dans un tableau d'octets, par exemple:
private static byte[] HexDecode(string hex) {
var bytes= new byte[hex.Length/2];
for (int i= 0; i<bytes.Length; i++) {
bytes[i]= byte.Parse(hex.Substring(i*2, 2), NumberStyles.HexNumber);
}
return bytes;
}
Je réalise que la question a reçu une réponse, mais je poste ceci au cas où d'autres en auraient besoin. Voici un extrait de code créé par le fournisseur de paiement (DIBS):
/**
* calculateMac
* Calculates the MAC key from a Dictionary<string, string> and a secret key
* @param params_dict The Dictionary<string, string> object containing all keys and their values for MAC calculation
* @param K_hexEnc String containing the hex encoded secret key from DIBS Admin
* @return String containig the hex encoded MAC key calculated
**/
public static string calculateMac(Dictionary<string, string> paramsDict, string kHexEnc)
{
//Create the message for MAC calculation sorted by the key
var keys = paramsDict.Keys.ToList();
keys.Sort();
var msg = "";
foreach (var key in keys)
{
if (key != keys[0]) msg += "&";
msg += key + "=" + paramsDict[key];
}
//Decoding the secret Hex encoded key and getting the bytes for MAC calculation
var kBytes = new byte[kHexEnc.Length / 2];
for (var i = 0; i < kBytes.Length; i++)
{
kBytes[i] = byte.Parse(kHexEnc.Substring(i * 2, 2), NumberStyles.HexNumber);
}
//Getting bytes from message
var msgBytes = Encoding.Default.GetBytes(msg);
//Calculate MAC key
var hash = new HMACSHA256(kBytes);
var macBytes = hash.ComputeHash(msgBytes);
var mac = BitConverter.ToString(macBytes).Replace("-", "").ToLower();
return mac;
}
Merci d'avoir économisé mon temps.
request.Method = "GET";
string signature = "";
string strtime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ssZ");
string secret = "xxxx";
string message = "sellerid:email:" + strtime;
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
var hash = new HMACSHA256(keyByte);
byte[] signature1 = hash.ComputeHash(messageBytes);
signature = BitConverter.ToString(signature1).Replace("-", "").ToLower();
}
request.Headers.Add("authorization", "HMAC-SHA256" + " " +
"[email protected],timestamp=" + strtime + ",signature=" + signature);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;