class OpenSSL::Cipher

Bietet symmetrische Algorithmen für Verschlüsselung und Entschlüsselung. Die verfügbaren Algorithmen hängen von der jeweiligen Version von OpenSSL ab, die installiert ist.

Auflistung aller unterstützten Algorithmen

Eine Liste der unterstützten Algorithmen kann durch

puts OpenSSL::Cipher.ciphers

Instanziieren eines Cipher

erhalten werden. Es gibt mehrere Möglichkeiten, eine Cipher-Instanz zu erstellen. Im Allgemeinen wird ein Cipher-Algorithmus anhand seines Namens, der Schlüssellänge in Bits und des zu verwendenden Chiffriermodus kategorisiert. Der generischste Weg, ein Cipher zu erstellen, ist der folgende:

cipher = OpenSSL::Cipher.new('<name>-<key length>-<mode>')

Das heißt, ein String, der aus der mit Bindestrichen verbundenen Verkettung der einzelnen Komponenten Name, Schlüssellänge und Modus besteht. Entweder vollständig großgeschriebene oder vollständig kleingeschriebene Strings können verwendet werden, zum Beispiel:

cipher = OpenSSL::Cipher.new('aes-128-cbc')

Auswahl des Verschlüsselungs- oder Entschlüsselungsmodus

Verschlüsselung und Entschlüsselung sind bei symmetrischen Algorithmen oft sehr ähnliche Operationen. Dies spiegelt sich darin wider, dass keine unterschiedlichen Klassen für die jeweilige Operation gewählt werden müssen; beides kann mit derselben Klasse durchgeführt werden. Dennoch müssen wir nach dem Erhalt einer Cipher-Instanz der Instanz mitteilen, was wir damit vorhaben. Wir müssen also entweder

cipher.encrypt

oder

cipher.decrypt

auf der Cipher-Instanz aufrufen. Dies sollte der erste Aufruf nach der Erstellung der Instanz sein, da andernfalls bereits gesetzte Konfigurationen im Prozess verloren gehen könnten.

Auswahl eines Schlüssels

Die symmetrische Verschlüsselung erfordert einen Schlüssel, der für die verschlüsselnde und die entschlüsselnde Partei derselbe ist und nach der anfänglichen Schlüsselvereinbarung als private Information geheim gehalten werden sollte. Es gibt viele Möglichkeiten, unsichere Schlüssel zu erstellen, die bemerkenswerteste ist, einfach ein Passwort als Schlüssel zu nehmen, ohne das Passwort weiter zu verarbeiten. Ein einfacher und sicherer Weg, einen Schlüssel für ein bestimmtes Cipher zu erstellen, ist:

cipher = OpenSSL::Cipher.new('aes-256-cfb')
cipher.encrypt
key = cipher.random_key # also sets the generated key on the Cipher

Wenn Sie unbedingt Passwörter als Verschlüsselungsschlüssel verwenden müssen, sollten Sie Password-Based Key Derivation Function 2 (PBKDF2) verwenden, indem Sie den Schlüssel mit Hilfe der von OpenSSL::PKCS5.pbkdf2_hmac_sha1 oder OpenSSL::PKCS5.pbkdf2_hmac bereitgestellten Funktionalität generieren.

Obwohl es Cipher#pkcs5_keyivgen gibt, ist dessen Verwendung veraltet und sollte nur in Legacy-Anwendungen verwendet werden, da es nicht die neueren PKCS#5 v2 Algorithmen verwendet.

Auswahl eines IV

Die Chiffriermodi CBC, CFB, OFB und CTR benötigen alle einen „Initialisierungsvektor“, kurz IV. Der ECB-Modus ist der einzige Modus, der keinen IV benötigt, aber es gibt fast keinen legitimen Anwendungsfall für diesen Modus, da er Muster im Klartext nicht ausreichend verbirgt. Daher:

Sie sollten niemals den ECB-Modus verwenden, es sei denn, Sie sind absolut sicher, dass Sie ihn unbedingt benötigen.

Aus diesem Grund werden Sie in jedem Fall mit einem Modus enden, der explizit einen IV erfordert. Obwohl der IV als öffentliche Information betrachtet werden kann, d. h. er kann einmal generiert öffentlich übertragen werden, sollte er dennoch unvorhersehbar bleiben, um bestimmte Arten von Angriffen zu verhindern. Daher idealerweise:

Erstellen Sie für jede Verschlüsselung Ihres Chiffres immer einen sicheren zufälligen IV.

Für jede Datenverschlüsselung sollte ein neuer, zufälliger IV erstellt werden. Betrachten Sie den IV als eine Nonce (einmal verwendete Nummer) – er ist öffentlich, aber zufällig und unvorhersehbar. Ein sicherer zufälliger IV kann wie folgt erstellt werden:

cipher = ...
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv # also sets the generated IV on the Cipher

Obwohl der Schlüssel im Allgemeinen ebenfalls ein zufälliger Wert ist, ist er eine schlechte Wahl als IV. Es gibt ausgeklügelte Wege, wie ein Angreifer einen solchen IV ausnutzen kann. Als Faustregel gilt, dass die Offenlegung des Schlüssels direkt oder indirekt unter allen Umständen vermieden werden sollte und Ausnahmen nur aus gutem Grund gemacht werden sollten.

Aufrufen von Cipher#final

ECB (das nicht verwendet werden sollte) und CBC sind beides blockbasierte Modi. Das bedeutet, dass sie im Gegensatz zu anderen Streaming-basierten Modi auf Datenblöcken fester Größe operieren und daher einen „Finalisierungsschritt“ erfordern, um den letzten Datenblock durch entsprechende Behandlung einer Form von Padding zu erzeugen oder korrekt zu entschlüsseln. Daher ist es unerlässlich, die Ausgabe von OpenSSL::Cipher#final zu Ihrem Verschlüsselungs-/Entschlüsselungsbuffer hinzuzufügen, sonst erhalten Sie Entschlüsselungsfehler oder abgeschnittene Daten.

Obwohl dies für Streaming-Modus-Chiffren nicht wirklich notwendig ist, wird dennoch empfohlen, dasselbe Muster zu verwenden, indem die Ausgabe von Cipher#final ebenfalls hinzugefügt wird – dies ermöglicht Ihnen auch, zukünftig einfacher zwischen Modi zu wechseln.

Einige Daten verschlüsseln und entschlüsseln

data = "Very, very confidential data"

cipher = OpenSSL::Cipher.new('aes-128-cbc')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv

encrypted = cipher.update(data) + cipher.final
...
decipher = OpenSSL::Cipher.new('aes-128-cbc')
decipher.decrypt
decipher.key = key
decipher.iv = iv

plain = decipher.update(encrypted) + decipher.final

puts data == plain #=> true

Authentifizierte Verschlüsselung und zugehörige Daten (AEAD)

Wenn die verwendete OpenSSL-Version dies unterstützt, sollte ein authentifizierter Verschlüsselungsmodus (wie GCM oder CCM) immer einem nicht authentifizierten Modus vorgezogen werden. Derzeit unterstützt OpenSSL AE nur in Kombination mit zugehörigen Daten (AEAD), bei denen zusätzliche zugehörige Daten in den Verschlüsselungsprozess einbezogen werden, um am Ende der Verschlüsselung einen Tag zu berechnen. Dieser Tag wird auch im Entschlüsselungsprozess verwendet und durch die Überprüfung seiner Gültigkeit wird die Authentizität eines gegebenen Chiffretexts hergestellt.

Dies ist den nicht authentifizierten Modi überlegen, da es erkennt, ob jemand tatsächlich den Chiffretext nach seiner Verschlüsselung verändert hat. Dies verhindert bösartige Modifikationen des Chiffretexts, die ansonsten ausgenutzt werden könnten, um Chiffretexte auf eine Weise zu verändern, die potenziellen Angreifern zugutekommt.

Zugehörige Daten, auch zusätzliche authentifizierte Daten (AAD) genannt, werden optional verwendet, wenn zusätzliche Informationen, wie z. B. Header oder Metadaten, vorhanden sind, die ebenfalls authentifiziert werden müssen, aber nicht unbedingt verschlüsselt werden müssen.

Ein Beispiel mit GCM (Galois/Counter Mode). Sie haben einen 16-Byte-Schlüssel, ein 12-Byte (96 Bit) Nonce und die zugehörigen Daten auth_data. Achten Sie darauf, das Paar aus Schlüssel und Nonce nicht wiederzuverwenden. Die Wiederverwendung einer Nonce ruiniert die Sicherheitsgarantien des GCM-Modus.

key = OpenSSL::Random.random_bytes(16)
nonce = OpenSSL::Random.random_bytes(12)
auth_data = "authenticated but unencrypted data"
data = "encrypted data"

cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt
cipher.key = key
cipher.iv = nonce
cipher.auth_data = auth_data

encrypted = cipher.update(data) + cipher.final
tag = cipher.auth_tag(16)

Nun sind Sie der Empfänger. Sie kennen den Schlüssel und haben Nonce, auth_data, verschlüsselt und tag über ein nicht vertrauenswürdiges Netzwerk erhalten. Beachten Sie, dass GCM einen Tag beliebiger Länge zwischen 1 und 16 Bytes akzeptiert. Möglicherweise müssen Sie zusätzlich prüfen, ob der empfangene Tag die richtige Länge hat, oder Sie erlauben Angreifern, einen gültigen einstellig-langen Tag für den manipulierten Chiffretext mit einer Wahrscheinlichkeit von 1/256 zu fälschen.

raise "tag is truncated!" unless tag.bytesize == 16
decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt
decipher.key = key
decipher.iv = nonce
decipher.auth_tag = tag # could be called at any time before #final
decipher.auth_data = auth_data

decrypted = decipher.update(encrypted) + decipher.final

puts data == decrypted #=> true

Beachten Sie, dass andere AEAD-Chiffren zusätzliche Schritte erfordern können, wie z. B. die Festlegung der erwarteten Tag-Länge (auth_tag_len=) oder der Gesamtdatenlänge (ccm_data_len=) im Voraus. Lesen Sie die entsprechende Manpage für Details.