none
CryptographicEngine mit utf16 encoding RRS feed

  • Frage

  • Hallo,

    ich möchte gerne einen string mit einer Methode

    string Verschluesseln(string _key, string data)

    verschlüsseln und den Rückgabewert entsprechend entschlüsseln können, sodass wieder das gleiche Ergebnis herauskommt wie vor der Verschlüsselung. Mit der normalen Funktion

    CryptographicBuffer.DecodeFromBase64String()

    Funktioniert das auch alles ganz gut, allerdings kann der string data dann keine Sonderzeichen beinhalten, ohne dass eine Exeption geworfen wird "Ungültige Daten";

    Deshalb habe ich es mit der Funktion

    CryptographicBuffer.ConvertStringToBinary()

    versucht. Meine Funktionen sehen wie folgt aus:

    public static string Verschluesseln(string _key, string data) {
                string key = _key;
                data = strErweitern(data); //Erweitert den string auf ein Vielfaches von 8 mit einem bestimmten Zeichen
                SymmetricKeyAlgorithmProvider AKYP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.Rc4);
                string verschlusseltstr = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16LE, CryptographicEngine.Encrypt(AKYP.CreateSymmetricKey(CryptographicBuffer.DecodeFromBase64String(key.Replace("-", "1"))),
                                                                                                                                                                      CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf16LE),
                                                                                                                                                                      null));
                return verschlusseltstr;                                                                                                                    
            }
            public static string Entschluesseln(string _key, string data) {
                string key = _key;
                SymmetricKeyAlgorithmProvider AKYP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.Rc4);
                string encydata = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16LE, CryptographicEngine.Decrypt(AKYP.CreateSymmetricKey(CryptographicBuffer.DecodeFromBase64String(key.Replace("-", "1"))),
                                                                                                                                                              CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf16LE),
                                                                                                                                                              null));
                encydata = strReduzieren(encydata); //Reduziert den string auf das Original, indem das Zeichen (z.B. ß) von hinten entfernt wird
                return encydata;
            }

    Mein Problem ist: Wenn ich einen string verschlüssle und wieder entschlüssle kommen nur irgendwelche chinesischen Zeichen dabei raus, aber nicht das, was ich ursprünglich wollte.

    Ich habe schon alle möglichen Kombinationsweisen zwischen ConvertStringToBinary und DecodeFrom64String ausprobiert. Das hat aber alles nichts gebracht.

    Ich habe es auch mit utf8 probiert, dort bekomme ich aber die Fehlermeldung:

    Für das Unicode-Zeichen ist kein zugeordnetes Zeichen in der Mehrbytecodepage vorhanden. (Ausnahme von HRESULT: 0x80070459)

    Womit ich nicht wirklich was anfangen kann.

    Ich hoffe ihr könnt mir helfen.

    Wie gesagt, es ist wichtig, das möglichst viele Sonderzeichen unterstützt werden.

    Gruß

    Hefeteig





    Fischertechnik Fan

    Mittwoch, 9. Januar 2013 19:34

Antworten

  • Hallo Hefeteig,

    Obwohl es so aussehen mag, dass man ein Byte-Array problemlos in ein UTF16-String speichern kann, ist dem nicht so. Oder wie Shawn Farkas in seinem Artikel zum Thema schrieb: "just because these encodings use all eight bits of a byte doesn't mean that every arbitrary sequence of bytes represents a valid character in them".

    Der Fehler wird leider oft erst beim Roundtripping sichtbar, also wenn man versucht, den Text wieder zu dekodieren. Die Lösung ist ganz einfach: Man verwendet einen base64 kodierten String für die Zwischenspeicherung, da diese Kodierung den verschlüsselten Byte-Puffer verlustlos speichern kann:

    public static string Verschluesseln(string base64Key, string base64Data)
    {
        // Übergebene Schlüssel- und Klartext-Parameter aus base64 dekodieren
        IBuffer bufferKey = CryptographicBuffer.DecodeFromBase64String(base64Key);
        IBuffer bufferData = CryptographicBuffer.DecodeFromBase64String(base64Data);
    
        // RC4-Schlüsselalgorithmus-Provider erstellen
        SymmetricKeyAlgorithmProvider rc4Provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.Rc4);
    
        // Symmetrischen Schlüssel erstellen anhand des dekodierten Schlüssel-Puffers erstellen
        CryptographicKey symmetricKey =  rc4Provider.CreateSymmetricKey(bufferKey);
    
        // Verschlüsseln
        IBuffer bufferEncrypted = CryptographicEngine.Encrypt(symmetricKey, bufferData, null);
    
        // Verschlüsselten Puffer nach base64 kodieren
        string base64Encoded = CryptographicBuffer.EncodeToBase64String(bufferEncrypted);
    
        return base64Encoded;
    }
    
    public static string Entschluesseln(string base64Key, string base64Data)
    {
        // Übergebene Schlüssel- und Klartext-Parameter aus base64 dekodieren
        IBuffer bufferKey = CryptographicBuffer.DecodeFromBase64String(base64Key);
        IBuffer bufferData = CryptographicBuffer.DecodeFromBase64String(base64Data);
    
        // RC4-Schlüsselalgorithmus-Provider erstellen
        SymmetricKeyAlgorithmProvider rc4Provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.Rc4);
                
        // Symmetrischen Schlüssel erstellen anhand des dekodierten Schlüssel-Puffers erstellen
        CryptographicKey symmetricKey = rc4Provider.CreateSymmetricKey(bufferKey);
    
        // Entschlüsseln
        IBuffer bufferDecrypted = CryptographicEngine.Decrypt(symmetricKey, bufferData, null);
    
        // Entschlüsselter Puffer in ein String konvertieren.
        string decryptedString = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16LE, bufferDecrypted);
    
        return decryptedString;
    }
    

    Hier ein Beispiel-Aufruf:

    string key = "Secret";               
    string plaintext = "Attack at dawn"; 
    
    // Schlüssel zu base64 codieren
    var keyBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf16LE);
    var base64Key = CryptographicBuffer.EncodeToBase64String(keyBuffer);
    
    // Klartext zu base64 codieren
    var plaintextBuffer = CryptographicBuffer.ConvertStringToBinary(plaintext, BinaryStringEncoding.Utf16LE);
    var base64Buffer = CryptographicBuffer.EncodeToBase64String(plaintextBuffer);
    
    // Verschlüsseln
    var encryptedBase64String = Verschluesseln(base64Key, base64Buffer);
    var decryptedPlaintext = Entschluesseln(base64Key, encryptedBase64String);
    

    Zum Schluss noch einige Bemerkungen:

    Schlüssel und Daten liegen in deinem Code mehrfach im Speicher, was ein Sicherheitsrisiko darstellt. Du solltest darauf achten, möglichst wenige und nur lokale Kopien davon zu behalten und diese sofort nach der Verwendung zu überschreiben. Der GC wird sich um die Bereinigung des Speichers kümmern.

    In deinem Code werden zwei Hilfsmethoden verwendet (strErweitern() und strReduzieren()). Über deren Sinn kann ich anhand des Kommentars nur mutmaßen. Da RC4 stream- und nicht blockorientiert arbeitet, muss kein Padding (Vielfaches der Blockgröße) angewandt werden.

    Über das Speichern von "Sonderzeichen" mußt Du dir keine Sorgen machen, solange Du UTF-16 Strings verwendest.

    Dein Problem tritt zwar in einer Windows 8 App auf, hat aber eher wenig damit oder mit WinRT zu tun.

    Gruß
    Marcel


    Donnerstag, 10. Januar 2013 11:15
    Moderator

Alle Antworten

  • Hallo Hefeteig,

    Obwohl es so aussehen mag, dass man ein Byte-Array problemlos in ein UTF16-String speichern kann, ist dem nicht so. Oder wie Shawn Farkas in seinem Artikel zum Thema schrieb: "just because these encodings use all eight bits of a byte doesn't mean that every arbitrary sequence of bytes represents a valid character in them".

    Der Fehler wird leider oft erst beim Roundtripping sichtbar, also wenn man versucht, den Text wieder zu dekodieren. Die Lösung ist ganz einfach: Man verwendet einen base64 kodierten String für die Zwischenspeicherung, da diese Kodierung den verschlüsselten Byte-Puffer verlustlos speichern kann:

    public static string Verschluesseln(string base64Key, string base64Data)
    {
        // Übergebene Schlüssel- und Klartext-Parameter aus base64 dekodieren
        IBuffer bufferKey = CryptographicBuffer.DecodeFromBase64String(base64Key);
        IBuffer bufferData = CryptographicBuffer.DecodeFromBase64String(base64Data);
    
        // RC4-Schlüsselalgorithmus-Provider erstellen
        SymmetricKeyAlgorithmProvider rc4Provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.Rc4);
    
        // Symmetrischen Schlüssel erstellen anhand des dekodierten Schlüssel-Puffers erstellen
        CryptographicKey symmetricKey =  rc4Provider.CreateSymmetricKey(bufferKey);
    
        // Verschlüsseln
        IBuffer bufferEncrypted = CryptographicEngine.Encrypt(symmetricKey, bufferData, null);
    
        // Verschlüsselten Puffer nach base64 kodieren
        string base64Encoded = CryptographicBuffer.EncodeToBase64String(bufferEncrypted);
    
        return base64Encoded;
    }
    
    public static string Entschluesseln(string base64Key, string base64Data)
    {
        // Übergebene Schlüssel- und Klartext-Parameter aus base64 dekodieren
        IBuffer bufferKey = CryptographicBuffer.DecodeFromBase64String(base64Key);
        IBuffer bufferData = CryptographicBuffer.DecodeFromBase64String(base64Data);
    
        // RC4-Schlüsselalgorithmus-Provider erstellen
        SymmetricKeyAlgorithmProvider rc4Provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.Rc4);
                
        // Symmetrischen Schlüssel erstellen anhand des dekodierten Schlüssel-Puffers erstellen
        CryptographicKey symmetricKey = rc4Provider.CreateSymmetricKey(bufferKey);
    
        // Entschlüsseln
        IBuffer bufferDecrypted = CryptographicEngine.Decrypt(symmetricKey, bufferData, null);
    
        // Entschlüsselter Puffer in ein String konvertieren.
        string decryptedString = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf16LE, bufferDecrypted);
    
        return decryptedString;
    }
    

    Hier ein Beispiel-Aufruf:

    string key = "Secret";               
    string plaintext = "Attack at dawn"; 
    
    // Schlüssel zu base64 codieren
    var keyBuffer = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf16LE);
    var base64Key = CryptographicBuffer.EncodeToBase64String(keyBuffer);
    
    // Klartext zu base64 codieren
    var plaintextBuffer = CryptographicBuffer.ConvertStringToBinary(plaintext, BinaryStringEncoding.Utf16LE);
    var base64Buffer = CryptographicBuffer.EncodeToBase64String(plaintextBuffer);
    
    // Verschlüsseln
    var encryptedBase64String = Verschluesseln(base64Key, base64Buffer);
    var decryptedPlaintext = Entschluesseln(base64Key, encryptedBase64String);
    

    Zum Schluss noch einige Bemerkungen:

    Schlüssel und Daten liegen in deinem Code mehrfach im Speicher, was ein Sicherheitsrisiko darstellt. Du solltest darauf achten, möglichst wenige und nur lokale Kopien davon zu behalten und diese sofort nach der Verwendung zu überschreiben. Der GC wird sich um die Bereinigung des Speichers kümmern.

    In deinem Code werden zwei Hilfsmethoden verwendet (strErweitern() und strReduzieren()). Über deren Sinn kann ich anhand des Kommentars nur mutmaßen. Da RC4 stream- und nicht blockorientiert arbeitet, muss kein Padding (Vielfaches der Blockgröße) angewandt werden.

    Über das Speichern von "Sonderzeichen" mußt Du dir keine Sorgen machen, solange Du UTF-16 Strings verwendest.

    Dein Problem tritt zwar in einer Windows 8 App auf, hat aber eher wenig damit oder mit WinRT zu tun.

    Gruß
    Marcel


    Donnerstag, 10. Januar 2013 11:15
    Moderator
  • Oh ja,

    Vielen Dank,

    im Prinzip hab ichs auch so gemacht, aber um das utf16 encoding an den richtigen Stellen zu verwenden hätte ich noch ewig rumprobiert.

    Jetzt funktionierts auf jeden Fall.

    Nochmal: Vielen Dank

    Gruß Hefeteig


    Fischertechnik Fan

    Freitag, 11. Januar 2013 15:13