Verwenden von Verschlüsselung mit SQL-Datenbanken

Adobe AIR 1.5 und höher

Alle Adobe AIR-Anwendungen verwenden dieselbe lokale Datenbank-Engine. Dementsprechend kann jede AIR-Anwendung eine Verbindung mit einer unverschlüsselten Datenbankdatei herstellen, aus der Datenbank lesen und in diese schreiben. Ab Adobe AIR 1.5 verfügt AIR über die Möglichkeit, verschlüsselte Datenbankdateien zu erstellen bzw. eine Verbindung dazu herzustellen. Wenn Sie eine verschlüsselte Datenbank verwenden, muss eine Anwendung den richten Verschlüsselungsschlüssel bereitstellen, um eine Verbindung zur Datenbank herzustellen. Wenn der falsche Verschlüsselungschlüssel (oder kein Verschlüsselungsschlüssel) angegeben wird, kann die Anwendung keine Verbindung zur Datenbank herstellen. Deshalb kann die Anwendung keine Daten aus der Datenbank lesen und keine Daten in die Datenbank schreiben bzw. darin ändern.

Um eine verschlüsselte Datenbank zu verwenden, müssen Sie die Datenbank als verschlüsselte Datenbank erstellen. Wenn Sie eine vorhandene verschlüsselte Datenbank verwenden, können Sie eine Verbindung zu der Datenbank herstellen. Sie können auch den Verschlüsselungsschlüssel einer verschlüsselten Datenbank ändern. Abgesehen vom Erstellen der verschlüsselten Datenbank und dem Herstellen der Verbindung zu einer verschlüsselten Datenbank unterscheidet sich die Arbeit mit einer verschlüsselten Datenbank nicht von der Arbeit mit einer nicht verschlüsselten Datenbank. Insbesondere die Ausführung von SQL-Anweisungen ist identisch, unabhängig davon, ob die Datenbank verschlüsselt ist oder nicht.

Verwendungszwecke einer verschlüsselten Datenbank

Die Verschlüsselung ist immer dann sinnvoll, wenn Sie den Zugriff auf Informationen, die in einer Datenbank gespeichert sind, beschränken möchten. Die Datenbankverschlüsselung von Adobe AIR lässt sich für verschiedene Zwecke einsetzen. Einige Beispiele für Situationen, die sich für verschlüsselte Datenbanken anbieten, sind folgende:

  • Ein schreibgeschützter Cachespeicher von privaten Anwendungsdaten, die von einem Server heruntergeladen werden.

  • Ein lokaler Anwendungsspeicher für private Daten, die mit einem Server synchronisiert werden (Daten werden an einen Server gesendet und vom Server heruntergeladen).

  • Verschlüsselte Dateien, die als Dateiformat für Dokumente verwendet werden, die in der Anwendung erstellt und bearbeitet werden. Die Dateien könnten für einen bestimmten Benutzer gedacht sein oder von allen Benutzern der Anwendung gemeinsam genutzt werden.

  • Jede beliebige andere Verwendung eines lokalen Datenspeichers, wie zum Beispiel unter Verwendungszwecke lokaler Datenbanken beschrieben, bei der die Daten vor Personen, die Zugriff auf den Computer oder die Datenbankdateien haben, geschützt werden sollen.

Wenn Ihnen klar ist, warum Sie eine verschlüsselte Datenbank verwenden möchten, können Sie leichter entscheiden, wie Sie Ihre Anwendung aufbauen. Insbesondere hat dies Auswirkungen darauf, wie Ihre Anwendung den Verschlüsselungsschlüssel für die Datenbank erstellt, erhält und speichert. Weitere Informationen zu diesen Aspekten finden Sie unter Überlegungen zur Verschlüsselung von Datenbanken .

Eine Alternativmethode zum Schutz vertraulicher Daten ist die Verwendung eines verschlüsselten lokalen Speichers . Dabei wird ein einzelner ByteArray -Wert mit einem Stringschlüssel gespeichert. Nur die AIR-Anwendung, die den Wert gespeichert hat, kann darauf zugreifen, und auch dies nur auf dem Computer, auf dem der Wert gespeichert wurde. Wenn Sie den verschlüsselten lokalen Speicher verwenden, brauchen Sie keinen eigenen Verschlüsselungsschlüssel zu erstellen. Aus diesem Grund eignet sich der verschlüsselte lokale Speicher am besten zum einfachen Speichern eines einzelnen Werts oder einer Gruppe von Werten, die sich unkompliziert in einem ByteArray kodieren lassen. Eine verschlüsselte Datenbank eignet sich am besten für größere Datenmengen, für die eine strukturierte Datenspeicherung und Abfragefunktionen erwünscht sind. Weitere Informationen zur Verwendung des verschlüsselten lokalen Datenspeichers finden Sie unter Verschlüsselter lokaler Speicher .

Erstellen einer verschlüsselten Datenbank

Die Datenbankdatei muss beim Erstellen verschlüsselt werden, wenn eine verschlüsselte Datenbank verwendet werden soll. Wenn eine Datenbank unverschlüsselt erstellt wird, kann sie später nicht verschlüsselt werden. Dies gilt auch im umgekehrten Fall; eine verschlüsselte Datenbank kann später nicht zu einer unverschlüsselten gemacht werden. Bei Bedarf können Sie den Verschlüsselungsschlüssel einer verschlüsselten Datenbank ändern. Ausführliche Informationen finden Sie unter Ändern des Verschlüsselungsschlüssels einer Datenbank . Wenn Sie eine vorhandene nicht verschlüsselte Datenbank mit Datenbankverschlüsselung nutzen möchten, erstellen Sie eine neue verschlüsselte Datenbank und kopieren Sie die vorhandene Tabellenstruktur und die Daten in die neue Datenbank.

Das Erstellen einer verschlüsselten Datenbank ist fast identisch mit dem Erstellen einer unverschlüsselten Datenbank wie unter Erstellen von Datenbanken beschrieben. Sie erstellen zunächst eine SQLConnection -Instanz, die die Verbindung zur Datenbank darstellt. Sie erstellen die Datenbank durch einen Aufruf der open() - oder openAsync() -Methode des SQLConnection-Objekts. Geben Sie dabei für den Speicherort der Datenbank eine noch nicht vorhandene Datei an. Der einzige Unterschied beim Erstellen einer verschlüsselten Datenbank besteht darin, dass Sie einen Wert für den encryptionKey -Parameter angeben (dies ist der fünfte Parameter der open() -Methode und der sechste Parameter der openAsync() -Methode).

Ein gültiger Wert für den encryptionKey -Parameter ist ein ByteArray -Objekt, das genau 16 Byte enthält.

Die folgenden Beispiele veranschaulichen die Erstellung einer verschlüsselten Datenbank. Der Einfachheit halber ist der Verschlüsselungsschlüssel in diesen Beispielen im Anwendungscode fest vorgegeben. Von dieser Methode wird jedoch dringend abgeraten, da sie nicht sicher ist.

var conn:SQLConnection = new SQLConnection(); 
     
var encryptionKey:ByteArray = new ByteArray(); 
encryptionKey.writeUTFBytes("Some16ByteString"); // This technique is not secure! 
     
// Create an encrypted database in asynchronous mode 
conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey); 
     
// Create an encrypted database in synchronous mode 
conn.open(dbFile, SQLMode.CREATE, false, 1024, encryptionKey);

Ein Beispiel für das empfohlene Verfahren zum Generieren eines Verschlüsselungsschlüssels finden Sie unter Beispiel: Generieren und Verwenden von Verschlüsselungsschlüsseln .

Herstellen einer Verbindung mit einer verschlüsselten Datenbank

Wie beim Erstellen von Datenbanken gibt es auch beim Herstellen einer Verbindung zu einer unverschlüsselten bzw. zu einer verschlüsselten Datenbank große Ähnlichkeiten. Das Verfahren wird ausführlicher unter Herstellen von Verbindungen mit Datenbanken beschrieben. Sie verwenden die open() -Methode zum Öffnen einer Verbindung im synchronen Ausführungsmodus oder die openAsync() -Methode zum Öffnen einer Verbindung im asynchronen Ausführungsmodus . Der einzige Unterschied besteht darin, dass Sie beim Öffnen einer verschlüsselten Datenbank den richtigen Wert für den encryptionKey -Parameter angeben müssen (dies ist der fünfte Parameter der open() -Methode und der sechste Parameter der openAsync() -Methode).

Wenn Sie einen falschen Verschlüsselungsschlüssel angeben, wird ein Fehler ausgegeben. Für die open() -Methode wird eine SQLError -Ausnahme ausgegeben. Für die openAsync() -Methode löst das SQLConnection -Objekt ein SQLErrorEvent -Ereignis aus, dessen error -Eigenschaft ein SQLError -Objekt enthält. In beiden Fällen hat das von der Ausnahme generierte SQLError-Objekt die errorID -Eigenschaft mit dem Wert 3138. Diese Fehler-ID entspricht der Fehlermeldung „File opened is not a database file“ (Die geöffnete Datei ist keine Datenbankdatei).

Im folgenden Beispiel wird das Öffnen einer verschlüsselten Datenbank im asynchronen Ausführungsmodus beschrieben. Der Einfachheit halber ist der Verschlüsselungsschlüssel in diesem Beispiel im Anwendungscode fest vorgegeben. Von dieser Methode wird jedoch dringend abgeraten, da sie nicht sicher ist.

import flash.data.SQLConnection; 
import flash.data.SQLMode; 
import flash.events.SQLErrorEvent; 
import flash.events.SQLEvent; 
import flash.filesystem.File; 
     
var conn:SQLConnection = new SQLConnection(); 
conn.addEventListener(SQLEvent.OPEN, openHandler); 
conn.addEventListener(SQLErrorEvent.ERROR, errorHandler); 
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db"); 
     
var encryptionKey:ByteArray = new ByteArray(); 
encryptionKey.writeUTFBytes("Some16ByteString"); // This technique is not secure! 
     
conn.openAsync(dbFile, SQLMode.UPDATE, null, false, 1024, encryptionKey); 
     
function openHandler(event:SQLEvent):void 
{ 
    trace("the database opened successfully"); 
} 
     
function errorHandler(event:SQLErrorEvent):void 
{ 
    if (event.error.errorID == 3138) 
    { 
        trace("Incorrect encryption key"); 
    } 
    else 
    { 
        trace("Error message:", event.error.message); 
        trace("Details:", event.error.details); 
    } 
} 

Im folgenden Beispiel wird das Öffnen einer verschlüsselten Datenbank im synchronen Ausführungsmodus beschrieben. Der Einfachheit halber ist der Verschlüsselungsschlüssel in diesem Beispiel im Anwendungscode fest vorgegeben. Von dieser Methode wird jedoch dringend abgeraten, da sie nicht sicher ist.

import flash.data.SQLConnection; 
import flash.data.SQLMode; 
import flash.filesystem.File; 
     
var conn:SQLConnection = new SQLConnection(); 
var dbFile:File = File.applicationStorageDirectory.resolvePath("DBSample.db"); 
     
var encryptionKey:ByteArray = new ByteArray(); 
encryptionKey.writeUTFBytes("Some16ByteString"); // This technique is not secure! 
     
try 
{ 
    conn.open(dbFile, SQLMode.UPDATE, false, 1024, encryptionKey); 
    trace("the database was created successfully"); 
} 
catch (error:SQLError) 
{ 
    if (error.errorID == 3138) 
    { 
        trace("Incorrect encryption key"); 
    } 
    else 
    { 
        trace("Error message:", error.message); 
        trace("Details:", error.details); 
    } 
} 

Ein Beispiel für das empfohlene Verfahren zum Generieren eines Verschlüsselungsschlüssels finden Sie unter Beispiel: Generieren und Verwenden von Verschlüsselungsschlüsseln .

Ändern des Verschlüsselungsschlüssels einer Datenbank

Wenn eine Datenbank verschlüsselt ist, können Sie den Verschlüsselungsschlüssel auch zu einem späteren Zeitpunkt ändern. Um den Verschlüsselungsschlüssel einer Datenbank zu ändern, öffnen Sie zunächst eine Verbindung mit der Datenbank, indem Sie eine SQLConnection -Instanz erstellen und ihre open() - oder openAsync() -Methode aufrufen. Wenn die Verbindung zur Datenbank hergestellt wurde, rufen Sie die reencrypt() -Methode auf und übergeben den neuen Verschlüsselungsschlüssel als Argument.

Wie bei den meisten Datenbankoperationen richtet sich das Verhalten der reencrypt() -Methode danach, ob die Datenbankverbindung den synchronen oder den asynchronen Ausführungsmodus verwendet. Wenn Sie die Verbindung zur Datenbank mit der open() -Methode herstellen, wird die reencrypt() -Operation synchron ausgeführt. Wenn die Operation abgeschlossen ist, wird die Ausführung mit der nächsten Codezeile fortgesetzt:

var newKey:ByteArray = new ByteArray(); 
// ... generate the new key and store it in newKey 
conn.reencrypt(newKey);

Wird die Verbindung zur Datenbank dagegen mit der openAsync() -Methode hergestellt, wird die reencrypt() -Operation asynchron ausgeführt. Mit dem Aufruf von reencrypt() beginnt die Neuverschlüsselung. Nach Abschluss der Operation löst das SQLConnection-Objekt ein reencrypt -Ereignis aus. Sie verwenden einen Ereignis-Listener, um festzustellen, wann die Neuverschlüsselung abgeschlossen ist:

var newKey:ByteArray = new ByteArray(); 
// ... generate the new key and store it in newKey 
     
conn.addEventListener(SQLEvent.REENCRYPT, reencryptHandler); 
     
conn.reencrypt(newKey); 
     
function reencryptHandler(event:SQLEvent):void 
{ 
    // save the fact that the key changed 
}

Die reencrypt() -Operation wird in einer eigenen Transaktion ausgeführt. Wenn die Operation unterbrochen wird oder fehlschlägt (zum Beispiel, weil die Anwendung vor Abschluss der Operation beendet wird), wird die Transaktion zurückgenommen. In diesem Fall ist der ursprüngliche Verschlüsselungsschlüssel weiterhin der gültige Verschlüsselungsschlüssel für die Datenbank.

Mit der reencrypt() -Methode kann der Verschlüsselungsschlüssel nicht von der Datenbank entfernt werden. Wenn Sie einen null -Wert oder einen Verschlüsselungsschlüssel, der kein 16-Byte-ByteArray ist, an die reencrypt() -Methode übergeben, wird ein Fehler ausgegeben.

Überlegungen zur Verschlüsselung von Datenbanken

Im Abschnitt Verwendungszwecke einer verschlüsselten Datenbank sind verschiedene Fälle aufgeführt, in denen die Verwendung einer verschlüsselten Datenbank angebracht ist. Selbstverständlich gelten für unterschiedliche Verwendungsszenarien unterschiedliche Datenschutzanforderungen. Wie Sie den Einsatz von Verschlüsselung in Ihre Anwendung einbinden, spielt eine wichtige Rolle beim Schutz der Daten in einer Datenbank. Wenn Sie zum Beispiel eine verschlüsselte Datenbank verwenden, um private Daten zu schützen, auch vor Benutzern desselben Computers, benötigt die Datenbank der einzelnen Benutzer jeweils einen eigenen Verschlüsselungsschlüssel. Die höchstmögliche Sicherheit erzielen Sie, wenn die Anwendung den Schlüssel aus einem vom Benutzer eingegebenen Kennwort generieren kann. Wenn der Verschlüsselungsschlüssel auf einem Kennwort basiert, wird sichergestellt, dass auch dann kein Zugriff auf die Daten möglich ist, wenn eine andere Person das Konto des Benutzers auf dem Computer verwendet. Ein anderes Szenario liegt vor, wenn eine Datenbankdatei von allen Benutzern Ihrer Anwendung, aber nicht von anderen Anwendungen gelesen werden soll. In diesem Fall benötigt jede installierte Kopie der Anwendung Zugriff auf einen gemeinsam genutzten Verschlüsselungsschlüssel.

Sie können Ihre Anwendung und insbesondere das Verfahren zum Generieren des Verschlüsselungsschlüssels entsprechend Ihren Anforderungen für den Schutz der Anwendungsdaten entwerfen. In der folgenden Liste finden Sie verschiedene Vorschläge für unterschiedliche Stufen des Datenschutzes:

  • Damit eine Datenbank für jeden Benutzer, der Zugriff auf die Anwendung hat, auf jedem Computer zugänglich ist, verwenden Sie einen einzelnen Schlüssel, der allen Instanzen der Anwendung zur Verfügung steht. Zum Beispiel kann eine Anwendung, wenn sie zum ersten Mal ausgeführt wird, den gemeinsam genutzten Verschlüsselungsschlüssel über ein sicheres Protokoll wie SSL von einem Server herunterladen. Der Schlüssel kann dann zur späteren Verwendung im verschlüsselten lokalen Speicher gespeichert werden. Alternativ dazu können Sie die Daten auf dem Computer auf Benutzerbasis verschlüsseln und die Daten mit einem Remote-Datenspeicher, zum Beispiel einem Server, synchronisieren, damit die Daten übertragbar sind.

  • Damit die Daten nur für einen einzelnen Benutzer auf einem Computer zugänglich sind, generieren Sie den Verschlüsselungsschlüssel aus einer benutzerspezifischen geheimen Eingabe (zum Beispiel aus einem Kennwort). Verwenden Sie keinen Wert, der einem bestimmten Computer zugeordnet ist (zum Beispiel ein Wert, der im verschlüsselten lokalen Speicher gespeichert ist), um den Schlüssel zu generieren. Alternativ dazu können Sie die Daten auf dem Computer auf Benutzerbasis verschlüsseln und die Daten mit einem Remote-Datenspeicher, zum Beispiel einem Server, synchronisieren, damit die Daten übertragbar sind.

  • Damit eine Datenbank nur einer einzelnen Person auf einem einzelnen Computer zugänglich ist, generieren Sie den Schlüssel aus einem Kennwort und einem „Salt“ (zufällig gewählte Bitfolge). Ein Beispiel für diese Vorgehensweise finden Sie unter Beispiel: Generieren und Verwenden von Verschlüsselungsschlüsseln .

Nachstehend sind zusätzliche Sicherheitsüberlegungen aufgeführt, die beim Entwickeln einer Anwendung zu beachten sind, die eine verschlüsselte Datenbank verwendet:

  • Ein System ist immer nur so sicher wie sein schwächster Bestandteil. Wenn der Verschlüsselungsschlüssel anhand eines vom Benutzer eingegebenen Kennworts generiert wird, ziehen Sie in Betracht, Mindestlänge und Komplexitätsstufe des Kennworts festzulegen. Ein kurzes Kennwort, das nur Buchstaben oder Ziffern enthält, ist relativ leicht herauszufinden.

  • Der Quellcode einer AIR-Anwendung wird als einfacher Text (für HTML-Inhalte) oder im leicht zu dekompilierenden Binärformat (für SWF-Inhalt) auf dem Computer des Benutzers gespeichert. Da der Quellcode zugänglich ist, beachten Sie diese zwei Punkte:

    • Geben Sie Verschlüsselungsschlüssel nie mit Hard-Coding im Quellcode an.

    • Gehen Sie immer davon aus, dass Angreifer das zum Generieren des Verschlüsselungsschlüssels verwendete Verfahren (zum Beispiel durch einen Zeichenzufallsgenerator oder einen bestimmten Hashing-Algorithmus) leicht herausfinden können.

  • Für die AIR-Datenbankverschlüsselung wird der Advanced Encryption Standard (AES) mit Zähler im CBC-MAC-Modus (CCM) verwendet. Bei dieser Verschlüsselungsart muss ein vom Benutzer eingegebener Schlüssel mit einem Salt-Wert kombiniert werden, damit er sicher ist. Ein Beispiel für diese Vorgehensweise finden Sie unter Beispiel: Generieren und Verwenden von Verschlüsselungsschlüsseln .

  • Wenn Sie eine Datenbank verschlüsseln, werden alle Festplattendateien verschlüsselt, die von der Datenbankengine zusammen mit dieser Datenbank verwendet werden. Die Datenbankengine speichert einige Daten jedoch in einem Arbeitsspeicher-Cache, um die Lese- und Schreibzeitleistung bei Transaktionen zu verbessern. Alle speicherresidenten Daten bleiben unverschlüsselt. Wenn ein Angreifer auf den von einer AIR-Anwendung verwendeten Arbeitsspeicher zugreifen kann, zum Beispiel durch Verwenden eines Debuggers, sind die Daten in einer geöffneten, unverschlüsselten Datenbank zugänglich.

Beispiel: Generieren und Verwenden von Verschlüsselungsschlüsseln

In dieser Beispielanwendung wird ein Verfahren zum Generieren eines Verschlüsselungsschlüssels veranschaulicht. Diese Anwendung soll die höchste Datenschutz- und Sicherheitsstufe für Benutzerdaten bieten. Ein wichtiger Aspekt beim Sichern privater Daten ist es, vom Benutzer zu verlangen, dass er jedes Mal ein Kennwort eingeben muss, wenn die Anwendung eine Verbindung zur Datenbank herstellt. Demzufolge sollte eine Anwendung, die diese Datenschutzstufe erfordert, den Verschlüsselungsschlüssel der Datenbank nie direkt speichern, wie in diesem Beispiel gezeigt.

Die Anwendung besteht aus zwei Teilen: einer ActionScript-Klasse, die einen Verschlüsselungsschlüssel generiert (die EncryptionKeyGenerator-Klasse), und einer Basisbenutzeroberfläche, die die Verwendung dieser Klasse veranschaulicht. Den vollständigen Quellcode finden Sie unter Vollständiger Beispielcode für das Generieren und Verwenden eines Verschlüsselungsschlüssels .

Beziehen eines sicheren Verschlüsselungsschlüssels mit der EncryptionKeyGenerator-Klasse

Ein detailliertes Verständnis der EncryptionKeyGenerator-Klasse ist nicht erforderlich, damit Sie diese Klasse in Ihrer Anwendung einsetzen können. Wenn Sie daran interessiert sind, wie die Klasse einen Verschlüsselungsschlüssel für eine Datenbank erstellt, lesen Sie Informationen über die EncryptionKeyGenerator-Klasse .

Gehen Sie folgendermaßen vor, um die EncryptionKeyGenerator-Klasse in Ihrer Anwendung zu verwenden:

  1. Laden Sie die EncryptionKeyGenerator-Klasse als Quellcode oder eine kompilierte SWC-Datei herunter. Die EncryptionKeyGenerator-Klasse ist im Opensource-Projekt für die ActionScript 3.0-Kernbibliothek (as3corelib) enthalten. Sie können das as3corelib-Paket einschließlich Quellcode und Dokumentation herunterladen. Sie können die SWC- oder Quellcodedateien auch von der Projektseite herunterladen.

  2. Legen Sie den Quellcode für die EncryptionKeyGenerator-Klasse (oder die as3corelib-SWC) an einem Speicherort ab, an dem der Quellcode Ihrer Anwendung ihn finden kann.

  3. Fügen Sie in Ihrem Anwendungsquellcode eine import -Anweisung für die EncryptionKeyGenerator-Klasse hinzu.

    import com.adobe.air.crypto.EncryptionKeyGenerator;
  4. Fügen Sie vor der Stelle, an der der Code die Datenbank erstellt oder eine Verbindung zu ihr herstellt, Code zum Erstellen einer EncryptionKeyGenerator-Instanz hinzu, indem der EncryptionKeyGenerator() -Konstruktor aufgerufen wird.

    var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator();
  5. Beziehen Sie ein Kennwort vom Benutzer:

    var password:String = passwordInput.text; 
     
    if (!keyGenerator.validateStrongPassword(password)) 
    { 
        // display an error message 
        return; 
    }

    Die EncryptionKeyGenerator-Instanz verwendet dieses Kennwort als Grundlage für den Verschlüsselungsschlüssel (siehe nächster Schritt). Die EncryptionKeyGenerator-Instanz testet das Kennwort anhand bestimmter Validierungsanforderungen für sichere Kennwörter. Wenn die Validierung fehlschlägt, kommt es zu einem Fehler. Wie der Beispielcode zeigt, können Sie das Kennwort frühzeitig überprüfen, indem die validateStrongPassword() -Methode des EncryptionKeyGenerator-Objekts aufgerufen wird. Auf diese Weise können Sie feststellen, ob das Kennwort die Mindestanforderungen für ein sicheres Kennwort erfüllt und einen Fehler vermeiden.

  6. Genieren Sie den Verschlüsselungsschlüssel aus dem Kennwort:

    var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);

    Die getEncryptionKey() -Methode generiert den Verschlüsselungsschlüssel (ein 16-Byte-ByteArray) und gibt ihn zurück. Sie können den Verschlüsselungsschlüssel dann verwenden, um die neue verschlüsselte Datenbank zu erstellen bzw. die vorhandene Datenbank zu öffnen.

    Die getEncryptionKey() -Methode hat einen erforderlichen Parameter; dies ist das in Schritt 5 eingeholte Kennwort.

    Hinweis: Um die höchste Stufe von Sicherheit und Datenschutz zu erhalten, muss eine Anwendung vom Benutzer verlangen, jedes Mal ein Kennwort einzugeben, wenn eine Verbindung zur Datenbank hergestellt werden soll. Speichern Sie das Benutzerkennwort oder den Datenbankverschlüsselungsschlüssel nicht direkt. Andernfalls besteht ein Sicherheitsrisiko. Stattdessen sollte eine Anwendung, wie im folgenden Beispiel, beim Herstellen einer Datenbankverbindung dasselbe Verfahren zum Ableiten des Verschlüsselungsschlüssels aus dem Kennwort verwenden wie zuvor beim Erstellen der Datenbank.

    Die getEncryptionKey() -Methode akzeptiert auch einen zweiten (optionalen) Parameter, den overrideSaltELSKey -Parameter. Der EncryptionKeyGenerator erstellt einen zufälligen Wert ( Salt genannt), der als Teil des Verschlüsselungsschlüssels verwendet wird. Damit der Verschlüsselungsschlüssel reproduziert werden kann, wird der Saltwert im verschlüsselten lokalen Speicher (Encrypted Local Store, ELS) der AIR-Anwendung gespeichert. Standardmäßig verwendet die EncryptionKeyGenerator-Klasse einen bestimmten String als ELS-Schlüssel. Es ist zwar unwahrscheinlich, aber nicht unmöglich, dass der Schlüssel mit einem anderen von der Anwendung verwendeten Schlüssel in Konflikt steht. Anstatt den Standardschüssel zu verwenden, können Sie Ihren eigenen ELS-Schlüssel angeben. Geben Sie in diesem Fall einen benutzerdefinierten Schlüssel an, indem Sie ihn als zweiten getEncryptionKey() -Parameter übergeben wie hier gezeigt:

    var customKey:String = "My custom ELS salt key"; 
    var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password, customKey);
  7. Erstellen oder Öffnen der Datenbank

    Mit einem von der getEncryptionKey() -Methode zurückgegebenen Verschlüsselungsschlüssel kann Ihr Code eine neue verschlüsselte Datenbank erstellen oder versuchen, eine Verbindung zu einer vorhandenen verschlüsselten Datenbank zu herzustellen. In beiden Fällen wird die open() - oder openAsync() -Methode der SQLConnection-Klasse verwendet. Dies wird unter Erstellen einer verschlüsselten Datenbank und Herstellen einer Verbindung mit einer verschlüsselten Datenbank beschrieben.

    In diesem Beispiel wurde die Anwendung so erstellt, dass die Datenbank im asynchronen Ausführungsmodus geöffnet wird. Der Code richtet die entsprechenden Ereignis-Listener ein und ruft die openAsync() -Methode auf, wobei der Verschlüsselungsschlüssel als abschließendes Argument übergeben wird:

    conn.addEventListener(SQLEvent.OPEN, openHandler); 
    conn.addEventListener(SQLErrorEvent.ERROR, openError); 
     
    conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey);

    In den Listener-Methoden entfernt der Code die Ereignis-Listener-Registrierungen. Dann wird eine Statusmeldung angezeigt, die angibt, ob die Datenbank erstellt oder geöffnet wurde oder ob ein Fehler aufgetreten ist. Der interessanteste Tel dieser Ereignisprozeduren befindet sich in der openError() -Methode. In dieser Methode überprüft eine if -Anweisung, ob die Datenbank vorhanden ist (ob also versucht wird, eine Verbindung zu einer vorhandenen Datenbank herzustellen) und ob die Fehler-ID mit der Konstante EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID übereinstimmt. Wenn beide Bedingungen zutreffen, bedeutet dies wahrscheinlich, dass das vom Benutzer eingegebene Kennwort falsch ist. (Es könnte jedoch auch bedeuten, dass die angegebene Datei überhaupt keine Datenbankdatei ist.) Mit dem nachstehenden Code wird die Fehler-ID überprüft:

    if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID) 
    { 
        statusMsg.text = "Incorrect password!"; 
    } 
    else 
    { 
        statusMsg.text = "Error creating or opening database."; 
    }

    Den vollständigen Code für die Beispiel-Ereignis-Listener finden Sie unter Vollständiger Beispielcode für das Generieren und Verwenden eines Verschlüsselungsschlüssels .

Vollständiger Beispielcode für das Generieren und Verwenden eines Verschlüsselungsschlüssels

Im Folgenden ist der gesamte Code für die Beispielanwendung „Generieren und Verwenden eines Verschlüsselungsschlüssels“ aufgeführt: Der Code besteht aus zwei Teilen.

Im Beispiel wird mit der EncryptionKeyGenerator-Klasse ein Verschlüsselungsschlüssel aus einem Kennwort erstellt. Die EncryptionKeyGenerator-Klasse ist im Opensource-Projekt für die ActionScript 3.0-Kernbibliothek (as3corelib) enthalten. Sie können das as3corelib-Paket einschließlich Quellcode und Dokumentation herunterladen. Sie können die SWC- oder Quellcodedateien auch von der Projektseite herunterladen.

Flex-Beispiel

Die MXML-Datei der Anwendung enthält den Quellcode für eine einfache Anwendung, mit der eine verschlüsselte Datenbank erstellt bzw. eine Verbindung zu dieser Datenbank hergestellt wird:

<?xml version="1.0" encoding="utf-8"?> 
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();"> 
    <mx:Script> 
        <![CDATA[ 
            import com.adobe.air.crypto.EncryptionKeyGenerator; 
             
            private const dbFileName:String = "encryptedDatabase.db"; 
             
            private var dbFile:File; 
            private var createNewDB:Boolean = true; 
            private var conn:SQLConnection; 
             
            // ------- Event handling ------- 
             
            private function init():void 
            { 
                conn = new SQLConnection(); 
                dbFile = File.applicationStorageDirectory.resolvePath(dbFileName); 
                if (dbFile.exists) 
                { 
                    createNewDB = false; 
                    instructions.text = "Enter your database password to open the encrypted database."; 
                    openButton.label = "Open Database"; 
                } 
            } 
             
            private function openConnection():void 
            { 
                var password:String = passwordInput.text; 
                 
                var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator(); 
                 
                if (password == null || password.length <= 0) 
                { 
                    statusMsg.text = "Please specify a password."; 
                    return; 
                } 
                 
                if (!keyGenerator.validateStrongPassword(password)) 
                { 
                    statusMsg.text = "The password must be 8-32 characters long. It must contain at least one lowercase letter, at least one uppercase letter, and at least one number or symbol."; 
                    return; 
                } 
                 
                passwordInput.text = ""; 
                passwordInput.enabled = false; 
                openButton.enabled = false; 
                 
                var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password); 
                 
                conn.addEventListener(SQLEvent.OPEN, openHandler); 
                conn.addEventListener(SQLErrorEvent.ERROR, openError); 
                  
                conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey); 
            } 
             
            private function openHandler(event:SQLEvent):void 
            { 
                conn.removeEventListener(SQLEvent.OPEN, openHandler); 
                conn.removeEventListener(SQLErrorEvent.ERROR, openError); 
                  
                statusMsg.setStyle("color", 0x009900); 
                if (createNewDB) 
                { 
                    statusMsg.text = "The encrypted database was created successfully."; 
                } 
                else 
                { 
                    statusMsg.text = "The encrypted database was opened successfully."; 
                } 
            } 
              
            private function openError(event:SQLErrorEvent):void 
            { 
                conn.removeEventListener(SQLEvent.OPEN, openHandler); 
                conn.removeEventListener(SQLErrorEvent.ERROR, openError); 
     
                if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID) 
                { 
                    statusMsg.text = "Incorrect password!"; 
                } 
                else 
                { 
                    statusMsg.text = "Error creating or opening database."; 
                } 
            } 
        ]]> 
    </mx:Script> 
    <mx:Text id="instructions" text="Enter a password to create an encrypted database. The next time you open the application, you will need to re-enter the password to open the database again." width="75%" height="65"/> 
    <mx:HBox> 
        <mx:TextInput id="passwordInput" displayAsPassword="true"/> 
        <mx:Button id="openButton" label="Create Database" click="openConnection();"/> 
    </mx:HBox> 
    <mx:Text id="statusMsg" color="#990000" width="75%"/> 
</mx:WindowedApplication>

Flash Professional-Beispiel

Die FLA-Datei der Anwendung enthält den Quellcode für eine einfache Anwendung, mit der eine verschlüsselte Datenbank erstellt bzw. eine Verbindung zu dieser Datenbank hergestellt wird: Die FLA-Datei enthält vier auf der Bühne platzierte Komponenten:

Instanzname

Komponententyp

Beschreibung

instructions

Label

Enthält die Anweisungen, die dem Benutzer angezeigt werden

passwordInput

TextInput

Eingabefeld, in das der Benutzer das Kennwort eingibt

openButton

Button

Schaltfläche, auf die der Benutzer nach Eingabe des Kennworts klickt

statusMsg

Label

Zeigt Statusmeldungen (Erfolg oder Fehlschlag) an

Der Code für die Anwendung ist in einem Schlüsselbild in Bild 1 der Hauptzeitleiste definiert. Folgendes ist der Code für die Anwendung:

import com.adobe.air.crypto.EncryptionKeyGenerator; 
     
const dbFileName:String = "encryptedDatabase.db"; 
     
var dbFile:File; 
var createNewDB:Boolean = true; 
var conn:SQLConnection; 
     
init(); 
     
// ------- Event handling ------- 
     
function init():void 
{ 
    passwordInput.displayAsPassword = true; 
    openButton.addEventListener(MouseEvent.CLICK, openConnection); 
    statusMsg.setStyle("textFormat", new TextFormat(null, null, 0x990000)); 
     
    conn = new SQLConnection(); 
    dbFile = File.applicationStorageDirectory.resolvePath(dbFileName); 
     
    if (dbFile.exists) 
    { 
        createNewDB = false; 
        instructions.text = "Enter your database password to open the encrypted database."; 
        openButton.label = "Open Database"; 
    } 
    else 
    { 
        instructions.text = "Enter a password to create an encrypted database. The next time you open the application, you will need to re-enter the password to open the database again."; 
        openButton.label = "Create Database"; 
    } 
} 
     
function openConnection(event:MouseEvent):void 
{ 
    var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator(); 
     
    var password:String = passwordInput.text; 
     
    if (password == null || password.length <= 0) 
    { 
        statusMsg.text = "Please specify a password."; 
        return; 
    } 
     
    if (!keyGenerator.validateStrongPassword(password)) 
    { 
        statusMsg.text = "The password must be 8-32 characters long. It must contain at least one lowercase letter, at least one uppercase letter, and at least one number or symbol."; 
        return; 
    } 
     
    passwordInput.text = ""; 
    passwordInput.enabled = false; 
    openButton.enabled = false; 
     
    var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password); 
     
    conn.addEventListener(SQLEvent.OPEN, openHandler); 
    conn.addEventListener(SQLErrorEvent.ERROR, openError); 
     
    conn.openAsync(dbFile, SQLMode.CREATE, null, false, 1024, encryptionKey); 
} 
     
function openHandler(event:SQLEvent):void 
{ 
    conn.removeEventListener(SQLEvent.OPEN, openHandler); 
    conn.removeEventListener(SQLErrorEvent.ERROR, openError); 
     
    statusMsg.setStyle("textFormat", new TextFormat(null, null, 0x009900)); 
    if (createNewDB) 
    { 
        statusMsg.text = "The encrypted database was created successfully."; 
    } 
    else 
    { 
        statusMsg.text = "The encrypted database was opened successfully."; 
    } 
} 
 
function openError(event:SQLErrorEvent):void 
{ 
    conn.removeEventListener(SQLEvent.OPEN, openHandler); 
    conn.removeEventListener(SQLErrorEvent.ERROR, openError); 
     
    if (!createNewDB && event.error.errorID == EncryptionKeyGenerator.ENCRYPTED_DB_PASSWORD_ERROR_ID) 
    { 
        statusMsg.text = "Incorrect password!"; 
    } 
    else 
    { 
        statusMsg.text = "Error creating or opening database."; 
    } 
}

Informationen über die EncryptionKeyGenerator-Klasse

Es ist nicht notwendig, die Funktionsweise der EncryptionKeyGenerator-Klasse vollständig zu verstehen, um mit ihr einen sicheren Verschlüsselungsschlüssel für die Anwendungsdatenbank zu erstellen. Die Verwendung dieser Klasse wird unter Beziehen eines sicheren Verschlüsselungsschlüssels mit der EncryptionKeyGenerator-Klasse beschrieben. Möglicherweise sind genauere Kenntnisse der von dieser Klasse verwendeten Verfahren aber nützlich. Sie möchten die Klasse vielleicht anpassen oder einige ihrer Verfahren einbauen, wenn eine andere Datenschutzstufe gewünscht wird.

Die EncryptionKeyGenerator-Klasse ist im Opensource-Projekt für die ActionScript 3.0-Kernbibliothek (as3corelib) enthalten. Sie können das as3corelib-Paket einschließlich Quellcode und Dokumentation herunterladen. Sie können die SWC- oder Quellcodedateien auch von der Projektseite herunterladen.

Wenn der Code eine EncryptionKeyGenerator-Instanz erstellt und deren getEncryptionKey() -Methode aufruft, werden verschiedenen Schritte ausgeführt, um sicherzustellen, dass nur berechtigte Benutzer Zugriff auf die Daten haben. Der Prozess ist identisch mit dem Generieren eines Verschlüsselungsschlüssels aus einem vom Benutzer eingegebenen Kennwort vor dem Erstellen der Datenbank bzw. mit dem Reproduzieren des Verschlüsselungsschlüssels vor dem Herstellen einer Verbindung zur Datenbank.

Erstellen und Validieren eines sicheren Kennworts

Wenn Programmcode die getEncryptionKey() -Methode aufruft, wird ein Kennwort als Parameter übergeben. Das Kennwort wird als Basis für den Verschlüsselungsschlüssel verwendet. Indem Informationen verwendet werden, die nur dem Benutzer bekannt sind, wird sichergestellt, dass nur der Benutzer, der das Kennwort kennt, auf die Daten in der Datenbank zugreifen kann. Selbst wenn sich ein Angreifer Zugriff auf das Benutzerkonto auf dem Computer verschafft, kann er nicht ohne Kenntnis des Kennworts auf die Datenbank zugreifen. Aus Sicherheitsgründen speichert die Anwendung das Kennwort niemals.

Der Anwendungscode erstellt eine EncryptionKeyGenerator-Instanz und ruft deren getEncryptionKey() -Methode auf. Dabei übergibt sie ein vom Benutzer eingegebenes Kennwort als Argument (in diesem Beispiel die Variable password ):

var keyGenerator:EncryptionKeyGenerator = new EncryptionKeyGenerator(); 
var encryptionKey:ByteArray = keyGenerator.getEncryptionKey(password);

Als ersten Schritt beim Aufrufen der getEncryptionKey() -Methode überprüft die EncryptionKeyGenerator-Klasse das vom Benutzer eingegebene Kennwort, um sicherzustellen, dass die Sicherheitsanforderungen für Kennwörter erfüllt werden. Die EncryptionKeyGenerator-Klasse erfordert, dass ein Kennwort 8 - 32 Zeichen enthält. Das Kennwort muss Großbuchstaben und Kleinbuchstaben sowie mindestens eine Ziffer oder ein Symbolzeichen enthalten.

Der reguläre Ausdruck, der dieses Muster überprüft, ist als Konstante mit dem Namen STRONG_PASSWORD_PATTERN definiert:

private static const STRONG_PASSWORD_PATTERN:RegExp = /(?=^.{8,32}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/;

Der Code, der das Kennwort überprüft, befindet sich in der validateStrongPassword() -Methode der EncryptionKeyGenerator-Klasse. Der Code lautet wie folgt:

public function vaidateStrongPassword(password:String):Boolean 
{ 
    if (password == null || password.length <= 0) 
    { 
        return false; 
    } 
     
    return STRONG_PASSWORD_PATTERN.test(password)) 
}

Intern ruft die getEncryptionKey() -Methode die validateStrongPassword() -Methode der EncryptionKeyGenerator-Klasse auf und gibt eine Ausnahme aus, wenn das Kennwort nicht gültig ist. Die validateStrongPassword() -Methode ist eine öffentliche Methode, sodass der Anwendungscode ein Kennwort überprüfen kann, ohne dass die getEncryptionKey() -Methode aufgerufen werden muss, wodurch sich Fehler vermeiden lassen.

Erweitern des Kennworts auf 256 Bit

Später im Prozess muss das Kennwort 256 Bits lang sein. Anstatt zu verlangen, dass jeder Benutzer ein Kennwort eingibt, dass genau 256 Bits (32 Zeichen) lang ist, erstellt der Code ein längeres Kennwort, indem die Zeichen des Kennworts wiederholt werden.

Die getEncryptionKey() -Methode ruft die concatenatePassword() -Methode auf, um das lange Kennwort zu erstellen.

var concatenatedPassword:String = concatenatePassword(password);

Folgendes ist der Code für die concatenatePassword() -Methode:

private function concatenatePassword(pwd:String):String 
{ 
    var len:int = pwd.length; 
    var targetLength:int = 32; 
     
    if (len == targetLength) 
    { 
        return pwd; 
    } 
     
    var repetitions:int = Math.floor(targetLength / len); 
    var excess:int = targetLength % len; 
     
    var result:String = ""; 
     
    for (var i:uint = 0; i < repetitions; i++) 
    { 
        result += pwd; 
    } 
     
    result += pwd.substr(0, excess); 
     
    return result; 
}

Wenn das Kennwort weniger als 256 Zeichen enthält, verkettet der Code das Kennwort mit sich selbst, um es auf eine Länge von 256 Bits zu bringen. Dabei wird die letzte Wiederholung ggf. gekürzt, um genau 256 Bits zu erhalten.

Generieren oder Abrufen eines 256-Bit-Salt-Werts

Der nächste Schritt besteht darin, einen 256-Bit-Salt-Wert zu bekommen, der später mit dem Kennwort kombiniert wird. Unter einem Salt versteht man einen zufälligen Wert, der mit einem vom Benutzer eingegebenen Wert zu einem Kennwort kombiniert wird. Durch die Verwendung eines Salts mit einem Kennwort wird sichergestellt, dass die vom System verwendete Kennwort-Salt-Kombination immer ein zufälliger Wert ist, selbst wenn der Benutzer ein lexikalisches Wort oder einen bekannten Begriff als Kennwort verwendet. Diese Zufälligkeit ist ein Schutz vor Wörterbuchangriffen, bei denen Angreifer eine Wortliste verwenden, um ein Kennwort zu erraten. Außerdem ist der Salt-Wert durch das Generieren und Speichern im verschlüsselten lokalen Speicher an das Benutzerkonto auf dem Computer mit der Datenbankdatei gebunden.

Wenn die Anwendung die getEncryptionKey() -Methode zum ersten Mal aufruft, erstellt der Code einen zufälligen 256-Bit-Salt-Wert. Danach lädt der Code den Salt-Wert aus dem verschlüsselten lokalen Speicher.

Der Salt-Wert wird in einer Variablen mit dem Namen salt gespeichert. Der Code bestimmt, ob bereits ein Salt erstellt wurde, indem versucht wird, den Salt-Wert aus dem verschlüsselten lokalen Speicher zu laden:

var salt:ByteArray = EncryptedLocalStore.getItem(saltKey); 
if (salt == null) 
{ 
    salt = makeSalt(); 
    EncryptedLocalStore.setItem(saltKey, salt); 
}

Wenn der Code einen neuen Salt-Wert erstellt, generiert die makeSalt() -Methode einen zufälligen 256-Bit-Wert. Da der Wert zum Schluss im verschlüsselten lokalen Speicher gespeichert wird, wird er als ByteArray generiert. Die makeSalt() -Methode verwendet die Math.random() -Methode, um den zufälligen Wert zu generieren. Die Math.random() -Methode kann 256 Bits nicht in einem Schritt generieren. Stattdessen verwendet der Code eine Schleife, um Math.random() acht Mal aufzurufen. Jedes Mal wird ein zufälliger uint-Wert zwischen 0 und 4294967295 (der höchste uint-Wert) generiert. Ein uint-Wert wird verwendet, weil uint genau 32 Bits verwendet. Indem acht uint-Werte in das ByteArray geschrieben werden, wird ein 256-Bit-Wert generiert. Folgendes ist der Code für die makeSalt() -Methode:

private function makeSalt():ByteArray 
{ 
    var result:ByteArray = new ByteArray; 
     
    for (var i:uint = 0; i < 8; i++) 
    { 
        result.writeUnsignedInt(Math.round(Math.random() * uint.MAX_VALUE)); 
    } 
     
    return result; 
}

Wenn der Code das Salt im verschlüsselten lokalen Speicher (Encrypted Local Store, ELS) speichert oder das Salt aus dem ELS abruft, benötigt er einen Stringschlüssel, unter dem das Salt gespeichert ist. Ohne Kenntnis des Schlüssels kann der Salt-Wert nicht abgerufen werden. In diesem Fall kann der Verschlüsselungsschlüssel nicht erstellt werden, um die Datenbank erneut zu öffnen. Standardmäßig verwendet die EncryptionKeyGenerator-Klasse einen vordefinierten ELS-Schlüssel, der in der Konstante SALT_ELS_KEY definiert ist. Anstatt den Standardschlüssel zu verwenden, kann der Anwendungscode auch einen ELS-Schlüssel angeben, der im Aufruf der getEncryptionKey() -Methode verwendet wird. Unabhängig davon, ob der Standardschlüssel oder ein von der Anwendung angegebener Salt-ELS-Schlüssel verwendet wird, wird der Schlüssel in einer Variablen mit dem Namen saltKey gespeichert. Diese Variable wird in den Aufrufen von EncryptedLocalStore.setItem() und EncryptedLocalStore.getItem() verwendet, wie bereit gezeigt.

Kombinieren des 256-Bit-Kennworts und des Salt-Werts mit dem XOR-Operator

Der Code verfügt nun über ein 256-Bit-Kennwort und einen 256-Bit-Salt-Wert. Als Nächstes wird eine XOR-Operation verwendet, um den Salt-Wert und das verlängerte Kennwort zu einem einzelnen Wert zu verbinden. Auf diese Weise wird im Endeffekt ein 256-Bit-Kennwort erstellt, das aus Zeichen besteht, die dem gesamten Bereich zulässiger Zeichen entnommen sind. Dies gilt, obwohl die eigentliche Kennworteingabe höchstwahrscheinlich hauptsächlich aus alphanumerischen Zeichen besteht. Mit dieser erhöhten Zufälligkeit wird die Gruppe der möglichen Kennwörter sehr viel größer, ohne dass der Benutzer ein langes, komplexes Kennwort eingeben muss.

Das Ergebnis der XOR-Operation wird in der Variablen unhashedKey gespeichert. Das eigentliche Ausführen einer bitweisen XOR-Operation für die beiden Werte erfolgt in der xorBytes() -Methode:

var unhashedKey:ByteArray = xorBytes(concatenatedPassword, salt);

Der bitweise XOR-Operator ( ^ ) nimmt zwei uint-Werte und gibt einen uint-Wert zurück. (Ein uint-Wert enthält 32 Bits.) Die Eingabewerte, die als Argumente an die xorBytes() -Methode übergeben werden, sind ein String (das Kennwort) und ein ByteArray (der Salt-Wert). Der Code verwendet eine Schleife, um jeweils 32 Bit aus jeder Eingabe zu extrahieren, um die Kombination mit dem XOR-Operator auszuführen.

private function xorBytes(passwordString:String, salt:ByteArray):ByteArray 
{ 
    var result:ByteArray = new ByteArray(); 
     
    for (var i:uint = 0; i < 32; i += 4) 
    { 
        // ... 
    } 
     
    return result; 
}

In der Schleife werden zunächst 32 Bit (4 Byte) aus dem passwordString -Parameter extrahiert. Diese Bit werden in einem zweiteiligen Prozess extrahiert und in einen uint-Wert ( o1 ) konvertiert. Zuerst ruft die charCodeAt() -Methode den numerischen Wert der einzelnen Zeichen ab. Als Nächstes wird dieser Wert an die entsprechende Position im uint-Wert verschoben, indem der Operator für die bitweise Verschiebung nach links ( << ) verwendet wird, und der verschobene Wert wird o1 hinzugefügt. Das erste Zeichen ( i ) wird zum Beispiel zu den ersten 8 Bits, indem der Operator für die bitweise Verschiebung nach links ( << ) verwendet wird, um die Bits um 24 Bits nach links zu verschieben, und dieser Wert dann o1 zugeordnet wird. Das zweite Zeichen (i + 1 ) wird zu den zweiten 8 Bits, indem sein Wert um 16 Bits verschoben und das Ergebnis o1 zugeordnet wird. Die Werte des dritten und vierten Zeichens werden auf dieselbe Art hinzugefügt.

        // ... 
         
        // Extract 4 bytes from the password string and convert to a uint 
        var o1:uint = passwordString.charCodeAt(i) << 24; 
        o1 += passwordString.charCodeAt(i + 1) << 16; 
        o1 += passwordString.charCodeAt(i + 2) << 8; 
        o1 += passwordString.charCodeAt(i + 3); 
         
        // ...

Die Variable o1 enthält jetzt 32 Bits aus dem passwordString -Parameter. Als Nächstes werden 32 Bits vom salt -Parameter extrahiert, indem dessen readUnsignedInt() -Methode aufgerufen wird. Die 32 Bits werden in der uint-Variablen o2 gespeichert.

        // ... 
         
        salt.position = i; 
        var o2:uint = salt.readUnsignedInt(); 
         
        // ...

Zum Schluss werden die beiden 32-Bit-Werte (uint-Werte) mit dem XOR-Operator kombiniert und das Ergebnis wird in einem ByteArray mit dem Namen result gespeichert.

        // ... 
         
        var xor:uint = o1 ^ o2; 
        result.writeUnsignedInt(xor); 
        // ...

Wenn eine Schleife abgeschlossen ist, wird das ByteArray mit dem XOR-Ergebnis zurückgegeben.

        // ... 
    } 
     
    return result; 
}

Hashing des Schlüssels

Nachdem das verkettete Kennwort und der Salt-Wert kombiniert wurden, besteht der nächste Schritt darin, diesen Wert noch sicherer zu gestaltet, indem Hashing mit dem SHA-256-Hashing-Algorithmus ausgeführt wird. Durch das Hashing wird Angreifern das Reverse-Engineering erschwert.

Zu diesem Zeitpunkt verfügt der Code über ein ByteArray mit den Namen unhashedKey , das die Kombination aus dem verketteten Kennwort und dem Salt enthält. Das ActionScript 3.0-Kernbibliothekprojekt (as3corelib) enthält eine SHA256-Klasse im com.adobe.crypto-Paket. Die SHA256.hashBytes() -Methode, die eine SHA-256-Hashfunktion für ein ByteArray ausführt und einen String zurückgibt, der das 256-Bit-Hash-Ergebnis als Hexadezimalzahl enthält. Die EncryptionKeyGenerator-Klasse verwendet die SHA256-Klasse für das Hashing des Schlüssels:

var hashedKey:String = SHA256.hashBytes(unhashedKey);

Extrahieren des Verschlüsselungsschlüssels aus dem Hash

Bei dem Verschlüsselungsschlüssel muss es sich um ein ByteArray handeln, das genau 16 Byte (128 Bit) lang ist. Das Ergebnis des SHA-256-Hash-Algorithmus ist immer 256 Bit lang. Der letzte Schritt besteht darin, 128 Bit aus dem Hash-Ergebnis auszuwählen, die als eigentlicher Verschlüsselungsschlüssel verwendet werden.

In der EncryptionKeyGenerator-Klasse reduziert der Code den Schlüssel auf 128 Bit, indem die generateEncryptionKey() -Methode aufgerufen wird. Dann wird das Ergebnis dieser Methode als Ergebnis der getEncryptionKey() -Methode zurückgegeben:

var encryptionKey:ByteArray = generateEncryptionKey(hashedKey); 
return encryptionKey;

Es ist nicht notwendig, die ersten 128 Bits als Verschlüsselungsschlüssel zu verwenden. Sie könnten auch einen Bereich von Bits auswählen, der an einem beliebigen Punkt beginnt, Sie könnten jedes zweite Bit auswählen oder eine andere beliebige Auswahl von Bits verwenden. Wichtig ist, dass der Code 128 bestimmte Bits auswählt und jedes Mal dieselben 128 Bits verwendet.

In diesem Fall verwendet die generateEncryptionKey() -Methode den Bitbereich, der beim 18. Byte anfängt, als Verschlüsselungsschlüssel. Wie bereits erwähnt, gibt die SHA256-Klasse einen String zurück, der einen 256-Bit-Hash als Hexadezimalzahl enthält. Ein einzelner Block von 128 Bit hat zu viele Byte, um sie in einem Schritt dem ByteArray hinzuzufügen. Der Code verwendet eine for -Schleife, um Zeichen aus dem Hexidezimalstring zu extrahieren, in numerische Werte zu konvertieren und sie dem ByteArray hinzuzufügen. Der SHA-256-Ergebnisstring ist 64 Zeichen lang. Ein Bereich von 128 Bits entspricht 32 Zeichen im String, und jedes Zeichen stellt 4 Bits dar. Die kleinste Datenmenge, die Sie einem ByteArray hinzufügen können, ist ein Byte (8 Bits), was zwei Zeichen im hash -String entspricht. Die Schleife zählt also in Schritten von 2 Zeichen von 0 bis 31 (32 Zeichen).

In der Schleife bestimmt der Code zunächst die Startposition für das aktuelle Zeichenpaar. Da der gewünschte Bereich bei dem Zeichen an Indexposition 17 (das 18. Byte) beginnt, wird der Variable position der aktuelle Iteratorwert ( i ) plus 17 zugewiesen. Der Code verwendet die substr() -Methode des String-Objekts, um die beiden Zeichen an der aktuellen Position zu extrahieren. Diese Zeichen werden in der Variable hex gespeichert. Als Nächstes verwendet der Code die parseInt() -Methode, um den hex -String in einen ganzzahligen Dezimalwert zu konvertieren. Dieser Wert wird in der int-Variablen byte gespeichert. Zum Schluss fügt der Code den Wert von byte zum result -ByteArray hinzu, indem die writeByte() -Methode verwendet wird. Wenn die Schleife abgeschlossen ist, enthält das result -ByteArray 16 Byte und kann als Verschlüsselungsschlüssel für eine Datenbank verwendet werden.

private function generateEncryptionKey(hash:String):ByteArray 
{ 
    var result:ByteArray = new ByteArray(); 
     
    for (var i:uint = 0; i < 32; i += 2) 
    { 
        var position:uint = i + 17; 
        var hex:String = hash.substr(position, 2); 
        var byte:int = parseInt(hex, 16); 
        result.writeByte(byte); 
    } 
     
    return result; 
}