Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

Hoe HMAC-SHA512 hash maken in Windows Phone 8

Pagina: 1
Acties:

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Ik zit met een probleem. Ik heb hem al op stackoverflow gepost, maar eigenlijk gaat het iets te diep voor SO, de enige topics die daar de laatste tijd nog beantwoord worden zijn de meest simpele vraagjes. Daarom even een copypaste, ik had niet heel erg veel trek het over te typen:

I'm working with the Kraken API and to query the private methods I need to send a hash as a parameter. This is what their documentation says:
Public methods can use either GET or POST.

Private methods must use POST and be set up as follows:

HTTP header:

API-Key = API key
API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key

POST data:

nonce = always increasing unsigned 64 bit integer
otp = two-factor password (if two-factor enabled, otherwise not required)

Note: There is no way to reset the nonce to a lower value so be sure to use a nonce generation method that won't generate numbers less than the previous nonce. A persistent counter or the current time in hundredths of a second precision or higher is suggested.
They also have a PHP/Node.JS/Python example that creates the API-Sign hash. I am trying to port this code to C# for Windows Phone 8, but I encountered a big problem: the HMACSHA512 class isn't available for Windows Phone. I tried searching for alternatives that can create a HMAC-SHA512 hash, but couldn't find much. HashLib isn't available for Windows Phone. CryptSharp is, but I can't figure out how to add both a message and a password like PHP's hash_hmac() function allows. I also went searching for the algorithm/pseudo code for the HMAC-SHA512 algorithm to implement my own class but strangely enough I couldn't find it (does it have another name?).

Long story short, I need to convert this code in a Windows Phone 8 compatible piece of code that yields the same result:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if(!isset($request['nonce'])) {
    // generate a 64 bit nonce using a timestamp at microsecond resolution
    // string functions are used to avoid problems on 32 bit systems
    $nonce = explode(' ', microtime());
    $request['nonce'] = $nonce[1] . str_pad(substr($nonce[0], 2, 6), 6, '0');
}

// build the POST data string
$postdata = http_build_query($request, '', '&');

// set API key and sign the message
$path = '/' . $this->version . '/private/' . $method;
$sign = hash_hmac('sha512', $path . hash('sha256', $request['nonce'] . $postdata, true), base64_decode($this->secret), true);
$headers = array(
    'API-Key: ' . $this->key,
    'API-Sign: ' . base64_encode($sign)
);


The first part (till $sign = ...) looks pretty straightforward:

C#:
1
2
3
4
5
6
7
long nonce = DateTime.UtcNow.Ticks;

string postData = "nonce=" + nonce;
if (!string.IsNullOrEmpty(otp))
{
    postData += "&otp=" + otp;
}


But when I get to the cryptography part, I get stuck due to the lack of libraries.

  • OnTracK
  • Registratie: Oktober 2002
  • Nu online
Onderaan dit topic staat dat je HashLib wel kan gebruiken door alle "BuildIn"-classes te verwijderen.
MSDN: HMACSHA512 with Windows Phone 8

Wat dat betekend weet ik niet echt...

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
OnTracK schreef op zondag 05 januari 2014 @ 00:03:
Onderaan dit topic staat dat je HashLib wel kan gebruiken door alle "BuildIn"-classes te verwijderen.
MSDN: HMACSHA512 with Windows Phone 8

Wat dat betekend weet ik niet echt...
Zit ik nu te proberen, ik zie 2 classes met "buildin" in de naam. Als ik die verwijder heb ik een probleem, aangezien het base classes zijn waar andere classes van afhangen. Zonder die classes werkt het niet.

  • Ghehe
  • Registratie: April 2011
  • Laatst online: 13:59

Ghehe

400 pound hacker

Hoe ik het voorlaatste bericht ("I pulled out all of the "BuildIn" (lees gewoon: build-in) functionality which referenced System.Security.Cryptography") interpreteer is dat ie uit HashLib alle referenties naar System.Security.Cryptography eruit heeft gehaald en mogelijk dan wat andere zooi in de plaats heeft gezet.

  • xos
  • Registratie: Januari 2002
  • Laatst online: 22-11 09:52

xos

In principe is het HMAC algoritme niet veel meer dan een manier om gegeven wat data, key en een hash functie een authenticatie code te berekenen. Het algoritme is triviaal en kan je zelf eenvoudig implementeren.

Het probleem wat je dan mogelijk nog hebt is de sha512 hash. Uit je post kan ik niet halen of deze hash functie in het framework zit. Het lijkt erop van niet, op deze link is iemand met hetzelfde probleem en een oplossing.

Wat ik dus zou doen is zolang het framework geen support heeft dit zelf implementeren en een onderhouds taakje aanmaken om dit te vervangen als Microsoft support heeft toegevoegd aan het framework.

Geeft de Microsoft HMAC implementatie de mogelijkheid om een hash callback mee te geven? In dat geval zou je alleen de sha512 maar hoeven te implementeren. Het HMAC algoritme blijft hetzelfde, ongeacht het gekozen hash algoritme.

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Thanks, ik weet niet echt hoe het allemaal werkt, maar ik kreeg ook het idee dat HMAC een apart algoritme is dat los staat van het daarin gebruikte hashalgoritme (MD5, SHA256, SHA512, etc.). Ik ben dus aan de slag gegaan om aan de hand van die pseudocode mijn eigen C#-implementatie te maken, die ziet er nu zo uit:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private string CreateHMACSHA512(string key, string message)
{
    const int blocksize = 1024;

    if (key.Length > blocksize)
    {
        key = Crypter.Sha512.Crypt(key);
    }
    if (key.Length < blocksize)
    {
        key = key + (0x00 * (blocksize - key.Length));
    }

    int outerKeyPad = 0x5c * blocksize;
    int innerKeyPad = 0x36 * blocksize;

    return Crypter.Sha512.Crypt(outerKeyPad + Crypter.Sha512.Crypt(innerKeyPad + message));
}


Waar ik alleen niet uit kwam was dit stukje:

code:
1
2
    o_key_pad = [0x5c * blocksize] &#8853; key // Where blocksize is that of the underlying hash function
    i_key_pad = [0x36 * blocksize] &#8853; key // Where &#8853; is exclusive or (XOR)


Ik moet dus "0x5c * blocksize" OF de key gebruiken. Waarom? In mijn code gebruik ik "0x5c * blocksize", maar dan gebruik ik "key" nergens in m'n code.

Als ik die HMAC werkend heb kan ik denk ik verder aan de slag met de SHA-512 implementatie van CryptSharp.

  • Ghehe
  • Registratie: April 2011
  • Laatst online: 13:59

Ghehe

400 pound hacker

Avalaxy schreef op zondag 05 januari 2014 @ 14:30:
Ik moet dus "0x5c * blocksize" OF de key gebruiken.
Zoals in de comments staat, bedoelen ze een bitwise XOR denk ik.

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Ghehe schreef op zondag 05 januari 2014 @ 14:35:
[...]

Zoals in de comments staat, bedoelen ze een bitwise XOR denk ik.
Klopt, die had ik ook geprobeerd maar dat matcht niet aangezien m'n "0x5c * blocksize" een int32 is en "key" is een string. Moet ik daarvoor de int converteren naar een string ofzo :?

  • Ghehe
  • Registratie: April 2011
  • Laatst online: 13:59

Ghehe

400 pound hacker

Avalaxy schreef op zondag 05 januari 2014 @ 15:02:
[...]


Klopt, die had ik ook geprobeerd maar dat matcht niet aangezien m'n "0x5c * blocksize" een int32 is en "key" is een string. Moet ik daarvoor de int converteren naar een string ofzo :?
Nee omgekeerd. Je string is een hexadecimale voorstelling van een (heel groot) getal.

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Kan je niet gewoon naar de source van deze class kijken?

MSDN: HMACSHA512 Class (System.Security.Cryptography)

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Ghehe schreef op zondag 05 januari 2014 @ 15:10:
[...]


Nee omgekeerd. Je string is een hexadecimale voorstelling van een (heel groot) getal.
Heb je misschien een voorbeeld? Ik heb om te testen even als key "test1" en als message "test2" ingevuld, maar "test1" parsen naar een getal lukt me niet. Ik heb het bv. geprobeerd met:

C#:
1
int.Parse(key, NumberStyles.HexNumber)


Maar dat gaat uiteraard niet omdat key geen hexnummer is.
Ben ik nu aan het proberen, dotPeek kan hem niet vinden.

  • xos
  • Registratie: Januari 2002
  • Laatst online: 22-11 09:52

xos

Avalaxy schreef op zondag 05 januari 2014 @ 14:30:
Thanks, ik weet niet echt hoe het allemaal werkt, maar ik kreeg ook het idee dat HMAC een apart algoritme is dat los staat van het daarin gebruikte hashalgoritme (MD5, SHA256, SHA512, etc.). Ik ben dus aan de slag gegaan om aan de hand van die pseudocode mijn eigen C#-implementatie te maken, die ziet er nu zo uit:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private string CreateHMACSHA512(string key, string message)
{
    const int blocksize = 1024;

    if (key.Length > blocksize)
    {
        key = Crypter.Sha512.Crypt(key);
    }
    if (key.Length < blocksize)
    {
        key = key + (0x00 * (blocksize - key.Length));
    }

    int outerKeyPad = 0x5c * blocksize;
    int innerKeyPad = 0x36 * blocksize;

    return Crypter.Sha512.Crypt(outerKeyPad + Crypter.Sha512.Crypt(innerKeyPad + message));
}


Waar ik alleen niet uit kwam was dit stukje:

code:
1
2
    o_key_pad = \[0x5c * blocksize] &#8853; key // Where blocksize is that of the underlying hash function
    i_key_pad = \[0x36 * blocksize] &#8853; key // Where &#8853; is exclusive or (XOR)


Ik moet dus "0x5c * blocksize" OF de key gebruiken. Waarom? In mijn code gebruik ik "0x5c * blocksize", maar dan gebruik ik "key" nergens in m'n code.

Als ik die HMAC werkend heb kan ik denk ik verder aan de slag met de SHA-512 implementatie van CryptSharp.
Paar dingen die opvallen:
  • de meegegeven key is een string. Dat doet vermoeden dat de key in een encoded vorm wordt doorgegeven. Een key is vaak een byte array.
  • de key lengte checks vinden plaats met verschillende eenheden. Blocksize is in bits, en Length geeft je het aantal bytes terug. Druk blocksize in bytes uit ipv in bits.
  • Je interpretatie van de pseudo code is niet helemaal juist
De notatie [0x5c * blocksize] wordt een byte array mee bedoelt. Bv voor een blocksize van 3 levert dat de volgende array op [0x5c, 0x5c, 0x5c]. Met die + wordt in dit geval concatenatie bedoelt, en niet optellen.

De aanpak die staat beschreven op het linkje om sha512 toe te voegen kan je ook gebruiken voor het hmac algoritme. Het interesante bestand kan je hier vinden in het mono repository.

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
xos schreef op zondag 05 januari 2014 @ 15:31:
[...]

Paar dingen die opvallen:
  • de meegegeven key is een string. Dat doet vermoeden dat de key in een encoded vorm wordt doorgegeven. Een key is vaak een byte array.
  • de key lengte checks vinden plaats met verschillende eenheden. Blocksize is in bits, en Length geeft je het aantal bytes terug. Druk blocksize in bytes uit ipv in bits.
  • Je interpretatie van de pseudo code is niet helemaal juist
De notatie \[0x5c * blocksize] wordt een byte array mee bedoelt. Bv voor een blocksize van 3 levert dat de volgende array op [0x5c, 0x5c, 0x5c]. Met die + wordt in dit geval concatenatie bedoelt, en niet optellen.
Ohhh :F Ik zit alles fout te doen. Ik ga weer door m'n code schoffelen :(

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Avalaxy schreef op zondag 05 januari 2014 @ 15:28:
[...]

[...]

Ben ik nu aan het proberen, dotPeek kan hem niet vinden.
Mono heeft hem wel: https://github.com/mono/m...ryptography/HMACSHA512.cs

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Ik geef het op, ik kom er echt niet uit 8)7 Misschien ben ik dom, maar 'simpel' te implementeren is het bepaald niet imho.

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Zoiets? Werkt wel, is niet optimaal :P. Even in een paar minuutjes in elkaar gehackt. Dus geen garanties etc.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static void Main(string[] args)
        {
            byte[] key = Encoding.ASCII.GetBytes("key");
            byte[] message = Encoding.ASCII.GetBytes("The quick brown fox jumps over the lazy dog");

            var hmacAlgorithm = HMAC.Create("HMACSHA512");
            hmacAlgorithm.Key = key;

            byte[] validHmac = hmacAlgorithm.ComputeHash(message);

            var hashAlgorithm = SHA512.Create();

            const int blockSize = 128; // Bytes (1024 bits)

            if (key.Length > blockSize)
            {
                key = hashAlgorithm.ComputeHash(key);
            }

            byte[] paddedKey = new byte[blockSize];

            if (key.Length < blockSize)
            {
                Array.Copy(key, 0, paddedKey, 0, key.Length);
            }

            byte[] o_key_pad = new byte[blockSize];
            byte[] i_key_pad = new byte[blockSize];

            for (int i = 0; i < blockSize; ++i)
            {
                o_key_pad[i] = (byte)(paddedKey[i] ^ (0x5c));
                i_key_pad[i] = (byte)(paddedKey[i] ^ (0x36));
            }

            // Dit voelt zo vies :(
            byte[] internalHash = hashAlgorithm.ComputeHash(i_key_pad.Concat(message).ToArray());
            byte[] result = hashAlgorithm.ComputeHash(o_key_pad.Concat(internalHash).ToArray());

            Console.WriteLine(validHmac.SequenceEqual(result));
        }

[ Voor 4% gewijzigd door HMS op 05-01-2014 20:27 ]


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Werkt niet:
  • WP heeft geen Encoding.ASCII (wel Encoding.UTF8 en Encoding.Unicode), valt wel omheen te werken.
  • HMAC heeft geen static methodes. HMAC.Create() wordt dus niet herkend.
  • Er wordt dus geen object aangemaakt, dus ook zaken als ComputerHash() bestaan niet.
  • Je doet SHA512.Create() maar ook de SHA512 class bestaat niet (zoals eerder genoemd).
Ik dacht overigens dat CryptSharp wel netjes SHA512 kon genereren, maar niet dus. Dit stukje code:

C#:
1
2
3
4
string expected = "b16ed7d24b3ecbd4164dcdad374e08c0ab7518aa07f9d3683f34c2b3c67a15830268cb4a56c1ff6f54c8e54a795f5b87c08668b51f82d0093f7baee7d2981181";
string actual = Crypter.Sha512.Crypt("test1");

Assert.AreEqual(expected, actual, true);


Geeft dit resultaat:

code:
1
2
3
4
5
Additional information: Assert.AreEqual failed. 

Expected:<b16ed7d24b3ecbd4164dcdad374e08c0ab7518aa07f9d3683f34c2b3c67a15830268cb4a56c1ff6f54c8e54a795f5b87c08668b51f82d0093f7baee7d2981181>. 

Actual:<$6$uCDmNlGi.pRGJqo2$djohU23xPHS/fn57u5nhAWiPQ5adl72UIvF7tAURjQCEqvlJTxLCbp4XjZO815WhykR22HutV.d4hZL4mZaMj1>.


Dus niet alleen het HMAC stuk is problematisch, ik moet ook eerst nog eens een valide SHA512 hash zien te genereren.

[ Voor 53% gewijzigd door Avalaxy op 05-01-2014 21:11 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Heb je BouncyCastle geprobeerd?

Op Google vind ik:
https://w8bouncycastle.codeplex.com/
https://github.com/smle/Bouncy-Castle-WP8
http://www.bouncycastle.org/csharp/

[ Voor 40% gewijzigd door HMS op 06-01-2014 00:22 ]


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Ik heb die Bouncy Castle WP8 wel in het project gekregen _/-\o_ Ik ben er alleen nog niet uit of de resultaten wel goed zijn. Volgens de unit test wel, maar als ik bv. op https://quickhash.com/hash-sha512-online invoer als input "Hi There" en onderin als HMAC key "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" dan zegt de site dat m'n output "e630a5975d9cfe99c10eecc5e226af922c481ba4a935f7d8a46dd1186a55a52bdc0b742ebad75cb9f59dfb16f7fb49cf84d1c2e3e09946bd86bc60a02cf965b1" moet zijn.

Volgens de unit test is de digest (output?) "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854".

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Volgens mij is de key zoals je die uit de RFC haalt niet een string maar een serie bytes met waarde 0x0b.

Probeer anders deze test vector:
Input: The quick brown fox jumps over the lazy dog
Key: key
MAC: b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791
a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a

[ Voor 62% gewijzigd door HMS op 06-01-2014 03:41 ]


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Ok ik ben een stapje verder in de puzzel, alleen krijg ik "EGeneral:Invalid arguments" terug, er zit dus nog iets fout. Dit is de PHP code van hun voorbeeld die zou moeten werken (met waardes erbij die ik krijg als ik hem even run):

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$nonce = 1234567890;
$secret = "test123";
$request['nonce'] = $nonce;

$postdata = http_build_query($request, '', '&');
echo "Postdata: " . $postdata . "<br />";
// Postdata: nonce=1234567890

$path = '/0/private/Balance';
$data = $path . hash('sha256', $nonce . $postdata, true);
echo "Data: " . $data . "<br />";
// Data: /0/private/Balance&#65533;&#65533;&#65533;4&#65533;&#65533;.&#65533;*&#65533;&#65533;&#65533;J&#65533;M&#65533;&#65533;&#65533;&#65533;4&#65533;&#65533;+&#65533;&#65533;DX

$key = base64_decode($secret);
echo "Base64: " . $key . "<br />";
// Base64: &#65533;&#65533;-&#65533;m

$sign = hash_hmac('sha512', $data, $key, true);
echo "Sign: " . $sign . "<br />";
// Sign: &#65533;&#65533;t&#65533;l&#65533;&#65533;&#65533;&#65533;IL&#65533;P&#65533;&#1593;&#65533;&#65533;ve&#65533;&#65533;&#65533;=4&#65533;&#65533;&#65533;r`&#65533;&#65533;&#65533;wd&#65533;&#65533;X&#65533;&#65533; o&#65533;Ka&#65533;7&#65533;&#65533;LK&#65533;&#65533;&#65533;&#65533;&#65533;


Hier is mijn C# code:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
long nonce = 1234567890;
apiSecret = "test123";

string postData = "nonce=" + nonce;
// nonce=1234567890

byte[] sha256Hash = CryptoHelper.Sha256Hash(nonce + postData);
string sha256AsString = Encoding.UTF8.GetString(sha256Hash, 0, sha256Hash.Length);\
// &#65533;&#65533;&#65533;4&#65533;&#65533;.&#65533;*&#65533;&#65533;&#65533;J&#65533;M&#65533;&#65533;&#65533;&#65533;4&#65533;&#65533;+&#65533;&#65533;DX

string message = uriPath + sha256AsString;
// /0/private/Balance&#65533;&#65533;&#65533;4&#65533;&#65533;.&#65533;*&#65533;&#65533;&#65533;J&#65533;M&#65533;&#65533;&#65533;&#65533;4&#65533;&#65533;+&#65533;&#65533;DX

string key = Convert.ToBase64String(Encoding.UTF8.GetBytes(apiSecret));
// dGVzdDEyMw==

HMac hmac = new HMac(new Sha512Digest());
byte[] resBuf = new byte[hmac.GetMacSize()];

byte[] m = StringToByteArray(message);
hmac.Init(new KeyParameter(Hex.Decode(key)));
hmac.BlockUpdate(m, 0, m.Length);
hmac.DoFinal(resBuf, 0);

string sign = Encoding.UTF8.GetString(resBuf, 0, resBuf.Length);
// [&#406;hFR&#421;&#65533;b&#65533;T&#65533;€&#65533;2"I&#65533;.#&#65533;2&#65533;&#65533;&#65533;&#65533;
&#65533;Kur&#65533;&#65533;"&#65533;+=W^1&#65533;b2&#65533;&#65533;`N&#65533;&#65533;&#65533;&#65533;&#65533;)


Alleen jammer van alle vage tekens die zowel GoT als m'n online draai-hier-je-PHP-zooi-maar-tooltjes hebben.

De reden dat ik de SHA256 en HMAC een byte[] laat returnen is omdat in het PHP-voorbeeld dmv de 'true' parameter de output op 'raw' wordt gezet. Ik weet niet zeker wat dat is, maar ik denk dat het overeenkomt met een byte array returnen?

Zowel $nonce als $data komt nog overeen met de C# output. Vanaf het Base64 stukje gaat het fout.

Oh en wat verder nog verschilt is dat zij de parameters zo meegeven met hun request:

PHP:
1
2
3
4
$headers = array(
    'API-Key: ' . $key,
    'API-Sign: ' . base64_encode($sign)
);


Ik doe dat zo:

C#:
1
2
_httpClient.DefaultRequestHeaders.Add("API-Key", apiKey);
_httpClient.DefaultRequestHeaders.Add("API-Sign", Base64Encode(Encoding.UTF8.GetString(apiSign, 0, apiSign.Length)));


Maargoed, nu slapen. Morgen weer verder.
HMS schreef op maandag 06 januari 2014 @ 03:36:
Volgens mij is de key zoals je die uit de RFC haalt niet een string maar een serie bytes met waarde 0x0b.

Probeer anders deze test vector:
Input: The quick brown fox jumps over the lazy dog
Key: key
MAC: b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791
a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a
Thanks, morgen nog even naar kijken.

[ Voor 5% gewijzigd door Avalaxy op 06-01-2014 04:37 ]


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Kun je niet gewoon in de sources van bijvoorbeeld Mono kijken?

https://github.com/mosa/M...rity.Cryptography/HMAC.cs

edit:
O, ik zie dat je dat gedeelte al had opgelost :)

[ Voor 14% gewijzigd door Woy op 06-01-2014 08:16 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Ik zie al wel dingen dat niet kloppen (aangenomen copy / paste)

PHP:
1
$key = base64_decode($secret);

en
C#:
1
string key = Convert.ToBase64String(Encoding.UTF8.GetBytes(apiSecret)); 


Let op encode / decode verschil.

C#:
1
hmac.Init(new KeyParameter(Hex.Decode(key))); 


Ik naam aan dat dit een decode doet van Hex -> Bytes. Terwijl Key met Base64 geencode is.

  • frG
  • Registratie: Augustus 2004
  • Laatst online: 20-11 20:03

frG

Je weet ook zeker dat UTF8 klopt en het niet ASCII moet zijn?

Bij de cryptsy API had ik dat probleem namelijk.

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Het base64 probleempje is opgelost. Ik deed het verkeerd. Het moest niet:

C#:
1
string key = Convert.ToBase64String(Encoding.UTF8.GetBytes(apiSecret));


zijn maar:

C#:
1
2
byte[] keyByteArray = Convert.FromBase64String(apiSecret);
string key = Encoding.UTF8.GetString(keyByteArray, 0, keyByteArray.Length);


Alleen geeft ie nu een "Index was outside the bounds of the array." error op:

C#:
1
hmac.Init(new KeyParameter(Hex.Decode(key)));


Wat gek is, aangezien dat stukje gewoon uit het voorbeeld van Bouncy Castle komt.

Edit: het Hex.Decode stukje weghalen en gewoon m'n byte array erin jassen lost dat probleem op. Nu even kijken of m'n HMAC-SHA512 hash goed is.

Edit2: nope. Error/invalid arguments.

Edit3: ok wtf, mijn code produceert gewoon de verkeerde HMAC-SHA512 ondanks dat ik er exact dezelfde parameters instop als bij PHP.

C#:
1
2
3
4
5
6
7
HMac hmac = new HMac(new Sha512Digest());
byte[] resBuf = new byte[hmac.GetMacSize()];

byte[] messageBytes = StringToByteArray("blaat12345");
hmac.Init(new KeyParameter(StringToByteArray("supermooiekey123")));
hmac.BlockUpdate(messageBytes, 0, messageBytes.Length);
hmac.DoFinal(resBuf, 0);


!=

PHP:
1
echo hash_hmac("sha512", "blaat12345", "supermooiekey123", true);


:?

[ Voor 48% gewijzigd door Avalaxy op 06-01-2014 20:52 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

En welke is goed en welke is fout? Ik neem aan dat je het met een test vector doet waarvan je de uitkomst weet :P. Hier kunnen we niet zoveel mee.

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
De PHPversie is uiteraard goed, want die komt uit de documentatie van kraken. Verder is de invoer hetzelfde, dus hoezo kunnen we er niet veel mee? :)

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Dit werkt bij mij gewoon hoor.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    public class Class1
    {
        public string Test()
        {
            HMac hmac = new HMac(new Sha512Digest());
            byte[] resBuf = new byte[hmac.GetMacSize()];

            byte[] messageBytes = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
            hmac.Init(new KeyParameter(Encoding.UTF8.GetBytes("key")));
            hmac.BlockUpdate(messageBytes, 0, messageBytes.Length);
            hmac.DoFinal(resBuf, 0);

            // Meest inefficiente manier om bytes to hex te doen, maar wel lekker compact.
            string test = BitConverter.ToString(resBuf).Replace("-", "").ToLowerInvariant();

            return test;
        }

        [Test]
        public void InvokeTest()
        {
            var result = Test();

            Assert.That(result, Is.EqualTo("b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a"));
        }
    }


XML:
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="NUnit" version="2.6.3" targetFramework="win81" />
  <package id="WinRTBouncyCastle" version="0.1.1.1" targetFramework="win81" />
</packages>

[ Voor 13% gewijzigd door HMS op 07-01-2014 18:41 ]


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Dat klopt ook wel, maar het komt niet overeen met wat PHP genereert is wat ik zeg. Ik zal de API toch de juiste key moeten geven anders weigert ie hem.

Heeft waarschijnlijk te maken met die raw_output parameter van hash_hmac(). Als ik die op false zet krijg ik dezelfde output als met het stukje C#, alleen dat voorbeeld heeft raw_output op true staan. En hoe ik dan dezelfde output kan krijgen in C# weet ik niet.

[ Voor 51% gewijzigd door Avalaxy op 07-01-2014 19:04 ]


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Het enige wat die parameter doet zo te zien is de return value wel of niet converteren naar een hex string, dus regel 14 in het code voorbeeld van HMS.

De code om je hash te genereren lijkt dus gewoon te kloppen, alleen vervolgens output je hem niet op de juiste manier.

[ Voor 28% gewijzigd door Woy op 07-01-2014 19:33 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Oh is dat het. Ik dacht dat de gegenereerde hash verkeerd was. Nee als PHP de raw bytes returned dan is het natuurlijk niet vreemd dat er verschil in output zit :P.

In C# is de raw binary data gewoon de byte array (resBuf). Ik weet niet waarom het wenselijk is om raw binary data te versturen, base64 lijkt me handiger. Maar goed, ik ken de API niet.

Je bent in ieder geval al van geen SHA512 HMAC, naar het oplossen van het krijgen van de correcte output :P.

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
HMS schreef op dinsdag 07 januari 2014 @ 19:58:
Je bent in ieder geval al van geen SHA512 HMAC, naar het oplossen van het krijgen van de correcte output :P.
Idd :P Ik ga nu even prutsen hoe ik dit goed krijg.

PHP echo't dit met raw output:
8aOt˷�n߉�ں�ixE;h�o( epNj�� }~.j���r/����_DO��EAܸ;��
Als ik een byte[] return en hier een leesbare string van probeer te maken dan krijg ik nét een ander resultaat:
private static string AsciiToString(byte[] bytes)
{
return string.Concat(bytes.Select(b => b <= 0x7f ? (char)b : '?'));
}
string asciiSign = AsciiToString(apiSign);

// 8aOt˷�n߉�ں�ixE;h�o( epNj�� }~.j��r/����_DO��EAܸ;��
En:
string utf8Sign = Encoding.UTF8.GetString(apiSign, 0, apiSign.Length);

// 8aOt˷�n߉�ں�ixE;h�o( epNj�� }~.j��r/����_DO��EAܸ;��

[ Voor 66% gewijzigd door Avalaxy op 07-01-2014 20:23 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Kan je niet beter van byte naar char gaan? Daarnaast, gebruikt PHP ook UTF8 of gebruikt die ASCII voor de encoding? (Dat UTF8 ASCII compatible is wil niet zeggen dat het resultaat gelijk is :P)

[ Voor 76% gewijzigd door HMS op 07-01-2014 21:03 ]


  • OnTracK
  • Registratie: Oktober 2002
  • Nu online
PHP heeft niet echt een interne encoding, en gebruikt in zekere zin gewoon bytes. Hoe dat gerepresenteerd wordt is afhankelijk welke headers je meestuurt naar de browser.

Sowieso is het erg onwaarschijnlijk dat bij dit type hash er een klein verschil in de output zit. Hashes zijn hetzelfde of helemaal niet hetzelfde.

[ Voor 14% gewijzigd door OnTracK op 07-01-2014 21:02 ]

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Het gaat om een klein verschil tussen wat C# output als RAW en wat PHP output ;). De berekende hashes zijn gelijk, zoals we al eerder vast gesteld hebben :).

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Well... het gekke is dat als ik het met obvious nepdata test ("blaat12345" en "supermooiekey123") er zoals hierboven maar 1 karakter verschilt (zover ik kan zien, geen idee wat de vraagtekentjes voor characters zijn). Als ik m'n echte API secret invul, met een nonce van 1234567890 en vervolgens netjes de echte data in die HMAC-SHA512 stop (dus een SHA256 hash, etc.), dan krijg ik 2 hashes die wel degelijk heel anders zijn. Dan geeft PHP namelijk dit:
��Ԋ�R�A;��P�XHϴ���2�O3��d��kg��BТt,ʣ�]�/�r�G�“~ ���/�
En C# geeft:
??m}K3?Mu?$???P?????|v???-?}???Q'039hC??{??M???T/?hb?-???? // ascii
��m}K3�Mu�$�ĤP�����|v���-�}��Q'039hC��{��M���T/�hb�-���� // utf8
Ik weet zeker dat de SHA256 en dergelijken (gewoon alle input die de HMAC functie ingaat) correct is, dat heb ik net nog vergeleken. Ik weet ook dat de output van die HMAC functie in principe goed is. Alleen krijg ik het niet zo gepresenteerd als dat ze het doen in PHP.

[ Voor 11% gewijzigd door Avalaxy op 07-01-2014 21:19 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Probeer eens:

C#:
1
2
3
4
5
6
var sb = new StringBuilder();
foreach(byte b in refBuf) 
{
    sb.Append((char)b);
}
string hash = sb.ToString();

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
HMS schreef op dinsdag 07 januari 2014 @ 21:20:
Probeer eens:

C#:
1
2
3
4
5
6
var sb = new StringBuilder();
foreach(byte b in refBuf) 
{
    sb.Append((char)b);
}
string hash = sb.ToString();
Dat resulteert weliswaar in een hele mooie leesbare string maar niet in een match:
ý¬m}K3ÆMuù$ùĤP›ÉóÎí|vüþ¸-õ}íðŒQ'039hC¦¨{˜úMù‹ËT/ãhb†-‚„¼Ÿ // C# met jouw code
��Ԋ�R�A;��P�XHϴ���2�O3��d��kg��BТt,ʣ�]�/�r�G�“~ ���/� // PHP

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Ik zie hier dat de API-Sign als base64 geencode kan worden. Dus misschien is dit niet echt een probleem?

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Klopt, het moet een Base64 worden, maar ik kan niet zomaar een byte[] naar een base64 gooien, het moet eerst een string worden.

  • OnTracK
  • Registratie: Oktober 2002
  • Nu online
Dat lijkt me onwaarschijnlijk, aangezien je dingen die naar base64 gaan meestal niet als string kunt representeren (dat is de hele reden dat je base64 gebruikt). Heb je geen Convert.ToBase64String(byte[] ) method?

[ Voor 12% gewijzigd door OnTracK op 07-01-2014 22:18 ]

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Klopt, ik zat er naast :P Het kan wel :) Betekent helaas nog niet dat het matcht :(

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Probeer je nog steeds die string met onleesbare tekens te krijgen (a.k.a. het matcht niet) met base64? Want dat gaat niet gebeuren.

Wat gebeurd er als je API aanroept? Werkt het dan niet met je C# base64 encoded API-Sign?

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
HMS schreef op woensdag 08 januari 2014 @ 03:33:
Probeer je nog steeds die string met onleesbare tekens te krijgen (a.k.a. het matcht niet) met base64? Want dat gaat niet gebeuren.

Wat gebeurd er als je API aanroept? Werkt het dan niet met je C# base64 encoded API-Sign?
Geen idee wat je precies bedoelt maar het werkt niet nee :+

Dit is gewoon het probleem:

��Ԋ�R�A;��P�XHϴ���2�O3��d��kg��BТt,ʣ�]�/�r�G�“~ ���/�
��\0Ԋ�R�A;��P�XHϴ���2�O3��d��kg��BТt,ʣ\b�]�/�r�G�“~ ���/�

De volgende stukjes worden toegevoegd (zowel in UTF8 als ASCII):

\0
\b
(en nog 2 blokjes die ik hier niet zichtbaar krijg).

Weet iemand toevallig waar die vandaan komen? M'n hash lijkt me nu echt helemaal goed. Als ik 1 getalletje verander krijg ik een hele andere hash. Dat de 2 hashes zó dicht bij elkaar zitten lijkt me dus geen toeval. Meer een gevalletje: op de juiste manier naar een string omtoveren.

Even in Fiddler proberen en kijken wat die roept.

edit: wtf :o ik heb het in fiddler werkend:

Afbeeldingslocatie: http://i.imgur.com/FGdGLWp.png

Dat is met:

C#:
1
Convert.ToBase64String(apiSign);


over de API sign heen.

For the record: dit is hoe ik m'n request zelf uitvoer:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
long nonce = DateTime.UtcNow.Ticks;
string apiKey = "xxxxxxxxxxxxxxxxxxx";
string apiSecret = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
byte[] apiSign = CreateApiSign("/0/private/Balance", apiSecret, nonce);

Uri uri = new Uri("https://api.kraken.com/0/private/Balance");

_httpClient.DefaultRequestHeaders.Add("API-Key", apiKey);
_httpClient.DefaultRequestHeaders.Add("API-Sign", Convert.ToBase64String(apiSign));

var result = await _httpClient.PostAsync(uri, new StringContent("nonce=" + nonce));

result.EnsureSuccessStatusCode();
var jsonContent = await result.Content.ReadAsStringAsync();
return jsonContent;

[ Voor 25% gewijzigd door Avalaxy op 08-01-2014 04:04 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Nou, probleem opgelost dus 8) . Voorbeeld in PHP was niet het handigste om te volgen blijkt dus :P

edit: ik bedoelde ook gewoon die byte array naar base64 knallen en dan een request doen. Ik zag geen reden waarom dat niet zou werken, et voila :P .

[ Voor 43% gewijzigd door HMS op 08-01-2014 04:09 ]


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Nou niet echt opgelost :P Want ik moet het in C# werkend krijgen, de hele dag handmatig requests met fiddler doen gaat niet :+

Ik hou er overigens rekening mee dat het een bug is in de HttpClient voor WP8, die port is bugged aan alle kanten...

[ Voor 32% gewijzigd door Avalaxy op 08-01-2014 04:08 ]


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS


  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
Woei! *O* *O* *O* *O* *O* *O* *O* *O* *O* *O* *O* :)F :)F :)F :)F :)F :)F }:O }:O }:O }:O

Opgelost!

C#:
1
var result = await _httpClient.PostAsync(uri, new StringContent("nonce=" + nonce)); 


Vervangen door:

C#:
1
2
3
var postParameters = new List<KeyValuePair<string, string>>();
postParameters.Add(new KeyValuePair<string, string>("nonce", nonce.ToString()));
var result = await _httpClient.PostAsync(uri, new FormUrlEncodedContent(postParameters));


@boven: yup :)

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Holy shit! Wat een oplossing ook, die HttpClient lijkt zo wel heel gaar.

Nou jongens, allemaal in koor: Developers, developers, developers, developers.... :7

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 22-11 16:12
HMS schreef op woensdag 08 januari 2014 @ 04:27:
Holy shit! Wat een oplossing ook, die HttpClient lijkt zo wel heel gaar.

Nou jongens, allemaal in koor: Developers, developers, developers, developers.... :7
Vorige grote probleem met die HttpClient was dat ik altijd maar dezelfde resultaten terugkreeg van dat ding. Bleek hij zeer agressief te cachen (wat geen normaal gedrag is voor de .NET HttpClient die wél goed in elkaar zit). Was ook gewoon niet uit te zetten met een functie ofzo :X Uiteindelijk op moeten lossen door een expire header mee te sturen, én een unieke code (guid/timestamp) als parameter achter de URL te hangen :X
Pagina: 1