![]() |
DEC und HMAC-SHA-256
Hallo zusammen.
Ich bin auf der Suche nach einer Implementierung des HMAC-SHA-256 ![]() Grüße |
Re: DEC und HMAC-SHA-256
In den Wikipedia-Artikel ist ein super (sogar Pascal ähnlicher) Pseudocode:
Delphi-Quellcode:
Da brauchst nicht viel ändern. Anstatt der Funktion Hash verwendest einfach SHA256 aus dem DEC.
function hmac (key, message)
if (length(key) > blocksize) then key = hash(key) // keys longer than blocksize are shortened else if (length(key) < blocksize) then key = key ∥ zeroes(blocksize - length(key)) // keys shorter than blocksize are zero-padded end if opad = [0x5c * blocksize] ⊕ key // Where blocksize is that of the underlying hash function ipad = [0x36 * blocksize] ⊕ key // Where ⊕ is exclusive or (XOR) return hash(opad ∥ hash(ipad ∥ message)) // Where ∥ is concatenation end function |
Re: DEC und HMAC-SHA-256
Ganz so einfach ist es leider nicht. Das bitweise xor lässt sich nicht einfach auf Strings anwenden, das Padding muss man noch selbst implementieren (soweit bin ich hoffentlich) - Problem ist, dass ich keine Beispiele für gültige Ein-/Ausgaben gefunden habe, um zu testen, ob es denn überhaupt richtig ist.
Für alle die es ausprobieren oder mit basteln wollen, hier mal der gesamte Quelltext. Aufgerufen wird Button1Click(). Es geht um die Produktdatenbankabfrage bei Amazon, man muss sich dort kostenlos registrieren, um einen Schlüssel zu bekommen. Außerdem nutze ich das DEC und himXML.
Delphi-Quellcode:
Bin für jede Hilfe dankbar.
unit Main;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, InvokeRegistry, Rio, SOAPHTTPClient, AWSECommerceService, StdCtrls, himXML, DECUtil, DECCipher, DECHash, DECFmt; const AWSID = 'DIE_ID'; sAWSID = 'Die geheime ID'; sACCES_ID = 'Den Associate-Tag'; type TForm1 = class(TForm) HTTPRIO1: THTTPRIO; Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); procedure HTTPRIO1BeforeExecute(const MethodName: string; SOAPRequest: TStream); private function GenerateCurrentTimeStamp: string; function GenerateHMACSignature(Action, Timestamp: string): String; function concat(c: Char; l: Integer): String; function SimpleCryptString(const S, Key: string): string; { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var AWSPort: AWSECommerceServicePortType; AmazonPort: AWSECommerceServicePortType; body: ItemSearch; arr_request: Array_Of_ItemSearchRequest; aRequest: ItemSearchRequest; aResponse: ItemSearchResponse; i: Integer; j: Integer; begin AWSPort := GetAWSECommerceServicePortType(False,'',HTTPRIO1); body := ItemSearch.Create; body.AWSAccessKeyId := AWSID; body.SubscriptionId := sACCES_ID; body.AssociateTag := sACCES_ID; aRequest := ItemSearchrequest.Create; aRequest.SearchIndex := 'Video'; aRequest.Title := 'Matrix'; SetLength(arr_request, 1); arr_request[0] := aRequest; body.Request := arr_request; aResponse := AWSPort.ItemSearch(body); for i := 0 to Length(aResponse.Items) - 1 do for j := 0 to Length(aResponse.Items[i].Item) - 1 do begin Memo1.Lines.Add(aResponse.Items[i].Item[j].ItemAttributes.Title); end; end; function TForm1.GenerateCurrentTimeStamp(): string; var TimeStamp: string; y,mon,d,h,m,s,ms: Word; begin DecodeDate(Now,y,mon,d); DecodeTime(Now,h,m,s,ms); // 2009-02-16T17:39:51.000Z Result := Format('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.000Z',[y,mon,d,h,m,s]); end; function TForm1.concat(c: Char; l: Integer): String; var I: Integer; begin Result := ''; for I := 0 to l-1 do Result := '0' + Result; end; function TForm1.SimpleCryptString(const S, Key: string): string; var i, j: Integer; C: Byte; P: PByte; begin SetLength(Result, Length(S)); P := PByte(Result); j := 1; for i := 1 to Length(S) do begin C := Ord(S[i]); C := C xor Ord(Key[j]); P^ := C; Inc(P); Inc(j); if j > Length(Key) then j := 1; end; end; function TForm1.GenerateHMACSignature(Action: String; Timestamp: string): String; var StringToSign: string; SignedString: string; CodedSignedString: string; key, amessage: string; blocksize: Integer; opad, ipad: String; i: Integer; var AHashClass: TDECHashClass; ATextFormat: TDECFormatClass; begin blocksize := 512; AHashClass := THash_SHA256; ATextFormat := TFormat_MIME64; key := sAWSID; StringToSign := Action + Timestamp; if (length(key) > blocksize) then key := AHashClass.CalcBinary(key) // keys longer than blocksize are shortened else if (length(key) < blocksize) then key := key + concat('0',blocksize - length(key)); // keys shorter than blocksize are zero-padded opad := concat(Chr(92),blocksize); // Where blocksize is that of the underlying hash function ipad := concat(Chr(54),blocksize); // Where xor is exclusive or (XOR) opad := SimpleCryptString(opad,key); ipad := SimpleCryptString(ipad,key); // for I := 1 to Length(opad) do begin // opad[i] := opad[i] xor key; // ipad[i] := ipad[i] xor key; // end; SignedString := AHashClass.CalcBinary(opad + AHashClass.CalcBinary(ipad + amessage)); // Where + is concatenation Result := SignedString; end; procedure TForm1.HTTPRIO1BeforeExecute(const MethodName: string; SOAPRequest: TStream); var XML: TXMLFile; Node: TXMLNode; S: string; begin SOAPRequest.Seek(0,0); XML := TXMLFile.Create(Self,false); xml.LoadFromStream(SOAPRequest); Node := XML.RootNode.Node['SOAP-ENV:Body'].Node['ItemSearch']; Node.AddNode('Timestamp').Text := GenerateCurrentTimeStamp; Node.AddNode('Action').Text := 'ItemSearch'; Node.AddNode('Signature').Text_Base64w := GenerateHMACSignature('ItemSearch',Node.Node['Timestamp'].Text); SOAPRequest.Seek(0,0); xml.SaveToStream(SOAPRequest); xml.SaveToXML(S); Memo1.Text := S; end; end. |
Re: DEC und HMAC-SHA-256
Der Pseudocode ist wirklich grausam (ein Operatorzeichen für mehrere verschieden Operation etc).
Aber auch in Deinem Code sind aber noch einige Bugs drin: - Concat verwendet überhaupt nicht das übergebene c:char - padding muß mit #0 und nich '0' erfolgen Vollständig getesteten HMAC-Quellcode findest und mit den Testvektoren (und Referenzen) auf meiner CRC-Hash-Seite ![]() im Archive crc_hash_2010-03-26.zip, und eine kurze Einführung auf ![]() Allerdings sind die Funktionen auf's wesentlich beschränkt (Base64 für Amazon mußt Du zB selbst ausführen), allerdings haben schon etliche Leute die Routinen für Amazon verwendet. |
Re: DEC und HMAC-SHA-256
Okay, der Code war zusammengeschustert ... und das sieht man. Ich habs mal auf das Wesentliche reduziert.
Delphi-Quellcode:
Bin mir trotz Doku nicht sicher, ob das so richtig ist, da ich die Test Vectors des zugehörigen RFCs und auch einige Beispiele von Webgeneratoren nicht reproduzieren konnte. Kennt sich jemand mit diesen Units aus und kann mir sagen wo mein Fehler ist?uses HMAC, Hash, SHA256, mem_util; {...} function GenerateHMACSignature(Text: string; Key: string): string; var ctx: THMAC_Context; phash: PHashDesc; mac: THashDigest; begin phash := FindHash_by_Name('SHA256'); if phash = nil then begin {Action for 'Hash function not found/registered.'} exit; end; hmac_init(ctx, phash, @key, sizeof(key)); hmac_update(ctx, @Text, sizeof(Text)); hmac_final(ctx, mac); Result := HexStr(@mac, Length(mac)); end; Außerdem ist mir immer noch nicht klar, WOVON genau Amazon möchte, dass ich die Signatur erstelle. Laut deren Doku soll es ein TimeStamp sein (s.o. im Quelltext. Das sollte gehen) und der Inhalt des Action-Tags - nur leider enthält der vom WSDL erzeugte Envelope überhaupt kein solches Tag. Es ist echt ein Krampf, die Schnittstelle war mal so gut :kotz: |
Re: DEC und HMAC-SHA-256
Guten Abend,
könnte es hier eine Rolle spielen das String = UniCode ist, und die Beispiele auf AnsiString basieren? Grüße Klaus |
Re: DEC und HMAC-SHA-256
Warum benutzen hier eigentlich die meisten immer diese unsäglichen Strings, Hash und HMAC arbeitet mit Bit/Bytefolgen. Wenn schon Strings dann richtig: daß erste Zeichen ist key[1], Länge nicht sizeof(=4!) sondern length. Bei meinen Supportanfragen mußten auch immer ansistrings für Amazon benutzt werden.
Hier die korrigierte Funktion:
Delphi-Quellcode:
function GenerateHMACSignature(Text, key: ansistring): ansistring;
var ctx: THMAC_Context; phash: PHashDesc; mac: THashDigest; begin phash := FindHash_by_Name('SHA256'); if phash = nil then begin {Action for 'Hash function not found/registered.'} exit; end; hmac_init(ctx, phash, @key[1], length(key)); hmac_update(ctx, @Text[1], length(Text)); hmac_final(ctx, mac); Result := HexStr(@mac, sizeof(mac)); end; |
Re: DEC und HMAC-SHA-256
Liste der Anhänge anzeigen (Anzahl: 2)
Also, ich habe das ganze aus einem funktionierenden PHP-Skript übernommen und versucht das ganze in Delphi umzusetzen. Das PHP-Skript ist wie folgt:
Code:
Damit kann ich mir eine erfolgreiche HMAC-Verschlüsselung aus PHP ausgeben lassen. Jetzt versuche ich das ganze mit Wolfgangs CRC/Hash-Units in Delphi 2009 umzusetzen (siehe Anhang). Problem: Die Ergebnisse stimmen nicht überein. Ich hoffe jemand kann mir sagen, wo mein Fehler ist. Der Klartext ist "Test" und der Schlüssel ist "geheim". PHP liefert "eAGKtiF4J8GZY4TelvKwbxNTTMxeUjKfYYaPae6vIiQ=" , Delphi aber andere Werte.
<?php
session_start(); $t = $_REQUEST["t"]; $k = $_REQUEST["k"]; if ((empty($t)) || (empty($k))) { die; } $aktuelle = gmdate("Y-m-d\TH:i:s\Z"); $aktuellezeit = urlencode($aktuelle); $signature1 = hash_hmac("sha256", $t, $k, true); $signature2 = base64_encode($signature1); print "Timestamp: \t $aktuelle "; print "Timestamp (URLEnccode): \t $aktuellezeit "; print "Text: \t $t "; print "Key: \t $k "; print "Result: \t $signature1 "; print "Result_Base64: \t $signature2"; ?> |
Re: DEC und HMAC-SHA-256
Also zumindest ist die HMAC-Berechnung OK und das Ergebnis in Hex für 'Test' und 'geheim' ist 'b2f53072cc320d47e80cfd6a4e5faf3001c83bb3784c6da6d 8a8dded1978cfcc'. Dies stimmt (bis auf UpperCase) mit der Ausgabe von zB CryptoBench (basiert auf Crypto ++) überein.
Lass Dir mal das PHP-Ergebnis in Hex ausgeben. Was liefert denn PHP für die RFC-Testwerte, die Du zB in den Programmen t_hmac.pas und t_hmac2.pas findest? |
Re: DEC und HMAC-SHA-256
Wenn ich mich nicht vertan habe, dann bekomme ich als Ergebnis das folgende:
Code:
Die Konvertierung lief wie folgt:
Timestamp: 2010-04-30T11:19:35Z
Timestamp (URLEnccode): 2010-04-30T11%3A19%3A35Z Text: "Test" Key: "geheim" Result: xŠ¶!x'Á™c„Þ–ò°oSLÌ^R2Ÿa†iî¯"$ Result_Base64: eAGKtiF4J8GZY4TelvKwbxNTTMxeUjKfYYaPae6vIiQ= Result_Hex: 7818ab6217827c1996384de96f2b06f13534ccc5e52329f61868f69eeaf2224
Code:
Die Testvektoren werde ich später mal durchprobieren
for ($i=0; $i < strlen($hex); $i++)
{ $s .= dechex(ord($hex[$i])); } |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz