web-dev-qa-db-fra.com

Calcul de HMACSHA256 à l'aide de c # pour faire correspondre l'exemple de fournisseur de paiement

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.

Méthode 1

key = 57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66
message = "amount=100&currency=EUR"
MAC = HMAC-SHA256( hexDecode(key), message )
result = b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905

Méthode 2

message = "amount=100&currency=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&currency=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/

44
Kjensen

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.

Présentation

Le programme peut être organisé en trois sections:

  1. Fonctions de hachage - ce sont les fonctions réelles qui calculent les hachages en utilisant byte[] Pour la saisie
  2. Assistants d'encodage - ils sont utilisés avec les fonctions hexadécimales de hachage (# 3) et aident à convertir les éléments suivants:
    • string -> byte[]
    • byte[] -> hex string
    • hex string -> byte[] (merci @bobince!)
  3. Fonctions hexadécimales de hachage - ce sont des fonctions d'aide pour que vous puissiez utiliser les fonctions de hachage (# 1) en utilisant à la place une chaîne hexadécimale en entrée. Ceux-ci utilisent les assistants d'encodage (# 2) pour ce faire.

Code

0. Utilisation d'instructions

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;

1. Fonctions de hachage

HMAC-SHA256 (méthode 1)

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

SHA256 (méthode 2)

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 ) )

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:

  1. Créez le tampon pour les données internes et stockez-le dans byte[] innerData
  2. Copiez le innerKey et le message dans le byte[] innerData
  3. Maintenant, calculez le hachage SHA256 de innerData et stockez-le dans byte[] innerHash
  4. Pour le hachage final et entier, créez un tampon pour celui-ci dans byte[] data
  5. Copiez le outerKey et innerHash, le hachage précédemment calculé (à partir de # 3), dans le data
  6. Calculez le hachage final de 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;
}

2. Fonctions d'assistance

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

hex 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;
}

3. Fonctions hexagonales de hachage

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:

Hachage hexadécimal pour HMAC

private static string HashHMACHex(string keyHex, string message)
{
    byte[] hash = HashHMAC(HexDecode(keyHex), StringEncode(message));
    return HashEncode(hash);
}

Hachage hexagonal pour SHA

private static string HashSHAHex(string innerKeyHex, string outerKeyHex, string message)
{
    byte[] hash = HashSHA(HexDecode(innerKeyHex), HexDecode(outerKeyHex), StringEncode(message));
    return HashEncode(hash);
}

4. Test de la console

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&currency=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

Conclusion

Enfin, juste pour vous assurer que tout a fonctionné, le code peut être trouvé à l'adresse suivante:
http://Pastebin.com/xAAuZrJX

118
Josh Bowden

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

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);
    }
5
Savas Adar

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;
}
1
bobince

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

http://tech.dibspayment.com/DX/Hosted/HMAC

1
Liknes

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;
1
Ganesh