Partager via


En-têtes de contexte dans ASP.NET Core

Arrière-plan et théorie

Dans le système de protection des données, une « clé » signifie un objet qui peut fournir des services de chiffrement authentifiés. Chaque clé est identifiée par un identifiant unique (un GUID), et elle contient des informations algorithmiques et du matériel entropique. Il est prévu que chaque clé porte une entropie unique, mais que le système ne peut pas l’appliquer, et nous devons également tenir compte des développeurs susceptibles de modifier manuellement l’anneau de clés en modifiant les informations algorithmiques d’une clé existante dans l’anneau de clés. Pour atteindre nos exigences de sécurité en fonction de ces cas, le système de protection des données a un concept d’agilité de chiffrement, ce qui permet en toute sécurité d’utiliser une valeur en tropique unique sur plusieurs algorithmes de chiffrement.

La plupart des systèmes qui prennent en charge l’agilité de chiffrement le font en incluant des informations d’identification sur l’algorithme à l’intérieur de la charge utile. L’OID de l’algorithme est généralement un bon candidat pour cela. Toutefois, un problème que nous avons rencontré est qu’il existe plusieurs façons de spécifier le même algorithme : « AES » (CNG) et les classes Aes, AesManaged, AesCryptoServiceProvider, AesCng et RijndaelManaged (étant donné des paramètres spécifiques) sont toutes la même chose, et nous devons conserver un mappage de tous ces éléments à l’OID correct. Si un développeur voulait fournir un algorithme personnalisé (ou même une autre implémentation d’AES !), il faudra lui dire son OID. Cette étape d’inscription supplémentaire rend la configuration système particulièrement douloureuse.

En prenant du recul, nous avons décidé que nous abordions le problème sous le mauvais angle. Un OID vous indique ce que l’algorithme est, mais nous ne nous soucions pas de cela. Si nous devons utiliser une valeur en tropique unique en toute sécurité dans deux algorithmes différents, il n’est pas nécessaire que nous sachions ce que sont réellement les algorithmes. Ce que nous nous soucions en fait de la façon dont ils se comportent. Tout algorithme de chiffrement de bloc symétrique décent est également une permutation pseudorandom forte (PRP) : corrigez les entrées (clé, mode chaînage, IV, texte brut) et la sortie de texte chiffré aura une probabilité écrasante être distincte de n’importe quel autre algorithme de chiffrement de bloc symétrique en fonction des mêmes entrées. De même, toute fonction de hachage à clé décente est également une fonction pseudorandom forte (PRF), et étant donné un ensemble d’entrées fixes, sa sortie sera largement distincte de toute autre fonction de hachage à clé.

Nous utilisons ce concept de PRP forts et de PRF pour créer un en-tête de contexte. Cet en-tête de contexte agit essentiellement comme une empreinte stable sur les algorithmes utilisés pour une opération donnée et fournit l’agilité de chiffrement nécessaire par le système de protection des données. Cet en-tête est reproductible et est utilisé ultérieurement dans le cadre du processus de dérivation de sous-clé. Il existe deux façons de générer l’en-tête de contexte en fonction des modes d’opération des algorithmes sous-jacents.

Chiffrement en mode CBC + authentification HMAC

L’en-tête de contexte se compose des composants suivants :

  • [16 bits] La valeur 00 00, qui est un marqueur qui signifie « Chiffrement CBC + Authentification HMAC ».

  • [32 bits] La longueur de la clé (en octets, big-endian) de l'algorithme de chiffrement par bloc symétrique.

  • [32 bits] Taille de bloc (en octets, big-endian) de l’algorithme de chiffrement de bloc symétrique.

  • [32 bits] Longueur de clé (en octets, big-endian) de l’algorithme HMAC. (Actuellement, la taille de clé correspond toujours à la taille de synthèse.)

  • [32 bits] Taille de synthèse (en octets, big-endian) de l’algorithme HMAC.

  • EncCBC(K_E, IV, ""), qui est la sortie de l’algorithme de chiffrement de bloc symétrique en fonction d’une entrée de chaîne vide et où IV est un vecteur tout-zéro. La construction de K_E est décrite ci-dessous.

  • MAC(K_H, ""), qui est la sortie de l’algorithme HMAC donnée pour une chaîne vide comme entrée. La construction de K_H est décrite ci-dessous.

Dans l’idéal, nous pourrions passer tous les vecteurs zéro pour K_E et K_H. Toutefois, nous voulons éviter la situation où l’algorithme sous-jacent vérifie l’existence de clés faibles avant d’effectuer des opérations (notamment DES et 3DES), ce qui empêche l’utilisation d’un modèle simple ou reproductible comme un vecteur zéro.

Au lieu de cela, nous utilisons le NIST SP800-108 KDF en mode compteur (voir NIST SP800-108, Sec. 5.1) avec une clé, une étiquette et un contexte de longueur nulle et HMACSHA512 comme PRF de base. Nous dérivons | K_E | + | K_H | octets de sortie, puis nous décomposons le résultat en K_E et K_H eux-mêmes. Mathématiquement, ceci est représenté comme suit.

( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")

Exemple : AES-192-CBC + HMACSHA256

Par exemple, considérez le cas où l’algorithme de chiffrement de bloc symétrique est AES-192-CBC et que l’algorithme de validation est HMACSHA256. Le système génère l’en-tête de contexte en suivant les étapes suivantes.

Tout d’abord, laissez ( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = ""), où | K_E | = 192 bits et | K_H | = 256 bits selon les algorithmes spécifiés. Cela entraîne K_E = 5BB6..21DD et K_H = A04A..00A9 dans l’exemple ci-dessous :

5B B6 C9 83 13 78 22 1D 8E 10 73 CA CF 65 8E B0
61 62 42 71 CB 83 21 DD A0 4A 05 00 5B AB C0 A2
49 6F A5 61 E3 E2 49 87 AA 63 55 CD 74 0A DA C4
B7 92 3D BF 59 90 00 A9

Ensuite, calculez Enc_CBC (K_E, IV, "") pour AES-192-CBC, étant donnés IV = 0* et K_E comme ci-dessus.

result := F474B1872B3B53E4721DE19C0841DB6F

Ensuite, calculez MAC(K_H, "") pour HMACSHA256, étant donné K_H comme indiqué ci-dessus.

result := D4791184B996092EE1202F36E8608FA8FBD98ABDFF5402F264B1D7211536220C

Cela produit l’en-tête de contexte complet ci-dessous :

00 00 00 00 00 18 00 00 00 10 00 00 00 20 00 00
00 20 F4 74 B1 87 2B 3B 53 E4 72 1D E1 9C 08 41
DB 6F D4 79 11 84 B9 96 09 2E E1 20 2F 36 E8 60
8F A8 FB D9 8A BD FF 54 02 F2 64 B1 D7 21 15 36
22 0C

Cet en-tête de contexte est l’empreinte numérique de la paire d’algorithmes de chiffrement authentifié (chiffrement AES-192-CBC + validation HMACSHA256). Les composants, comme décrit ci-dessus sont les suivants :

  • le marqueur (00 00)

  • longueur de la clé de chiffrement de bloc (00 00 00 18)

  • taille de bloc de chiffrement (00 00 00 10)

  • longueur de la clé HMAC (00 00 00 20)

  • taille de synthèse HMAC (00 00 00 20)

  • la sortie du PRP pour le chiffrement par bloc (F4 74 - DB 6F) et

  • sortie HMAC PRF (D4 79 - end).

Note

L’en-tête de contexte d’authentification par chiffrement en mode CBC + HMAC est généré de la même façon, peu importe que les implémentations des algorithmes soient fournies par Windows CNG ou par des types SymmetricAlgorithm et KeyedHashAlgorithm gérés. Cela permet aux applications s’exécutant sur différents systèmes d’exploitation de produire de manière fiable le même en-tête de contexte, même si les implémentations des algorithmes diffèrent entre les systèmes d’exploitation. (Dans la pratique, keyedHashAlgorithm n’a pas besoin d’être un HMAC approprié. Il peut s’agir de n’importe quel type d’algorithme de hachage à clé.)

Exemple : 3DES-192-CBC + HMACSHA1

Tout d’abord, laissez ( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = ""), où | K_E | = 192 bits et | K_H | = 160 bits selon les algorithmes spécifiés. Cela entraîne K_E = A219..E2BB et K_H = DC4A..B464 dans l’exemple ci-dessous :

A2 19 60 2F 83 A9 13 EA B0 61 3A 39 B8 A6 7E 22
61 D9 F8 6C 10 51 E2 BB DC 4A 00 D7 03 A2 48 3E
D1 F7 5A 34 EB 28 3E D7 D4 67 B4 64

Ensuite, calculez Enc_CBC (K_E, IV, "") pour le 3DES-192-CBC étant donné IV = 0* et K_E comme ci-dessus.

result := ABB100F81E53E10E

Ensuite, calculez MAC(K_H, "") pour HMACSHA1 en utilisant K_H comme indiqué ci-dessus.

result := 76EB189B35CF03461DDF877CD9F4B1B4D63A7555

Cela produit l’en-tête de contexte complet qui est une empreinte numérique de la paire d’algorithmes de chiffrement authentifiée (chiffrement 3DES-192-CBC + validation HMACSHA1), illustré ci-dessous :

00 00 00 00 00 18 00 00 00 08 00 00 00 14 00 00
00 14 AB B1 00 F8 1E 53 E1 0E 76 EB 18 9B 35 CF
03 46 1D DF 87 7C D9 F4 B1 B4 D6 3A 75 55

Les composants se décomposent comme suit :

  • le marqueur (00 00)

  • longueur de la clé de chiffrement de bloc (00 00 00 18)

  • taille de bloc de chiffrement (00 00 00 08)

  • longueur de la clé HMAC (00 00 00 14)

  • taille de synthèse HMAC (00 00 00 14)

  • la sortie du chiffrement de bloc PRP (AB B1 - E1 0E) et

  • sortie HMAC PRF (76 EB - end).

Chiffrement galois/mode compteur + authentification

L’en-tête de contexte se compose des composants suivants :

  • [16 bits] Valeur 00 01, qui est un marqueur qui signifie « Chiffrement GCM + authentification ».

  • [32 bits] La longueur de la clé (en octets, big-endian) de l'algorithme de chiffrement par bloc symétrique.

  • [32 bits] Taille nonce (en octets, big-endian) utilisée pendant les opérations de chiffrement authentifiées. (Pour notre système, cela est fixé à une taille de nonce = 96 bits.)

  • [32 bits] Taille de bloc (en octets, big-endian) de l’algorithme de chiffrement de bloc symétrique. (Pour GCM, cette valeur est fixe à la taille de bloc = 128 bits.)

  • [32 bits] Taille de balise d’authentification (en octets, big-endian) produite par la fonction de chiffrement authentifiée. (Pour notre système, cela est fixe à la taille de balise = 128 bits.)

  • [128 bits] La balise de Enc_GCM (K_E, nonce, ""), qui est la sortie de l'algorithme de chiffrement par bloc symétrique donné une chaîne vide en entrée et où le nonce est un vecteur de 96 bits tout à zéro.

K_E est dérivé à l’aide du même mécanisme que dans le scénario d’authentification CBC Encryption + HMAC. Toutefois, étant donné qu'il n'y a pas de K_H en jeu ici, nous avons essentiellement | K_H | = 0, et l'algorithme se réduit à la forme ci-dessous.

K_E = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")

Exemple : AES-256-GCM

Tout d’abord, let K_E = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = ""), où | K_E | = 256 bits.

K_E := 22BC6F1B171C08C4AE2F27444AF8FC8B3087A90006CAEA91FDCFB47C1B8733B8

Ensuite, calculez la balise d'authentification de Enc_GCM (K_E, nonce, "") pour AES-256-GCM donnée nonce = 096 et K_E comme ci-dessus.

result := E7DCCE66DF855A323A6BB7BD7A59BE45

Cela produit l’en-tête de contexte complet ci-dessous :

00 01 00 00 00 20 00 00 00 0C 00 00 00 10 00 00
00 10 E7 DC CE 66 DF 85 5A 32 3A 6B B7 BD 7A 59
BE 45

Les composants se décomposent comme suit :

  • le marqueur (00 01)

  • longueur de la clé de chiffrement de bloc (00 00 00 20)

  • la taille du nonce (00 00 00 0C)

  • taille du bloc du chiffrement par blocs (00 00 00 10)

  • taille (00 00 00 10) de la balise d’authentification et

  • balise d'authentification résultant de l'exécution du chiffrement par bloc (E7 DC - end).