Tipps zur AES-Kodierung für Entwickler

HINWEIS: Die Informationen auf dieser Seite sind für die Arbeit mit WinZip® nicht von Bedeutung, sondern lediglich für die Entwickler von Dienstprogrammen zur Verarbeitung von ZIP-Dateien gedacht.

Dieses Dokument enthält Informationen für Entwickler und andere interessierte Benutzer, die die AES-Verschlüsselungsformate AE-1 und AE-2 in eigenen ZIP-Dateidienstprogrammen unterstützen möchten. WinZip Computing übernimmt keinerlei Garantie für die in diesem Dokument enthaltenen Informationen. Insbesondere bietet WinZip Computing keine Gewähr für die Fehlerfreiheit dieser Informationen oder ihre Eignung für einen bestimmten Zweck sowie dafür, dass das hier beschriebene Dateiformat auch in künftigen WinZip-Versionen unterstützt wird. Es wird ausdrücklich empfohlen, jeglichen Code und alle Verfahren mit der üblichen Sorgfalt zu testen und zu validieren.

Diese Informationen dienen als Ergänzung zu den im Dokument Informationen zur AES-Verschlüsselung: Verschlüsselungsspezifikation AE-1 und AE-2 behandelten Grundlagen.

In diesem Dokument wird davon ausgegangen, dass Sie das AES-Encryption-Paket von Dr. Brian Gladman verwenden. Dr. Gladman hat in großzügiger Weise eine Beispielanwendung veröffentlicht, die die Verwendung seiner Encryption/Decryption- und anderer Routinen demonstriert. Die im Folgenden dargestellten Codebeispiele wurden dieser Beispielanwendung entnommen. Die AES-Bibliothek und die Beispielanwendung können von der AES-Projektseite auf Dr. Gladmans Website heruntergeladen werden.


Generieren von Saltwerten

Bitte lesen Sie die Informationen zu Saltwerten in der Verschlüsselungsspezifikation.

Dr. Gladman hat in den Dateien PRNG.C und PRNG.H einen Pseudozufallszahlengenerator bereitgestellt, der für das Generieren von Saltwerten verwendet werden kann. Diese Dateien sind in dem Beispielpaket enthalten, das auf der AES-Projektseite auf Dr. Gladmans Website zum Download bereitsteht.

Im Folgenden finden Sie einige Richtlinien für den Einsatz von Dr. Gladmans Generator. Bitte beachten Sie, dass der Generator ähnlich wie ein E/A-Stream verwendet, also geöffnet (initialisiert), eingesetzt und anschließend wieder geschlossen wird. Es wird empfohlen, den Generator beim Starten der Anwendung zu initialisieren und zusammen mit der Anwendung zu schließen. (Wenn Sie Ihren Code in C++ schreiben, sollten Sie diese Aktionen in eine C++-Klasse packen, die den Generator beim Aufbau initialisiert und beim Abbau wieder schließt.)

  1. Für die Initialisierung des Generators müssen Sie eine Entropiefunktion in Ihrem Code bereitstellen. Für diesen Zweck reicht eine relativ einfache Entropiefunktion aus, wie beispielsweise die folgende, die hauptsächlich auf dem Windows-Performancezähler basiert:
    int entropy_fun(
     unsigned char buf[],
     unsigned int len)
        {
        unsigned __int64 pentium_tsc[1];
        unsigned int     i;
        static unsigned int num = 0;
    
        // this sample code returns the following sequence of entropy information
        // - the current 8-byte Windows performance counter value
        // - an 8-byte representation of the current date/time
        // - an 8-byte value built from the current process ID and thread ID
        // - all subsequent calls return the then-current 8-byte performance
        //      counter value
    
        switch (num)
            {
        case 1:
            ++num;
            // use a value that is unlikely to repeat across system reboots
            GetSystemTimeAsFileTime((FILETIME *)pentium_tsc);
            break;
    
        case 2:
            ++num;
    
            {
            // use a value that distinguishes between different instances of this
            // code that might be running on different processors at the same time
            unsigned __int32 processtest = GetCurrentProcessId();
            unsigned __int32 threadtest = GetCurrentThreadId();
    
            pentium_tsc[0] = processtest;
            pentium_tsc[0] = (pentium_tsc[0] << 32) + threadtest;
            }
            break;
    
        case 0:
            ++num;
            // fall through to default case
        default:
    
            // use a rapidly-changing value
    
            //  Note: check QueryPerformanceFrequency() first to
            //  ensure that QueryPerformanceCounter() will work.
            QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc);
            break;
            }
    
        for(i = 0; i < 8 && i < len; ++i)
            buf[i] = ((unsigned char*)pentium_tsc)[i];
    
        return i;
        }
    
    Hinweis: Der für die Entropiefunktion benötigte Prototyp ist in PRNG.H definiert.

  2. Initialisieren Sie den Generator durch einen Aufruf von prng_init(), und geben Sie hierbei die Adressen Ihrer Entropiefunktion und einer Instanz der (in PRNG.H definierten) prng_ctx-Struktur an. Die Variable prng_ctx enthält einen Kontext für den Generator und dient als Parameter für die übrigen Generatorfunktionen. Aus diesem Grund muss der Zustand der Variablen bis zum Schließen des Generators beibehalten werden.
    prng_ctx ctx;
    prng_init(entropy_fun, &ctx);
    Diese Funktion muss während einer Anwendungssitzung nur einmal aufgerufen werden (sofern der Stream zwischenzeitlich nicht geschlossen wird).

  3. Rufen Sie die Funktion prng_rand() auf, um eine zufällige Bytefolge beliebiger Größe zu erzeugen. Dieser Code erzeugt 16 Zufallsbytes, die als Saltwert für die 256-Bit-AES-Verschlüsselung geeignet sind.
    unsigned char buffer[16];
    prng_rand(buffer, sizeof(buffer), &ctx);

    Beachten Sie, dass es sich bei dem Parameter ctx um dieselbe Variable prng_ctx handelt, die auch beim Initialisierungsaufruf verwendet wurde.

  4. Wenn Sie den Generator nicht mehr benötigen (in der Regel also beim Beenden Ihrer Anwendung), schließen Sie ihn durch einen Aufruf der Funktion prng_end:
    prng_end(&ctx);

    Hierbei handelt es sich bei dem Parameter ctx wiederum um dieselbe Variable prng_ctx, die auch beim Initialisierungsaufruf verwendet wurde.

Verschlüsselung und Entschlüsselung

Das Ver- und Entschlüsseln der Daten funktioniert auf ganz ähnliche Weise, wiederum mithilfe eines Streams: der Stream wird "geöffnet", erhält die zu verschlüsselnden oder zu entschlüsselnden Daten und wird anschließend wieder geschlossen. Beim Öffnen des Streams wird der Kennwortprüfwert und beim Schließen des Streams der Authentifizierungscode zurückgegeben.

Führen Sie die folgenden grundlegenden Schritte aus:

  1. Stream zur Verschlüsselung oder Entschlüsselung initialisieren und Kennwortprüfwert ermitteln.

    Die Initialisierung erfolgt beim Verschlüsseln und beim Entschlüsseln auf dieselbe Art und Weise:

    fcrypt_ctx zctx;     // the encryption context
    int rc = fcrypt_init(
      KeySize,       // extra data value indicating key size
      pszPassword,     // the password
      strlen(pszPassword), // number of bytes in password
      achSALT,       // the salt
      achPswdVerifier,   // on return contains password verifier
      &zctx);       // Verschlüsselungskontext

    Der Rückgabewert 0 gibt an, dass der Stream erfolgreich initialisiert wurde. Alle anderen Rückgabewerte deuten auf Fehler hin. Beachten Sie, dass es sich bei Kennwörtern um durch Nullen begrenzte ANSI-Zeichenfolgen handelt, die keine Nullen enthalten dürfen. (Um Kompatibilitätsprobleme zwischen den verschiedenen gängigen Zeichensätzen, insbesondere in unterschiedlichen Windows-Versionen, zu vermeiden, sollten die Benutzer dazu angehalten werden, Kennwörter zu verwenden, die ausschließlich aus den Standardzeichen 32 bis 127 bestehen.)

    Die Funktion gibt mit achPswdVerifier den Kennwortprüfwert zurück (hierfür ist ein 2-Byte-Puffer erforderlich). Beim Verschlüsseln speichern Sie diesen Wert wie in der Verschlüsselungsspezifikation angegeben in der ZIP-Datei, beim Entschlüsseln vergleichen Sie den Rückgabewert mit dem in der ZIP-Datei gespeicherten Wert. Wenn die beiden Werte voneinander abweichen, hat entweder der Benutzer ein falsches Kennwort eingegeben, oder die Datei wurde nach dem Verschlüsseln in irgendeiner Weise verändert. (Beachten Sie, dass bei der Eingabe eines falschen Kennworts immer noch eine Wahrscheinlichkeit von 1 zu 65.536 besteht, dass die beiden Werte übereinstimmen.)

    Der initialisierte Verschlüsselungskontext (zctx) wird als Parameter für die Encryption/Decryption-Funktionen verwendet. Aus diesem Grund muss sein Zustand bis zum Schließen des Streams beibehalten werden.

  2. Daten verschlüsseln oder entschlüsseln.

    Verschlüsseln:

    fcrypt_encrypt(
      pchData, // pointer to the data to encrypt
      cb,   // how many bytes to encrypt
      &zctx); // Verschlüsselungskontext

    Entschlüsseln:

    fcrypt_decrypt(
      pchData, // pointer to the data to decrypt
      cb,   // how many bytes to decrypt
      &zctx); // Entschlüsselungskontext

    Unter Umständen müssen Sie die Verschlüsselungs- oder Entschlüsselungsfunktion mehrmals aufrufen und hierbei den jeweils nächsten Datenchunk an den Puffer übergeben. Um die Kompatibilität mit der AE-1- und der AE-2-Spezifikation zu gewährleisten, muss die Puffergröße ein Vielfaches von 16 Byte betragen, wobei lediglich der letzte Puffer naturgemäß kleiner sein kann. Aus Gründen der Effizienz sollte im Allgemeinen ein deutlich größerer Puffer von beispielsweise 32.768 Byte verwendet werden.

    Hinweis: Beim Verschlüsseln von Nulllängedateien können Sie diesen Schritt einfach überspringen. Der Kennwortprüfwert (Schritt 1) und der Authentifizierungscode (Schritt 3) werden jedoch auch hierbei ermittelt und verwendet.

  3. Stream schließen und Authentifizierungscode ermitteln.

    Schließen Sie den Stream nach dem Verschlüsseln bzw. Entschlüsseln wie folgt:

    int rc = fcrypt_end(
      achMAC, // on return contains the authentication code
      &zctx); // Verschlüsselungskontext

    Der Rückgabewert entspricht der Größe des Authentifizierungscodes und lautet bei AE-1 und AE-2 stets 10. Der Authentifizierungscode selbst wird mit achMAC an Ihren Puffer übergeben. Hierbei handelt es sich um ein für die Aufnahme von mindestens 10 Zeichen dimensioniertes char-Array. Beim Verschlüsseln speichern Sie diesen Wert wie in der Verschlüsselungsspezifikation angegeben in der ZIP-Datei, beim Entschlüsseln vergleichen Sie den Rückgabewert mit dem in der ZIP-Datei gespeicherten Wert. Wenn die beiden Werte voneinander abweichen, hat entweder der Benutzer ein falsches Kennwort eingegeben, oder die verschlüsselten Daten wurden nach dem Speichern in irgendeiner Weise verändert.

    Beachten Sie, dass die Entschlüsselung auch dann fehlschlagen kann, wenn die verschlüsselten Daten nicht verändert wurden und der in Schritt 1 ermittelte Kennwortprüfwert korrekt war. Der Kennwortprüfwert eignet sich nur zu einer raschen Überprüfung im Vorfeld und erkennt die meisten falschen Kennwörter. Da hierbei jedoch noch keine Prüfung des Kennworts selbst erfolgt, besteht immer noch eine Chance von 1 zu 65,536, dass ein falsches Kennwort nicht erkannt wird. Aus diesem Grund sollten Sie auch bei korrektem Kennwortprüfwert nach Abschluss des Vorgangs den Authentifizierungscode prüfen.

Hinweise

  • Dr. Gladmans AES-Code basiert auf der von der Plattform, auf der er ausgeführt wird, verwendeten Bytereihenfolge (Little-Endian oder Big-Endian). Diese Reihenfolge wird von einer C-Präprozessorkonstante namens PLATFORM_BYTE_ORDER bestimmt, die in der Datei AESOPT.H definiert ist. Vergewissern Sie sich, dass PLATFORM_BYTE_ORDER den Ihrer Plattform entsprechenden Wert ermittelt. Wenn dies nicht der Fall ist, müssen Sie den korrekten Wert selbst einstellen. Wenn Sie den Microsoft-Compiler auf einer Intel-Plattform verwenden, wird der korrekte Wert ermittelt, der in diesem Fall AES_LITTLE_ENDIAN lautet. Wie uns allerdings berichtet wurde, wird bei Verwendung des Borland C++-Builders nicht der richtige Wert voreingestellt, sodass eine manuelle Anpassung erforderlich ist. Weitere Informationen zu diesem Thema finden Sie in den Kommentaren in der Datei AESOPT.H.

Änderungsverlauf

Änderungen gegenüber Dokumentversion 1.04 vom Juli 2008:

  1. Beispiel-Entropiefunktion

    Die Beispiel-Entropiefunktion wurde um Informationen am Beginn des Entropie-Streams erweitert, die den Tag sowie den jeweiligen Prozess und Thread angeben.


Dokumentversion: 1.04
Zuletzt geändert: 21. Juli 2008

Copyright© 2003-2008 WinZip International LLC.
Alle Rechte vorbehalten