![]() |
MD5 - Unterschiede zw. Indy und DEC6
Hallo,
ich verwende Indy zur Kommunikation mit einem Webserver. Der Webserver verlangt Digest-Authentication und Indy hat dafür ja die Klasse TIdDigestAuthentication. In dieser Klasse wird zur Berechnung des Hashes MD5 verwendet, was in der Klasse TIdHashMessageDigest5 implementiert ist. Jetzt mal unabhängig davon, dass MD5 nicht mehr als sicher gilt, ist der Algorithmus als solcher ja standardisiert und es sollte bei verschiedenen Komponenten, die Hash-Klassen anbieten, doch dann immer der gleiche Hash rauskommen, oder? Jedenfalls benutze ich auch DEC6 und wenn ich mir da den MD5-Hash des gleichen Ausgangswertes mit der Klasse THash_MD5 geben lasse, bekomme ich einen anderen Hash-Wert als bei Indy.
Delphi-Quellcode:
Hashwert bei Indy: B089896DDE61B804B603F7866A9D664B
lMD5_Indy := TIdHashMessageDigest5.Create;
try sValue := lMD5_Indy.HashStringAsHex('Testwert'); finally lMD5_Indy.Free; end; lMD5_DEC := THash_MD5.Create; try sValue := lMD5_DEC.CalcString('Testwert', TFormat_HEX); finally lMD5_DEC.Free; end; Hashwert bei DEC: 7C94514A886400F144C1B2FF80854F33 Welche Erklärung kann es dafür geben? |
AW: MD5 - Unterschiede zw. Indy und DEC6
Ich tippe auf automatische interne Umwandlung des übergebenen Strings. Ansi, UTF8, UnicodeString, sowas in der Richtung.
|
AW: MD5 - Unterschiede zw. Indy und DEC6
Die Berechnung selbst sollte bei Beiden korrekt sein.
Ich geht da mal nicht von einem Fehler aus, aber es kommt auch darauf an, wie der String "binär" interpretiert wird. * Unicode (UTF-16), also 2 Byte pro Char * ANSI, also mit der aktuellen Codepage und 1 Byte pro Char (besonder unpraktisch, wenn z.B. in deutschem, englischen, russischen oder japanischem Windows andere Codepages/Charsets verwendet werden) * UTF-8 ... wird meistens gern benutzt Und das kannst du dir ja im Code ansehn, was HashStringAsHex und CalcString wie intern nutzen. * falls Parameter als "String" sind * ansonsten wird natürlich schon bei Übergabe des Parameters konvertiert |
AW: MD5 - Unterschiede zw. Indy und DEC6
B089896DDE61B804B603F7866A9D664B ist auf jeden Fall der richtige MD5 Hash für 'Testwert'. Ich vermute auch, dass die DEC Variante den Unicodestringparameter nicht korrekt verarbeitet. Dazu müsstest du mal den Quelltext der lMD5_DEC.CalcString Methode hier posten.
Mit neueren Delphi's kannst du auch die mitgeliferten Hash Routinen nutzen:
Delphi-Quellcode:
hash := System.Hash.THashMD5.GetHashString('Testwert');
|
AW: MD5 - Unterschiede zw. Indy und DEC6
Beide Methoden haben als Parameter einen ganz normalen String.
Indy function HashStringAsHex(const AStr: String; ADestEncoding: IIdTextEncoding = nil): String; In der Methode wird HashString aufgerufen (als Parameter auch ein ganz normaler String).
Delphi-Quellcode:
In WriteStringToStream steht u.a. folgendes:
function HashString(const ASrc: string; ADestEncoding: IIdTextEncoding = nil): TIdBytes;
var LStream: TStream; begin LStream := TMemoryStream.Create; try WriteStringToStream(LStream, ASrc, ADestEncoding); LStream.Position := 0; Result := HashStream(LStream); finally FreeAndNil(LStream); end; end;
Delphi-Quellcode:
Ohne Encoding-Angabe ist der Standard bei Indy dann ASCII.
LBytes := ToBytes(AStr, LLength, AIndex, ADestEncoding);
DEC6 function CalcString(const Value: string; Format: TDECFormatClass = nil): string; overload;
Delphi-Quellcode:
In der Methode wird CalcBuffer aufgerufen,
function TDECHash.CalcString(const Value: string; Format: TDECFormatClass): string;
var Size : Integer; Data : TBytes; begin Result := ''; if Length(Value) > 0 then begin {$IFDEF HAVE_STR_LIKE_ARRAY} Size := Length(Value) * SizeOf(Value[low(Value)]); Data := CalcBuffer(Value[low(Value)], Size); {$ELSE} Size := Length(Value) * SizeOf(Value[1]); Data := CalcBuffer(Value[1], Size); {$ENDIF} Result := StringOf(ValidFormat(Format).Encode(Data)); end else begin SetLength(Data, 0); result := StringOf(ValidFormat(Format).Encode(CalcBuffer(Data, 0))); end; end;
Delphi-Quellcode:
in der mit dem übergebenen Wert dann als untypisierter Buffer weitergearbeitet wird.
function TDECHash.CalcBuffer(const Buffer; BufferSize: Integer): TBytes;
|
AW: MD5 - Unterschiede zw. Indy und DEC6
Danke Rolf.
Habe auch noch einmal verschiedene Werte getestet. Mit System.Hash.THashMD5 von Delphi und TIdHashMessageDigest5 von Indy bekomme ich immer die gleichen Hash-Werte, so wie man es auch erwartet. THash_MD5 von DEC6 gibt immer einen anderen Hash-Wert, was die Vermutung nahe legt, dass im DEC6 intern etwas anderes passiert. Vielleicht kann TurboMagic etwas dazu sagen, wenn er den Thread vielleicht liest. |
AW: MD5 - Unterschiede zw. Indy und DEC6
Zitat:
Ansonsten sollte die Ursache ja jetzt klar sein. Wenn bei Indy per Default der String als ANSI-String (also 1 Byte pro Zeichen) angenommen wird, und bei TDECHash.CalcBuffer der übergebene String als untypisierter Buffer ankommt, dann werden da höchstwahrscheinlich 2 Byte pro Zeichen verarbeitet. |
AW: MD5 - Unterschiede zw. Indy und DEC6
Du müsstest auch mal abklären, was für ein Format zur Berechnung des MD5 der Webdienst benötigt. Vermutlich erwartet der nähmlich einen MD5 von einem UTF-8 string. In dem Fall musst du deinen String zuerst in UTF-8 umwandeln und dann von diesem den MD5 berechnen.
Delphi-Quellcode:
var
b:TBytes; hash: string; begin b := TEncoding.UTF8.GetBytes('Testwörter'); // StringOf(b) ergibt "Testwörter" hash := System.Hash.THashMD5.GetHashString(StringOf(b)); ... end; |
AW: MD5 - Unterschiede zw. Indy und DEC6
Ich habe momentan DEC nicht installiert.
Aber wenn's immer noch so ist wie früher: Mit DEC wird doch ein Testprogramm für alle eingebauten Hash Funktionen mitgeliefert. Hast du dort überprüft wie DEC die ![]() |
AW: MD5 - Unterschiede zw. Indy und DEC6
Bei Indy würde ich immer enUTF8 angeben, ansonsten wird wie bereits gesagt intern immer das ASCII encoding genutzt, was einfach alle Zeichen größer $7F als '?' interpretiert - siehe TIdASCIIEncoding.GetBytes in IdGlobal.pas
Ebenso musst du bei DEC dafür sorgen, dass er einen UTF8 String hasht und nicht einen UnicodeString (UTF16) - das kann man einfach mit einem Cast auf UTF8String bewerkstelligen, hier baut der Compiler immer die notwendige Konvertierungsfunktion ein. Durch die Überladung von CalcString mit RawByteString wird dann diese aufgerufen.
Delphi-Quellcode:
Ausgabe (Obacht, ich hab da nen ä in den String geschmuggelt, um das mit dem UTF8 zu testen):
var lMD5_Indy := TIdHashMessageDigest5.Create;
try var sValue := lMD5_Indy.HashStringAsHex('Tästwert', enUTF8); Writeln(sValue); finally lMD5_Indy.Free; end; var lMD5_DEC := THash_MD5.Create; try var sValue := lMD5_DEC.CalcString(UTF8String('Tästwert'), TFormat_HEX); Writeln(sValue); finally lMD5_DEC.Free; end; 9C6F9390DE3580AA8717DAA21D1E3622 9C6F9390DE3580AA8717DAA21D1E3622 Laut diverser online md5 Generatoren ist das wohl richtig. |
AW: MD5 - Unterschiede zw. Indy und DEC6
Liste der Anhänge anzeigen (Anzahl: 1)
ich habe "Testwert" (natürlich ohne ") in das DEC-Hash eingegeben und erhalte das beigefügte Ergebnis genau wie mit Indy.
Ich habe für das Programm DEC Hash Vcl DEC 6.41 (also das aktuelle DEC) verwendet. |
AW: MD5 - Unterschiede zw. Indy und DEC6
Hallo,
schön, dass ihr das schon alleine rausbekommen habt. Danke an SGlienke! Nach dem halt UnicodeString in DelphiUTF16 ist, würde ich so ein Verhalten auch ehrlicherweise erwarten. Warum? 1. Weil es wie von Stefan richtig bemerkt ja extra eine RawByteString Variante davon gibt die man in den anderen Fällen nutzen kann. 2. Man sonst bei wirklicher Ausnutzung eines UnicodeString/string mit Zeichen > 255 Schwierigkeiten hätte. Dann bräuchte man gar keine Variante mit UnicodeString/string anzubieten. Grüße TurboMagic |
AW: MD5 - Unterschiede zw. Indy und DEC6
Hallo,
danke an Stefan für den Hinweis zu UTF8. Allerdings kann ich bei Indy nichts am Aufruf von HashStringAsHex ändern, da die Klasse TIdHashMessageDigest5 intern in TIdDigestAuthentication verwendet wird. Frage an TurboMagic: Die Hash-Methode von Delphi (System.Hash.THashMD5.GetHashString) kann ich ganz normal mit einem String aufrufen, da dort intern eine Umwandlung des Parameters erfolgt (TEncoding.UTF8.GetBytes(...)). Wäre das nicht auch bei den DEC-Funktionen sinnvoll, damit man die Umwandlung mit UTF8String nicht immer selber machen muss? |
AW: MD5 - Unterschiede zw. Indy und DEC6
Du kannst die String-Variable auch einfach als
Delphi-Quellcode:
deklarieren, anstatt als
UTF8String
Delphi-Quellcode:
.
string
|
AW: MD5 - Unterschiede zw. Indy und DEC6
Hallo,
ich bin mir noch nicht sicher ob ich Himitsu richtig verstehe. Wenn man einen normalen String mit den DEC string Funktionen benutzt ist der ja UTF16 in Delphi. Weißt man einen UTF8String einem String zu dürfte der ja in UTF16 konvertiert werden, oder? Dann käme ja nicht das richtige raus. Wenn man die DEC erweitern möchte (weil es zu müßig ist RawByteString Typecasts hinzuschreiben, dann müsste man wohl noch UTF8String basierte Overloads hinzufügen. Oder was hab' ich jetzt falsch verstanden? :? Falls wir uns einig werden was der richtige Ansatz ist bin ich auch für Codespenden dankbar ;-) Grüße TurboMagic |
AW: MD5 - Unterschiede zw. Indy und DEC6
Gäbe es bloß ein CalcString mit string, würde es stimmen.
Aber da es eine Überladung mit einem kompatiblen Typen zum UTF8String gibt, wird Jenes verwendet, also RawByteString. Bei einer Überladung von String (bzw. UnicodeString/WideString) und AnsiString, da wird es schwieriger. Vor 2009 wäre der UTF8String in jeden anderen AnsiString-Typen übergegangen, womöglich sogar ohne Konvertierung. Jetzt wird es komplizierter. Ich würde hoffen es ginge da auf String/UnicodeString, aber es wäre auch möglich, dass es mit Konvertierung in den AnsiString ginge (was blöd wäre). |
AW: MD5 - Unterschiede zw. Indy und DEC6
Ich habe noch eine etwas ältere Version von DEC (Version 6.2 laut readme).
Delphi-Quellcode:
Hier kommt der richtige MD5-Hash raus.
function TDECHash.CalcString(const Value: RawByteString; Format: TDECFormatClass): RawByteString;
var Buf: TBytes; begin Result := ''; if Length(Value) > 0 then {$IF CompilerVersion >= 24.0} Result := BytesToRawString(ValidFormat(Format).Encode(CalcBuffer(Value[low(Value)], Length(Value) * SizeOf(Value[low(Value)])))) {$ELSE} Result := BytesToRawString(ValidFormat(Format).Encode(CalcBuffer(Value[1], Length(Value) * SizeOf(Value[1])))) {$IFEND} else begin SetLength(Buf, 0); Result := BytesToRawString(ValidFormat(Format).Encode(CalcBuffer(Buf, 0))); end; end; |
AW: MD5 - Unterschiede zw. Indy und DEC6
Naja, in deinem Fall ist auch klar warum das richtige raus kommt:
Die benutzt RawByteString, also 8-Bit pro Zeichen. Und wie ja schon ermittelt wurde kommt auch bei der aktuellen Version das richtige raus, wenn man das richtige Overload aufruft. => also ggf. TypeCast Grüße TurboMagic |
AW: MD5 - Unterschiede zw. Indy und DEC6
@TurboMagic
It will be nice if there a demo, a specific one to point and emphasize how strings being handled and how other library does it, eg. Indy or any popular PHP library .. etc |
AW: MD5 - Unterschiede zw. Indy und DEC6
Nja, eine gute Überladung von UTF8String mit String+RawByteString lässt sich leider nicht bilden, da es bei "anderen" AnsiString-Typen keinen wirklich sicheren Pfad ergibt,
außer man würde RawByteString und String/UnicodeString nehmen und intern immer nach UTF-8 konvertieren, was aber den Leuten die Möglichkeit nehmen würde, auch andere Konvertierungen zu nutzen. Am Einfachsten wäre wohl eine Standardfunktion, mit Typ UTF8String (ohne Überladungen), da das vermutlich der häufigste/nützlichste Fall sein dürfte und Delphi alle übergebenen Stringtypen automatisch konvertieren würde, und daneben eine andere überladene Funktion mit RawByteString, String/UnicodeString, TBytes und Pointer+Len, für alle anderen Fälle und Daten/Strings in allen möglichen Datenformaten, welche ohne automatische Konvertierung behandelt werden. Alternativ eben nur die letzte überladene Parameter-Variante, mit einem zusätzlichen Parameter, welcher das interne Encoding vorgibt, in was vor der Hash-Berechnung konvertiert würde, also z.B. ein Enum oder TEncoding, mit Default auf UTF-8. Alles in Richtung TBytes und oder Pointer (RawByteString als Kopie mit SetCodePage oder einfach an String/UnicodeString übergeben, sowie den UnicodeString mit TEncoding in ein TBytes und am Ende dann daraus der Hash) |
AW: MD5 - Unterschiede zw. Indy und DEC6
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:28 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 by Thomas Breitkreuz