Compartir vía


Encabezados de contexto en ASP.NET Core

Antecedentes y teoría

En el sistema de protección de datos, una "clave" significa un objeto que puede proporcionar servicios de cifrado autenticados. Cada clave se identifica mediante un identificador único (un GUID) y lleva con ella información algorítmica y material entrópico. Está pensado para que cada clave lleve una entropía única, pero el sistema no puede exigirlo, y también es necesario tener en cuenta a los desarrolladores que podrían cambiar manualmente el anillo de claves modificando la información algorítmica de una clave existente en el anillo de claves. Para lograr nuestros requisitos de seguridad dados estos casos, el sistema de protección de datos tiene un concepto de agilidad criptográfica, lo que permite usar de forma segura un único valor entrópico en varios algoritmos criptográficos.

La mayoría de los sistemas que admiten la agilidad criptográfica lo hacen mediante la inclusión de información de identificación sobre el algoritmo dentro de la carga. El OID del algoritmo suele ser un buen candidato para esto. Sin embargo, un problema que hemos tenido es que hay varias maneras de especificar el mismo algoritmo: las clases "AES" (CNG) y los Aes administrados, AesManaged, AesCryptoServiceProvider, AesCng y RijndaelManaged (determinados parámetros específicos) son realmente lo mismo, y tendríamos que mantener una asignación de todos ellos al OID correcto. Si un desarrollador quería proporcionar un algoritmo personalizado (o incluso otra implementación de AES), tendría que indicarnos su OID. Este paso de registro adicional hace que la configuración del sistema sea especialmente dolorosa.

Retrocedemos, y decidimos que nos acercamos al problema desde la dirección equivocada. Un OID le indica cuál es el algoritmo, pero realmente no nos importa esto. Si necesitamos usar un único valor entrópico de forma segura en dos algoritmos diferentes, no es necesario que sepamos cuáles son realmente los algoritmos. Lo que realmente nos importa es cómo se comportan. Cualquier algoritmo de cifrado de bloque simétrico decente también es una fuerte permutación de pseudoaleatorio (PRP): corrija las entradas (clave, modo de encadenamiento, IV, texto sin formato) y la salida del texto cifrado con una probabilidad abrumadora será distinta de cualquier otro algoritmo de cifrado de bloque simétrico dadas las mismas entradas. Del mismo modo, cualquier función hash con clave decente también es una función pseudoaleatoria fuerte (PRF), y dado un conjunto de entrada fijo, su salida será abrumadoramente distinta de cualquier otra función hash con clave.

Usamos estos conceptos de PRP y PRF seguros para crear un encabezado de contexto. Este encabezado de contexto actúa básicamente como una huella digital estable sobre los algoritmos que se usan para cualquier operación determinada y proporciona la agilidad criptográfica necesaria para el sistema de protección de datos. Este encabezado es reproducible y se usa más adelante como parte del proceso de derivación de subclave. Hay dos maneras diferentes de crear el encabezado de contexto en función de los modos de funcionamiento de los algoritmos subyacentes.

Cifrado en modo CBC + autenticación HMAC

El encabezado de contexto consta de los siguientes componentes:

  • [16 bits] El valor 00 00, que es un marcador que significa "Cifrado CBC + autenticación HMAC".

  • [32 bits] La longitud de clave (en bytes, big-endian) del algoritmo de cifrado de bloques simétrico.

  • [32 bits] el tamaño del bloque (en bytes, big-endian) del algoritmo de cifrado de bloques simétrico.

  • [32 bits] La longitud de clave (en bytes, big-endian) del algoritmo HMAC. (Actualmente, el tamaño de clave siempre coincide con el tamaño de hash).

  • [32 bits] El tamaño de hash (en bytes, big-endian) del algoritmo HMAC.

  • EncCBC(K_E, IV, ""), que es la salida del algoritmo de cifrado de bloques simétricos dada una entrada de cadena vacía y donde IV es un vector todo cero. La construcción de K_E se describe a continuación.

  • MAC(K_H, ""), que es la salida del algoritmo HMAC dada una entrada de cadena vacía. La construcción de K_H se describe a continuación.

Idealmente, podríamos pasar vectores de todo cero para K_E y K_H. Sin embargo, queremos evitar la situación en la que el algoritmo subyacente comprueba la existencia de claves débiles antes de realizar cualquier operación (en particular DES y 3DES), lo que impide el uso de un patrón simple o repetible como un vector todo cero.

En su lugar, usamos NIST SP800-108 KDF en modo contador (vea NIST SP800-108, Sec. 5.1) con una clave de longitud cero, etiqueta y contexto y HMACSHA512 como PRF subyacente. Derivamos | K_E | + | K_H | bytes de salida y, a continuación, descomponemos el resultado en K_E y K_H en sí mismos. Matemáticamente, esto se representa de la siguiente manera.

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

Ejemplo: AES-192-CBC + HMACSHA256

Por ejemplo, considere el caso en el que el algoritmo de cifrado de bloques simétrico es AES-192-CBC y el algoritmo de validación es HMACSHA256. El sistema generaría el encabezado de contexto mediante los pasos siguientes.

En primer lugar, deje ( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = ""), donde | K_E | = 192 bits y | K_H | = 256 bits por los algoritmos especificados. Esto conduce a K_E = 5BB6..21DD y K_H = A04A..00A9 en el ejemplo siguiente:

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

A continuación, calcule Enc_CBC (K_E, IV, "") para AES-192-CBC dado IV = 0* y K_E como se ha indicado anteriormente.

result := F474B1872B3B53E4721DE19C0841DB6F

A continuación, calcule MAC(K_H, "") para HMACSHA256 dado K_H tal y como se ha indicado anteriormente.

result := D4791184B996092EE1202F36E8608FA8FBD98ABDFF5402F264B1D7211536220C

Esto genera el encabezado de contexto completo siguiente:

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

Este encabezado de contexto es la huella digital del par de algoritmos de cifrado autenticado (cifrado AES-192-CBC + validación HMACSHA256). Los componentes, como se describió anteriormente, son:

  • el marcador (00 00)

  • la longitud de clave de cifrado de bloque (00 00 00 18)

  • el tamaño del bloque de cifrado de bloques (00 00 00 10)

  • la longitud de la clave HMAC (00 00 00 20)

  • el tamaño del hash de HMAC (00 00 00 20)

  • la salida de PRP de cifrado de bloques (F4 74 - DB 6F) y

  • la salida de PRF de HMAC (D4 79 - end).

Nota:

El encabezado de contexto de cifrado en modo CBC + autenticación HMAC se crea de la misma manera, independientemente de si las implementaciones de algoritmos las proporciona Windows CNG o los tipos SymmetricAlgorithm y KeyedHashAlgorithm administrados. Esto permite a las aplicaciones que se ejecutan en diferentes sistemas operativos generar de forma confiable el mismo encabezado de contexto, aunque las implementaciones de los algoritmos difieren entre los sistemas operativos. (En la práctica, el KeyedHashAlgorithm no tiene que ser un HMAC adecuado. Puede ser cualquier tipo de algoritmo hash con clave).

Ejemplo: 3DES-192-CBC + HMACSHA1

En primer lugar, deje ( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = ""), donde | K_E | = 192 bits y | K_H | = 160 bits por los algoritmos especificados. Esto conduce a K_E = A219..E2BB y K_H = DC4A..B464 en el ejemplo siguiente:

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

A continuación, calcule Enc_CBC (K_E, IV, "") para 3DES-192-CBC dado IV = 0* y K_E como se ha indicado anteriormente.

result := ABB100F81E53E10E

A continuación, calcule MAC(K_H, "") para HMACSHA1 dado K_H tal y como se ha indicado anteriormente.

result := 76EB189B35CF03461DDF877CD9F4B1B4D63A7555

Esto genera el encabezado de contexto completo que es una huella digital del par de algoritmos de cifrado autenticado (cifrado 3DES-192-CBC + validación HMACSHA1), que se muestra a continuación:

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

Los componentes se desglosan de la siguiente manera:

  • el marcador (00 00)

  • la longitud de clave de cifrado de bloque (00 00 00 18)

  • el tamaño del bloque de cifrado de bloques (00 00 00 08)

  • la longitud de la clave HMAC (00 00 00 14)

  • el tamaño del hash de HMAC (00 00 00 14)

  • la salida de PRP de cifrado de bloques (AB B1 - E1 0E) y

  • la salida de PRF de HMAC (76 EB - end).

Cifrado en modo galois/contador + autenticación

El encabezado de contexto consta de los siguientes componentes:

  • [16 bits] El valor 00 01, que es un marcador que significa "Cifrado GCM + autenticación".

  • [32 bits] La longitud de clave (en bytes, big-endian) del algoritmo de cifrado de bloques simétrico.

  • [32 bits] El tamaño de nonce (en bytes, big-endian) usado durante las operaciones de cifrado autenticadas. (Para nuestro sistema, esto se fija con un tamaño de nonce = 96 bits).

  • [32 bits] el tamaño del bloque (en bytes, big-endian) del algoritmo de cifrado de bloques simétrico. (Para GCM, esto se fija en el tamaño de bloque = 128 bits).

  • [32 bits] El tamaño de etiqueta de autenticación (en bytes, big-endian) generado por la función de cifrado autenticada. (Para nuestro sistema, esto se fija en el tamaño de etiqueta = 128 bits).

  • [128 bits] La etiqueta de Enc_GCM (K_E, nonce, ""), que es la salida del algoritmo de cifrado de bloques simétricos dada una entrada de cadena vacía y donde nonce es un vector todo cero de 96-bit.

K_E se deriva mediante el mismo mecanismo que en el escenario de cifrado CBC + autenticación HMAC. Sin embargo, dado que no hay ningún K_H en juego aquí, básicamente tenemos | K_H | = 0, y el algoritmo se contrae al formulario siguiente.

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

Ejemplo: AES-256-GCM

En primer lugar, deje K_E = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = ""), donde | K_E | = 256 bits.

K_E := 22BC6F1B171C08C4AE2F27444AF8FC8B3087A90006CAEA91FDCFB47C1B8733B8

A continuación, calcule la etiqueta de autenticación de Enc_GCM (K_E, nonce, "") para AES-256-GCM dado nonce = 096 y K_E como se ha indicado anteriormente.

result := E7DCCE66DF855A323A6BB7BD7A59BE45

Esto genera el encabezado de contexto completo siguiente:

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

Los componentes se desglosan de la siguiente manera:

  • el marcador (00 01)

  • la longitud de clave de cifrado de bloque (00 00 00 20)

  • el tamaño de nonce (00 00 00 0C)

  • el tamaño del bloque de cifrado de bloques (00 00 00 10)

  • el tamaño de la etiqueta de autenticación (00 00 00 10) y

  • la etiqueta de autenticación de la ejecución del cifrado de bloques (E7 DC - end).