Unterschlüsselableitung und authentifizierte Verschlüsselung in ASP.NET Core
Die meisten Schlüssel im Schlüsselbund enthalten irgendeine Form von Entropie und haben algorithmische Informationen wie „CBC-Verschlüsselung + HMAC-Validierung“ oder „GCM-Verschlüsselung + Validierung“. In diesen Fällen bezeichnen wir die eingebettete Entropie als Hauptschlüsselmaterial (oder KM, keyring material) für diesen Schlüssel und führen eine Schlüsselableitungsfunktion aus, um die Schlüssel abzuleiten, die für die eigentlichen kryptographischen Vorgänge verwendet werden.
Hinweis
Schlüssel sind abstrakt, und eine benutzerdefinierte Implementierung verhält sich möglicherweise nicht wie unten dargestellt. Wenn der Schlüssel seine eigene Implementierung von IAuthenticatedEncryptor
bereitstellt, anstatt eine unserer integrierten Fabriken zu verwenden, gilt der in diesem Abschnitt beschriebene Mechanismus nicht mehr.
Zusätzliche authentifizierte Daten und Ableitung von Unterschlüsseln
Die Schnittstelle IAuthenticatedEncryptor
dient als zentrale Schnittstelle für alle authentifizierten Verschlüsselungsvorgänge. Die Methode Encrypt
benötigt zwei Puffer: plaintext und additionalAuthenticatedData (AAD). Der Inhalt von plaintext fließt unverändert in den Aufruf von IDataProtector.Protect
, aber das AAD wird vom System generiert und besteht aus drei Komponenten:
Der 32-Bit Magic Header 09 F0 C9 F0, der diese Version des Datenschutzsystems identifiziert.
Die 128-Bit-Schlüssel-ID.
Eine Zeichenfolge variabler Länge, die aus der Zweckkette gebildet wird, die
IDataProtector
erzeugt hat, das diesen Vorgang ausführt.
Da die AAD für das Tupel aus allen drei Komponenten eindeutig ist, können wir sie verwenden, um neue Schlüssel aus KM abzuleiten, anstatt KM selbst für alle unsere kryptografischen Vorgänge zu verwenden. Bei jedem Aufruf von IAuthenticatedEncryptor.Encrypt
findet der folgende Prozess der Schlüsselableitung statt:
( K_E, K_H ) = SP800_108_CTR_HMACSHA512(K_M, AAD, contextHeader || keyModifier)
Hier rufen wir den NIST SP800-108 KDF im Zählermodus (siehe NIST SP800-108, Abschnitt 5.1) mit den folgenden Parametern auf:
Schlüsselableitungsschlüssel (KDK) =
K_M
PRF = HMACSHA512
label = additionalAuthenticatedData
context = contextHeader || keyModifier
Der Kontext-Header ist von variabler Länge und dient im Wesentlichen als Fingerabdruck der Algorithmen, für die wir K_E
und K_H
ableiten. Der Schlüsselmodifikator ist eine 128-Bit-Zeichenkette, die für jeden Aufruf von Encrypt
nach dem Zufallsprinzip generiert wird und dazu dient, mit überwältigender Wahrscheinlichkeit sicherzustellen, dass KE und KH für diesen spezielle Authentifizierungs-Verschlüsselungsvorgang eindeutig sind, selbst wenn alle anderen Eingaben in den KDF konstant sind.
Bei der Verschlüsselung im CBC-Modus + HMAC-Validierungsvorgang ist | K_E |
die Länge des symmetrischen Blockchiffrierschlüssels und | K_H |
ist die Hash-Größe der HMAC-Routine. Für GCM-Verschlüsselungs- und Validierungsvorgänge: | K_H | = 0
.
Verschlüsselung im CBC-Modus + HMAC-Validierung
Sobald K_E
über den obigen Mechanismus generiert wurde, erzeugen wir einen zufälligen Initialisierungsvektor und führen den symmetrischen Blockchiffrieralgorithmus aus, um den Klartext zu verschlüsseln. Der Initialisierungsvektor und der Chiffretext durchlaufen dann die HMAC-Routine, die mit dem Schlüssel K_H
initialisiert wurde, um den MAC zu erzeugen. Dieser Prozess und der Rückgabewert werden unten grafisch dargestellt.
output:= keyModifier || iv || E_cbc (K_E,iv,data) || HMAC(K_H, iv || E_cbc (K_E,iv,data))
Hinweis
Die Implementierung von IDataProtector.Protect
wird der Ausgabe den Magic Header und die Schlüssel-ID voranstellen, bevor sie an den Aufrufer zurückgegeben wird. Da der Magic Header und die Schlüssel-ID implizit Teil des AAD sind und der Schlüsselmodifikator als Eingabe in den KDF eingespeist wird, bedeutet dies, dass jedes einzelne Byte der endgültigen zurückgegebenen Nutzdaten vom MAC authentifiziert wird.
Galois/Zähler-Modus Verschlüsselung + Validierung
Sobald K_E
über den obigen Mechanismus generiert wurde, erzeugen wir eine zufällige 96-Bit-Nonce und führen den symmetrischen Blockchiffrieralgorithmus aus, um den Klartext zu verschlüsseln und das 128-Bit-Authentifizierungskennzeichen zu erzeugen.
output := keyModifier || nonce || E_gcm (K_E,nonce,data) || authTag
Hinweis
Obwohl GCM das Konzept von AAD nativ unterstützt, geben wir AAD nur an die ursprüngliche KDF weiter und entscheiden uns dafür, einen leeren String als AAD-Parameter an GCM zu übergeben. Dies hat zwei Gründe. Erstens wollen wir, um die Flexibilität zu unterstützen, K_M
nie direkt als Verschlüsselungsschlüssel verwenden. Außerdem stellt GCM sehr strenge Anforderungen an die Einzigartigkeit seiner Eingaben. Die Wahrscheinlichkeit, dass die GCM-Verschlüsselungsroutine jemals für zwei oder mehr verschiedene Eingabedatensätze mit demselben Paar (Schlüssel, Nonce) aufgerufen wird, darf 2^-32 nicht überschreiten. Wenn wir K_E
festlegen, können wir nicht mehr als 2^32 Verschlüsselungsvorgänge durchführen, bevor wir den Grenzwert von 2^-32 überschreiten. Dies mag wie eine sehr große Anzahl von Vorgängen erscheinen, aber ein Webserver mit hohem Datenverkehr kann innerhalb weniger Tage 4 Milliarden Anforderungen bewältigen, was weit unter der normalen Lebensdauer dieser Schlüssel liegt. Um den zulässigen Wahrscheinlichkeitsgrenzwert von 2^-32 einzuhalten, verwenden wir weiterhin einen 128-Bit-Schlüsselmodifikator und eine 96-Bit-Nonce, wodurch sich die Anzahl der nutzbaren Vorgänge für jedes beliebige K_M
radikal erhöht. Der Einfachheit halber teilen wir den KDF-Codepfad zwischen CBC- und GCM-Operationen, und da AAD bereits im KDF berücksichtigt wird, ist es nicht nötig, es an die GCM-Routine weiterzuleiten.