Verschlüsselung

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von xd-franky-5.

    Verschlüsselung

    Hallo Leute,
    ich würde in mein Programm gerne eine Verschlüsselung einbauen, mit Passwortschutz. Heißt das Programm soll
    1. eine Datei mit einem Passwort verschlüsseln
    2. eine Verschlüsselung bei einer Datei erkennen und
    3. mit dem RICHTIG eingegebenen Passwort eine Datei wieder entschlüsseln bzw. für den Moment lesbar machen (Die Datei ist eine XML-Datei).
    Ich weiß aber nicht ob das machbar ist, sicher ist und wie man das dann überhaupt machen würde. Eine Datei verschlüsseln hätte ich mit AES gemacht und das Passwort gehasht und gesaltet (wobei ich auch nicht ganz weiß wie das in VB.NET geht). Wie man so eine Datei erkennt weiß ich nicht vor Allem weiß ich nicht wie man entschlüsselt und entsaltet.
    Wenn mir das jemand erklären könnte wäre das toll :)

    mfG Frank
    Du hast einerseits deinen symmetrischen Verschlüsselungsalgorithmus (d.h. gleiches Passwort zum Ver- & Entschlüsseln) und auf der anderen Seite einen Schlüsselableitungsalgorithmus, welcher den Schlüssel für den Verschlüsselungsalgorithmus erstellt. Dabei ist der Ablauf folgender:
    Bei Verschlüsselung: Der Schlüsselableitungsalgorithmus (bspw. PBKDF2 oder eine einfache Hashfunktion) erhält das eingegebene Passwort und einen zufälligen Salt (oft auch eine zufällige Iterationszahl, d.h. wie oft der Algorithmus laufen soll; höher ist generell besser) und generiert dir eine Bytefolge, welche du nun bei deinem Verschlüsselungsalgorithmus (bspw. AES) als Key verwenden kannst (im Fall von AES kannst du dir mit dem Schlüsselableitungsalgorithmus auch den IV generieren lassen). Diesen Verschlüsselungsalgorithmus lässt du dann auf die Daten laufen und speicherst zusätzlich eine Checksumme der Originaldaten und die Iterationszahl und den Salt für den Schlüsselableitungsalgorithmus in der Datei.
    Bei der Entschlüsselung liest du dann die Iterationszahl und den Salt aus der Datei aus, leitest dir dann damit und dem vom Nutzer eingegebenen Passwort mit dem Schlüsselableitungsalgorithmus wieder Key & IV ab und entschlüsselst mit dem gleichen Verschlüsselungsalgorithmus die Daten, welche du dann mit der Checksumme validieren kannst (Überprüfen, ob das korrekte Passwort eingegeben wurde).

    Grüße
    Stefan
    Wie eine symmetrische Verschlüsselung grundsätzlich funktioniert: AES Verschlüsselung
    Hier ist ganz praktisch: Man kann den CryptoStream direkt auf einen FileStream (Zieldatei) draufsetzen und von einem anderen FileStream (Quelldatei) Daten blockweise reinschaufeln. Das spart RAM.
    Ein paar Details zu den Dateien: Wenn Du einfach nur die verschlüsselten Daten in die Zieldatei schreibst, dann kann man:
    1. später nicht erkennen, dass es sich um eine verschlüsselte Datei handelt (verstehe ich richtig, dass Du mit dem zweiten Punkt im Startpost meinst, dass das Programm erkennen können soll, ob eine Datei mit Deinem Programm verschlüsselt wurde?) und
    2. die Datei nicht mehr entschlüsseln, weil man den Salt nicht mehr hat.
    Du solltest Dir deshalb ein minimalistisches Dateiformat überlegen.
    Mein Vorschlag wäre folgender:
    • Die ersten Bytes der Datei sind eine "Magic Number". Die ist für alle Dateien, die Dein Programm erstellt, gleich. Das könnte z.B. eine GUID sein, die Du einmal generierst und dann fix im Code stehen hast. Wenn Du dann eine Datei wieder entschlüsseln willst, kannst Du prüfen, ob die ersten Bytes der Datei passen. Wenn nein, dann ist die Datei definitiv nicht mit Deinem Programm erstellt worden. Wenn ja, dann ist die Wahrscheinlichkeit, dass die Datei mit Deinem Programm erstellt wurde, sehr hoch. Es ist unwahrscheinlich, dass ein anderes Programm zufällig genau die gleichen Bytes am Anfang einer Datei geschrieben hat. Aber nur für den Fall der Fälle solltest Du auf Exceptions beim Entschlüsseln vorbereitet sein.
    • Alternative zum obrigen Punkt:
      Du speicherst alle verschlüsselten Dateien mit einer eigenen Dateiendung. Einfach an den originalen Dateinamen die eigene Dateiendung hinten dran hängen. Hat eine Datei diese Dateiendung, wurde sie mit Deinem Programm erstellt. Beim Entschlüsseln die Endung wieder entfernen. (Auch hier kann wieder zufällig ein anderes Programm die gleiche Dateiendung erstellt haben, deshalb auch hier beim Entschlüsseln auf Exceptions vorbereitet sein.)
    • Die nächsten Bytes in der Datei sind der Salt. Dieser ist üblicherweise immer gleich lang, also ist es kein Problem, den einfach in die Datei zu packen.
    • Dann kommen die verschlüsselten Daten.


    Zwei wichtige Hinweise noch:
    Wenn es sicher sein soll, dann Verschlüsselungen nicht selber basteln.
    Verschlüsselungen arbeiten immer auf rohen Bytes, nicht auf Strings. Deshalb sind alle Daten als Bytes zu behandeln.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @nafets & @Niko Ortner Okay danke für eure Hilfe erstmal. Also die Verschlüsselung muss nicht 100% sicher sein also ich weiß nicht ob ich überhaupt salten muss. Und nun zum Code:

    1. Passwort hashen hab ich so gemacht (ohne salt, SHA-256 als Algo):

    VB.NET-Quellcode

    1. Dim sha256Obj As New SHA256CryptoServiceProvider
    2. Dim bytesToHash() As Byte = Encoding.ASCII.GetBytes(Text)
    3. bytesToHash = sha256Obj.ComputeHash(bytesToHash)


    2. Datei in Bytes umwandeln

    VB.NET-Quellcode

    1. Dim fbytes() As Byte = File.ReadAllBytes(FileName)


    3. Checksumme der Originaldatei ermitteln

    VB.NET-Quellcode

    1. Dim fsha256Obj As New SHA256CryptoServiceProvider
    2. Dim fbytesToHash() As Byte = fsha256Obj.ComputeHash(fbytes)


    5. AES einrichten

    VB.NET-Quellcode

    1. Dim AES As New AesCryptoServiceProvider()
    2. With AES
    3. .Key = bytesToHash
    4. .IV = bytesToHash
    5. End With


    6. Encrypten

    VB.NET-Quellcode

    1. Dim fsInput As New FileStream(FileName, FileMode.Open, FileAccess.Read)
    2. Dim fsEncrypted As New FileStream(FileName & "e", FileMode.Create, FileAccess.Write)
    3. Using cryptostream As New CryptoStream(fsEncrypted, AES.CreateEncryptor, CryptoStreamMode.Write)
    4. Dim bytearrayinput(fsInput.Length - 1) As Byte
    5. fsInput.Read(bytearrayinput, 0, bytearrayinput.Length)
    6. cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length)
    7. End Using


    7. Bytes zusammenführen

    VB.NET-Quellcode

    1. fbytes = File.ReadAllBytes(FileName & "e")
    2. Dim startIndex As Integer = fbytes.Length
    3. Array.Resize(fbytes, fbytes.Length + fbytesToHash.Length)
    4. fbytesToHash.CopyTo(fbytes, startIndex)
    5. File.WriteAllBytes(FileName & "e", fbytes)


    Ich weiß nicht, ob das alles so richtig ist und ob es funktioniert, hab's noch nicht getestet. Bitte korrigiert mich :)

    mfG Frank

    EDIT: Bis auf den IV hat alles geklappt, hab's getestet

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „xd-franky-5“ ()

    xd-franky-5 schrieb:

    5. AES einrichten

    Visual Basic-Quellcode

    1. Dim AES As New AesCryptoServiceProvider()
    2. With AES
    3. .Key = bytesToHash
    4. .IV = bytesToHash
    5. End With

    Zwei Anmerkungen dazu: der IV hat die falsche Länge wenn ich mich recht entsinne und du solltest nicht für beides die gleichen Werte verwenden. Nutz für die Key- & IV-Generation doch einfach einen dafür gemachten Algorithmus wie PBKDF2 (wie ich es übrigens schon oben erwähnt hatte).

    xd-franky-5 schrieb:

    6. Encrypten

    Visual Basic-Quellcode

    1. Dim fsInput As New FileStream(FileName, FileMode.Open, FileAccess.Read)
    2. Dim fsEncrypted As New FileStream(FileName & "e", FileMode.Create, FileAccess.Write)
    3. Using cryptostream As New CryptoStream(fsEncrypted, AES.CreateEncryptor, CryptoStreamMode.Write)
    4. Dim bytearrayinput(fsInput.Length - 1) As Byte
    5. fsInput.Read(bytearrayinput, 0, bytearrayinput.Length)
    6. cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length)
    7. End Using

    Ein paar Anmerkungen hierzu:
    -Mach die FileStreams auch mit einem Using-Block
    -Du musst nicht die gesamten Daten in einem Byte-Array puffern - du kannst direkt von einem in den anderen Stream lesen

    xd-franky-5 schrieb:

    7. Bytes zusammenführen

    Visual Basic-Quellcode

    1. fbytes = File.ReadAllBytes(FileName & "e")
    2. Dim startIndex As Integer = fbytes.Length
    3. Array.Resize(fbytes, fbytes.Length + fbytesToHash.Length)
    4. fbytesToHash.CopyTo(fbytes, startIndex)
    5. File.WriteAllBytes(FileName & "e", fbytes)

    Das hier passt gar nicht - schreib den Hash direkt in den ​fsEncrypted-Stream, sobald er erstellt wurde. Dann kannst du dir den Code hier sparen.

    Grüße
    Stefan
    @nafets
    Wie soll das dann aussehen, ich kenne nur den md5 und sha Alogrithmus, so wie ich ihn benutzt hab'. Wenn ich den IV weg lasse, wird er auch automatisch generiert, ist das O.K. ?
    Und wie ist das gemeint mit "von einem in den anderen Stream lesen" ? Welchen Stream in welchen ?

    mfG Frank

    EDIT: Meinst du so ?

    VB.NET-Quellcode

    1. Using fsInput As New FileStream(FileName, FileMode.Open, FileAccess.Read)
    2. Using fsEncrypted As New FileStream(FileName & "_e", FileMode.Create, FileAccess.Write)
    3. fsEncrypted.Write(fbytesToHash, 0, fbytesToHash.Length)
    4. Using cryptostream As New CryptoStream(fsEncrypted, AES.CreateEncryptor, CryptoStreamMode.Write)
    5. fsInput.CopyTo(cryptostream)
    6. End Using
    7. End Using
    8. End Using

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „xd-franky-5“ ()

    Zum Beispiel: Ja, das sollte soweit passen.
    Zum Hash: Ich hatte doch schon in meinem ersten Post bei PBKDF2 extra einen Link zur MSDN hinterlegt, wo es auch ein Beispiel gibt, da die Benennung der Klasse anders ist. Damit sollte es für dich doch möglich sein, deinen Key & IV zu generieren, oder?
    Und ich meinte, dass du nicht CopyTo verwenden musst, sondern auch blockweise lesen kannst.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets“ ()

    Hab's jetzt noch mal versucht, wie findest du es so:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Security.Cryptography
    3. Imports System.Text
    4. Public Class Crypt
    5. Dim MagicBytes() As Byte
    6. Sub New(ByVal MagicString As String)
    7. MagicBytes = Encoding.Unicode.GetBytes(MagicString)
    8. End Sub
    9. Private Function CreateSalt() As String
    10. Dim chars As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+=][}{<>"
    11. Dim salt As String = ""
    12. Dim rnd As New Random
    13. For i As Integer = 1 To 64
    14. Dim x As Integer = rnd.Next(0, chars.Length - 1)
    15. salt &= (chars.Substring(x, 1))
    16. Next
    17. Return salt
    18. End Function
    19. Private Function CreateChecksum(ByVal Bytes() As Byte) As Byte()
    20. Dim result() As Byte
    21. Dim sha As New SHA512Managed()
    22. result = sha.ComputeHash(Bytes)
    23. Return result
    24. End Function
    25. Public Sub EncryptFile(ByVal FileName As String, ByVal Key As String)
    26. Dim salt() As Byte = Encoding.Unicode.GetBytes(CreateSalt())
    27. Dim checksum() As Byte = CreateChecksum(File.ReadAllBytes(FileName))
    28. Dim AES As New AesCryptoServiceProvider()
    29. Dim rfc As New Rfc2898DeriveBytes(Key, salt)
    30. AES.Key = rfc.GetBytes(32)
    31. AES.IV = rfc.GetBytes(16)
    32. Using fsInput As New FileStream(FileName, FileMode.Open, FileAccess.Read)
    33. Using fsEncrypted As New FileStream(FileName & "_e", FileMode.Create, FileAccess.Write)
    34. fsEncrypted.Write(MagicBytes, 0, MagicBytes.Length)
    35. fsEncrypted.Write(salt, 0, salt.Length)
    36. fsEncrypted.Write(checksum, 0, checksum.Length)
    37. Using cryptostream As New CryptoStream(fsEncrypted, AES.CreateEncryptor, CryptoStreamMode.Write)
    38. fsInput.CopyTo(cryptostream)
    39. End Using
    40. End Using
    41. End Using
    42. File.Delete(FileName)
    43. File.Move(FileName & "_e", FileName)
    44. End Sub
    45. Public Function DecryptFile(ByVal FileName As String, ByVal Key As String) As Boolean
    46. Try
    47. Dim salt(127) As Byte
    48. Dim mag(MagicBytes.Length - 1) As Byte
    49. Dim checksum(63) As Byte
    50. Using fshash As New FileStream(FileName, FileMode.Open, FileAccess.Read)
    51. fshash.Read(mag, 0, mag.Length)
    52. fshash.Read(salt, 0, salt.Length)
    53. fshash.Read(checksum, 0, checksum.Length)
    54. Dim AES As New AesCryptoServiceProvider()
    55. Dim rfc As New Rfc2898DeriveBytes(Key, salt)
    56. AES.Key = rfc.GetBytes(32)
    57. AES.IV = rfc.GetBytes(16)
    58. Using cryptostream As New CryptoStream(fshash, AES.CreateDecryptor, CryptoStreamMode.Read)
    59. Using fs As New FileStream(FileName & "_d", FileMode.Create)
    60. cryptostream.CopyTo(fs)
    61. End Using
    62. End Using
    63. End Using
    64. If checksum.SequenceEqual(CreateChecksum(File.ReadAllBytes(FileName & "_d"))) Then
    65. File.Delete(FileName)
    66. File.Move(FileName & "_d", FileName)
    67. Return True
    68. Else
    69. File.Delete(FileName & "_d")
    70. Return False
    71. End If
    72. Catch ex As Exception
    73. File.Delete(FileName & "_d")
    74. Return False
    75. End Try
    76. End Function
    77. Public Function IsEncrypted(ByVal FileName As String) As Boolean
    78. Dim mag(MagicBytes.Length - 1) As Byte
    79. Using fshash As New FileStream(FileName, FileMode.Open, FileAccess.Read)
    80. fshash.Read(mag, 0, mag.Length)
    81. End Using
    82. Return mag.SequenceEqual(MagicBytes)
    83. End Function
    84. End Class



    Hab jetzt noch eins: ich muss immer salt und magicbytes auslesen sonst liest der Stream falsch, kann ich das auch schöner lösen ?

    EDIT: Hab' den Code nochmal überarbeitet

    C#-Quellcode

    1. using Microsoft.VisualBasic;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.Data;
    6. using System.Diagnostics;
    7. using System.IO;
    8. using System.Security.Cryptography;
    9. using System.Text;
    10. public class Crypt
    11. {
    12. byte[] MagicBytes;
    13. public Crypt(string MagicString)
    14. {
    15. MagicBytes = Encoding.Unicode.GetBytes(MagicString);
    16. }
    17. private string CreateSalt()
    18. {
    19. string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+=][}{<>";
    20. string salt = "";
    21. Random rnd = new Random();
    22. for (int i = 1; i <= 64; i++) {
    23. int x = rnd.Next(0, chars.Length - 1);
    24. salt += (chars.Substring(x, 1));
    25. }
    26. return salt;
    27. }
    28. private byte[] CreateChecksum(byte[] Bytes)
    29. {
    30. byte[] result = null;
    31. SHA512Managed sha = new SHA512Managed();
    32. result = sha.ComputeHash(Bytes);
    33. return result;
    34. }
    35. public void EncryptFile(string FileName, string Key)
    36. {
    37. byte[] salt = Encoding.Unicode.GetBytes(CreateSalt());
    38. byte[] checksum = CreateChecksum(File.ReadAllBytes(FileName));
    39. AesCryptoServiceProvider AES = new AesCryptoServiceProvider();
    40. Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(Key, salt);
    41. AES.Key = rfc.GetBytes(32);
    42. AES.IV = rfc.GetBytes(16);
    43. using (FileStream fsInput = new FileStream(FileName, FileMode.Open, FileAccess.Read)) {
    44. using (FileStream fsEncrypted = new FileStream(FileName + "_e", FileMode.Create, FileAccess.Write)) {
    45. fsEncrypted.Write(MagicBytes, 0, MagicBytes.Length);
    46. fsEncrypted.Write(salt, 0, salt.Length);
    47. fsEncrypted.Write(checksum, 0, checksum.Length);
    48. using (CryptoStream cryptostream = new CryptoStream(fsEncrypted, AES.CreateEncryptor, CryptoStreamMode.Write)) {
    49. fsInput.CopyTo(cryptostream);
    50. }
    51. }
    52. }
    53. File.Delete(FileName);
    54. File.Move(FileName + "_e", FileName);
    55. }
    56. public bool DecryptFile(string FileName, string Key)
    57. {
    58. try {
    59. byte[] salt = new byte[128];
    60. byte[] mag = new byte[MagicBytes.Length];
    61. byte[] checksum = new byte[64];
    62. using (FileStream fshash = new FileStream(FileName, FileMode.Open, FileAccess.Read)) {
    63. fshash.Read(mag, 0, mag.Length);
    64. fshash.Read(salt, 0, salt.Length);
    65. fshash.Read(checksum, 0, checksum.Length);
    66. AesCryptoServiceProvider AES = new AesCryptoServiceProvider();
    67. Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(Key, salt);
    68. AES.Key = rfc.GetBytes(32);
    69. AES.IV = rfc.GetBytes(16);
    70. using (CryptoStream cryptostream = new CryptoStream(fshash, AES.CreateDecryptor, CryptoStreamMode.Read)) {
    71. using (FileStream fs = new FileStream(FileName + "_d", FileMode.Create)) {
    72. cryptostream.CopyTo(fs);
    73. }
    74. }
    75. }
    76. if (checksum.SequenceEqual(CreateChecksum(File.ReadAllBytes(FileName + "_d")))) {
    77. File.Delete(FileName);
    78. File.Move(FileName + "_d", FileName);
    79. return true;
    80. } else {
    81. File.Delete(FileName + "_d");
    82. return false;
    83. }
    84. } catch (Exception ex) {
    85. File.Delete(FileName + "_d");
    86. return false;
    87. }
    88. }
    89. public bool IsEncrypted(string FileName)
    90. {
    91. byte[] mag = new byte[MagicBytes.Length];
    92. using (FileStream fshash = new FileStream(FileName, FileMode.Open, FileAccess.Read)) {
    93. fshash.Read(mag, 0, mag.Length);
    94. }
    95. return mag.SequenceEqual(MagicBytes);
    96. }
    97. }

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „xd-franky-5“ ()