Identische Verschlüsselung erzielt anderes Ergebnis als in JavaScript.

  • VB.NET
  • .NET (FX) 4.0

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Nikx.

    Identische Verschlüsselung erzielt anderes Ergebnis als in JavaScript.

    Hej alle.

    Mein Freund und ich versuche zwei Verschlüsselungen auf einander abzustimmen. Einmal in VB, einmal in JavaScript. Leider erhalten wir aber nicht die gleiche Ausgabe.
    Das ganze basiert auf folgendem System:

    Die verwendete Eingabe ist beispielsweise "demo".
    Diese wird mit MD5 gehasht, und die ersten 16 Zeichen davon ergeben den IV einer AES-Verschlüsselung.

    VB.NET-Quellcode

    1. Imports System.Security.Cryptography
    2. Sub Foo()
    3. Dim AESProvider As New AesCryptoServiceProvider()
    4. Dim MD5Provider As New MD5CryptoServiceProvider()
    5. Dim toCrypt As String = Console.ReadLine()
    6. Dim toCryptBytes As Byte() = Encoding.Default.GetBytes(toCrypt)
    7. Dim MD5Hash As Byte() = MD5Provider.ComputeHash(toCryptBytes)
    8. ReDim Preserve MD5Hash(15)
    9. AESProvider.IV = MD5Hash
    10. End Sub


    Quellcode

    1. var iv = CryptoJS.enc.Hex.parse(CryptoJS.MD5(input).toString().substring(0, 16));


    Bei unserer Fehlersuche haben wir alle Werte überprüft angefangen halt mit dem Hash.
    Sein Hash (JS) sieht folgendermaßen aus fe01ce2a7fbac8f meiner hingegen so FE1CE2A7FBAC8FAFAED7C982A4E229. Woher dieser Unterschied? Bei meinem fehlt die 0 am Anfang, es sind alles Großbuchstaben und er ist länger. Dabei wird das Array doch gekürzt ReDim Preserve MD5Hash(15)?

    Zum Umwandeln in Hex verwende ich dem Fall folgende Funktion:

    VB.NET-Quellcode

    1. Public Function StrToHex(ByRef Data As String) As String
    2. Dim sVal As String
    3. Dim sHex As String = ""
    4. While Data.Length > 0
    5. sVal = Conversion.Hex(Strings.Asc(Data.Substring(0, 1).ToString()))
    6. Data = Data.Substring(1, Data.Length - 1)
    7. sHex = sHex & sVal
    8. End While
    9. Return sHex
    10. End Function


    Aufgerufen mit Console.WriteLine(StrToHex(Encoding.Default.GetString(MD5Hash))).

    Selbes Problem auch mit dem Salt (Jeweils mit SHA256 gehasht):
    JS: 2a97516c354b68848cdbd8f54a226a0a55b21ed138e207ad6c5cbb9c00aa5aea
    VB.NET: 2A97516C354B68848CDBD8F54A226AA55B21ED138E27AD6C5CBB9C0AA5AEA
    Allerdings fehlen dieses mal nur 0en und alle Buchstaben sind groß.

    Interessant ist auch dass sein PBKDF2-Key (bzw Rfc2898) bei einer Size von 32 128 Zeichen lang ist, meiner aber nur 64.
    Wie können wir beide Ergebnisse gleich bekommen? Hier nochmal beide Source Codes:

    VB.NET

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Security.Cryptography
    3. Imports System.Text
    4. Sub Main()
    5. Dim AESProvider As New AesCryptoServiceProvider()
    6. Dim SHA256Provider As New SHA256CryptoServiceProvider()
    7. Dim MD5Provider As New MD5CryptoServiceProvider()
    8. Dim toCrypt As String = Console.ReadLine()
    9. Dim toCryptBytes As Byte() = Encoding.Default.GetBytes(toCrypt)
    10. Dim MD5Hash As Byte() = MD5Provider.ComputeHash(toCryptBytes)
    11. ReDim Preserve MD5Hash(15)
    12. Dim RfcProvider As New Rfc2898DeriveBytes(toCryptBytes, SHA256Provider.ComputeHash(toCryptBytes), 2)
    13. AESProvider.IV = MD5Hash
    14. AESProvider.Key = RfcProvider.GetBytes(32)
    15. Dim cryptoTransform As ICryptoTransform = AESProvider.CreateEncryptor
    16. Dim MS As New MemoryStream()
    17. Dim cryptoStream As New CryptoStream(MS, cryptoTransform, CryptoStreamMode.Write)
    18. cryptoStream.Write(toCryptBytes, 0, toCryptBytes.Length)
    19. cryptoStream.FlushFinalBlock()
    20. Dim toOutputBytes As Byte() = MS.ToArray()
    21. Dim toOutput As String = Convert.ToBase64String(toOutputBytes)
    22. Console.WriteLine("> " & toOutput)
    23. Console.ReadLine()
    24. End Sub
    25. Public Function StrToHex(ByRef Data As String) As String
    26. Dim sVal As String
    27. Dim sHex As String = ""
    28. While Data.Length > 0
    29. sVal = Conversion.Hex(Strings.Asc(Data.Substring(0, 1).ToString()))
    30. Data = Data.Substring(1, Data.Length - 1)
    31. sHex = sHex & sVal
    32. End While
    33. Return sHex
    34. End Function

    JS

    Quellcode

    1. var salt = CryptoJS.SHA256(input).toString(), iv = CryptoJS.MD5(input).toString().substring(0, 16);
    2. var key = CryptoJS.PBKDF2(input, CryptoJS.enc.Hex.parse(salt), {keySize: 32, iterations: 2});
    3. return CryptoJS.AES.encrypt(input, key, {iv: CryptoJS.enc.Hex.parse(iv)}).ciphertext.toString(CryptoJS.enc.Base64);


    Ich weiß natürlich, dass das schon eine ziemlich spezielle Frage ist, freue mich aber dennoch über jede Antwort.
    Danke im Voraus und Grüße
    Fabian
    @Vainamo V
    Also ich hab es mal mit dotnet-snippets.de/snippet/den…ines-strings-ermitteln/75 probiert und da bekomme ich den richtigen Wert raus.
    Wenn du den Hash mal in JS ausgibst kommt da sicher genau das selbe heraus.

    Dann könnt ihr euch zu eurem AES weiter vorarbeiten.

    Wobei diese Idee nicht sicher ist.
    1. ist MD5 veraltet. Da sollte man SHA512 (oder etwas Vergleichbares) mit einem Salt benutzen.
    2. Kann es Dopplungen geben wenn ihr nicht den ganzen Hash benutzt.

    Beispiel
    bei "abc" bekommt man den Hash 0123456789
    bei "efg" bekommt man den Hash 01234abcde
    Benutzt ihr nun die ersten vier Zeichen als Schlüssel kann ich sowohl mit "abc" als auch "efg" eure Daten entschlüsseln.

    Wieso wollt ihr überhaupt auf solch ein System zurückgreifen?

    Edit. Achja, ReDim nicht benutzen. Einfach eine neue Variable erstellen und das Array auch "richtig" abschneiden. z.B Array.Copy
    Ohne jetzt zu hinterfragen wozu das alles und ob es Sinn macht.
    Erstens sind die ersten 16 Zeichen bei einem String etwas anderes als bei Bytes. In JS habt ihr 15 String Zeichen. In Hex sind "2" Zeichen aber 1 Byte. Das heisst, ihr verwendet nur 7,5 Byte. Entsprechend musst du das Byte Array auch nur 7,5 Byte abschneiden.
    Das geht bei Byte aber bescheiden (Also es geht aber, naja). Nehmt einfach 8 Byte, also 16 Zeichen im JS.
    Außerdem würde ich auch nicht Redim verwenden sondern:

    VB.NET-Quellcode

    1. Dim MD5Hash() As Byte = New Byte(7) {} '<- glaub so geht es in vb.net
    2. Array.Copy(MD5Provider.ComputeHash(toCryptBytes), MD5Hash, 7)
    3. 'Zum Anzeigen einfach:
    4. Debug.print(BitConverter.ToString(MD5Hash))
    5. AESProvider.IV = MD5Hash
    Das ist meine Signatur und sie wird wunderbar sein!
    @LaMiy Danke für deine Antwort. Den Code hab ich mir angesehen, da stellt sich mir aber die Frage, benutze ich überhaupt die richtige Methode um aus dem ByteArray einen String zu machen? Und ist der Hash bei gleich lang, wie der gepostete JS-Hash?

    Im wesentlichen funktioniert das System wie folgt:
    Aus einem eingegeben Wert wird ein MD5 Hash und ein SHA256 Hash errechnet. Der MD5 Hash wird der Initalisierungs-Vektor des AES Providers. Allerdings nur die ersten 16 Bytes weil mehr dafür nicht zugelassen sind. Der SHA256 Hash wird der Salt eines Rfc2898 Providers. Dieser generiert einen Key aus dem eingegeben Wert, dem deraus gehashten Salt und dieser Key landet wieder beim AES Provider. Der erstellt einen Encrypter der von einem CryptoStream gebraucht wird um den Wert verschlüsselt in einen MemoryStream zu schreiben. Zu letzt wird der Inhalt des MStreams in einen Base64-String konvertiert. Ob MD5 jetzt nicht so sicher ist spielt kaum eine Rolle da es ja lediglich einen kleinen Teil der Verschlüsselung darstellt, wodurch auch das Problem mit den Doppelungen uninteressant wird.

    Im Wesentlich brauchen wir dieses System weil wir an einer webbasierten Version meines Chats schreiben, wobei aber beide die gleiche Datenbank benutzen sollen. Dementsprechend sollte, der Einfachheit halber, die Verschlüsselung gleich sein.

    Dieses System ist aber bewusst so kompliziert gewählt. In diesem Fall ist nämlich jeder auftretende Wert immer aus der Eingabe des Benutzers erstellt, es gibt also nirgends irgendwelche gespeicherten Salt o.ä. um die Passwörter in der Datenbank zu entschlüsseln. Sondern es wird lediglich die Eingabe des Benutzers erneut Verschlüsselt und geschaut ob beide (das errechnete und das aus der Datenbank) identisch sind.

    @Mono ReDim Preserve macht das gleiche, allerdings hab ich nicht gewusst, dass das Array auf 8 zugeschnitten werden muss.
    Auch BitConverter.ToString() bringt schon deutlich weiter, dankeschön.
    Edit: Wenn ich das Array auf 8 zuschneide kommt: Der angegebene Initialisierungsvektor (IV) stimmt nicht mit der Blockgröße für diesen Algorithmus überein.?
    Wir haben es, weil es immer noch nicht funktioniert hat, ganz anders gemacht. Den JS-Code in eine HTML-Datei, ein WebBrowser Control und folgender Code:

    VB.NET-Quellcode

    1. Imports System.Security.Permissions
    2. <PermissionSet(SecurityAction.Demand, Name:="FullTrust")>
    3. <System.Runtime.InteropServices.ComVisibleAttribute(True)>
    4. Public Class frmMain
    5. Private Sub Encrypt(Data As String)
    6. WebBrowser1.Document.GetElementById("input").InnerHtml = Data
    7. WebBrowser1.Document.InvokeScript("Encrypt")
    8. MessageBox.Show(Me.WebBrowser1.Document.GetElementById("output").InnerHtml)
    9. End Sub
    10. End Class


    Auch wenn es nicht zum Erfolg geführt hat, danke für eure Antworten.
    Den JS-Code in eine HTML-Datei, ein WebBrowser Control und folgender Code WTF?!

    Zunächst sei angemerkt, dass der IV möglichst zufällig gewählt sein sollte. Ihn aus einer Benutzereingabe zu generieren (so sieht es hier für mich aus), kann eine blöde Idee sein. Grundsätzlich will man nicht mehrmals den gleichen IV mit dem gleichen Key verwenden.

    Zur eigentlichen Frage:
    MD5 produziert einen Hash mit 128bit. Der Umstand, dass "MD5 veraltet" ist, kann hier vermutlich ignoriert werden, da es ja nur darum geht, aus einem variable langen String einen IV zu generieren (was wie geschrieben nicht optimal sein muss).
    In .NET bekommst du entsprechend ein Byte-Array der Länge 16 zurück. (=128bit, perfekt als IV (AES128 vorausgesetzt), nix mit ReDim oder so)
    Auf der JavaScript-Seite verwendet ihr CryptoJS, dessen MD5-Funktion den Hash als String mit 32 Hexadezimalzeichen Zeichen codiert. Entsprechend kommt bei dem JS-Code (.substring(0, 16)) aus dem ersten Post etwas "falsches" heraus.

    Was die Konvertierung aus Post #1 in .NET angeht: Groß- und Kleinschreibung ist mehr oder weniger Implementierungssache. Die einen verwenden Groß-, die andere Kleinbuchstaben. Das ändert nichts an dem eigentlichen Hash-Wert. Muss bei Stringvergleichen natürlich beachten werden, aber das sollte ja nicht wirklich ein Problem darstellen.
    Die fehlenden Nullen liegen an einem Fehler in StrToHex. Hier muss "0" zur Zeichenkette hinzugefügt werden, falls sVal.Length == 1.

    Im wesentlichen funktioniert das System wie folgt:
    Aus einem eingegeben Wert wird ein MD5 Hash und ein SHA256 Hash errechnet. Der MD5 Hash wird der Initalisierungs-Vektor des AES Providers. Allerdings nur die ersten 16 Bytes weil mehr dafür nicht zugelassen sind. Der SHA256 Hash wird der Salt eines Rfc2898 Providers. Dieser generiert einen Key aus dem eingegeben Wert, dem deraus gehashten Salt und dieser Key landet wieder beim AES Provider. Der erstellt einen Encrypter der von einem CryptoStream gebraucht wird um den Wert verschlüsselt in einen MemoryStream zu schreiben. Zu letzt wird der Inhalt des MStreams in einen Base64-String konvertiert. Ob MD5 jetzt nicht so sicher ist spielt kaum eine Rolle da es ja lediglich einen kleinen Teil der Verschlüsselung darstellt, wodurch auch das Problem mit den Doppelungen uninteressant wird.


    Sag mir bitte (nicht), dass das in etwa so aussieht:

    Quellcode

    1. Eingabe x
    2. iv = md5(x)
    3. salt = sha256(x)
    4. key = pbkdf2(x, salt)
    5. c = aes(x, key, iv)
    6. Ausgabe base64(c)


    Nur so aus Interesse, was würde dann weiter mit dem base64-Codierten Wert passieren?

    Dieses System ist aber bewusst so kompliziert gewählt.
    Noch eine kleine Anmerkung dazu: Zusätzliche Komplexität bringt nicht immer zusätzliche Sicherheit. In der Regel ist eher das Gegenteil der Fall.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „3daycliff“ () aus folgendem Grund: Typos...

    Doch das sieht in etwa so aus, bis auf ein zwei Unterschiede in der weiteren Verarbeitung des Salts. Der Base64 String wird am Ende übrigens in einer Datenbank gespeichert. Sicherlich ist das sicherheitstechnisch auf Dauer gesehen nicht die beste Lösung aber für den Moment reicht es.
    Moin, hatte mit Sha512 letztens so meine Schwierigkeiten, dort das selbe Problem, es fehlten 0en.
    Das Problem lag allerdings keineswegs an meiner Implementation, sondern an der finalen Konvertierung der Bytes zu Hex.
    Schau dir den Part also nochmal an.

    Grüße
    "Life isn't about winning the race. Life is about finishing the race and how many people we can help finish the race." ~Marc Mero

    Nun bin ich also auch soweit: Keine VB-Fragen per PM! Es gibt hier ein Forum, verdammt!