Lo sé, el consejo general es "mantener las manos alejadas de las cosas criptográficas". Y la forma estándar de cifrar datos de copia de seguridad de forma segura sería utilizando GnuPG. Sin embargo, para un ejercicio bastante académico, me gustaría pensar en un protocolo que funcionaría solo con las herramientas estándar de OpenSSL (es decir, dgst, enc, rsautl & Co) invocado desde un shell POSIX.
El escenario incluye un sistema más o menos confiable y desde el cual se crean las copias de seguridad, esto podría e. sol. Ser un servidor de correo o archivos. Luego hay un almacenamiento que no es 100% confiable (por ejemplo, un almacenamiento en la nube o un servidor de respaldo que no está completamente bajo mi control). Por lo tanto, el propósito del cifrado es proteger la confidencialidad y la integridad de los datos mientras se almacenan en el almacenamiento no confiable. Esta protección también debería funcionar en caso de que el sistema de confianza se vea comprometido, al menos para los datos almacenados antes de que se comprometiera el sistema de confianza.
Así que aquí hay un tipo de protocolo que debería cumplir con los criterios mencionados anteriormente:
Paso 1: Par de claves RSA
Este paso solo debe realizarse una vez. Se necesita un par de claves RSA. Este par de claves no se creará en el sistema "confiable", sino en un cuadro realmente confiable (por ejemplo, una estación de trabajo confiable o en cualquier lugar en el que confíe para mantener su conjunto de claves GPG). Dado que la clave privada solo es necesaria para la recuperación, nunca ingresará a los dominios del sistema confiable o del almacenamiento no confiable. El tamaño de la clave debe ser 4096 bits o superior (veremos eso más adelante). OpenSSL generalmente ofrece la generación de claves y la separación mediante esos comandos:
EDIT: cambió a PKCS # 8 para las claves, probablemente sea más resistente a los ataques de fuerza bruta
openssl genpkey -aes-256-cbc -algorithm RSA -pkeyopt 'rsa_keygen_bits:8192' -out private.key
openssl pkey -in private.key -out public.key -pubout
Paso 2: generar secretos de sesión
Para cada proceso de cifrado, se generarán secretos dedicados (clave, vector de inicialización y sal):
EDITAR: se eliminó la sal según el comentario de Ctulhu
key=$(openssl rand -hex 32)
iv=$(openssl rand -hex 16)
hmacpw=$(openssl rand -base64 48)
Tenga en cuenta: soy consciente de que esto tendrá los secretos sin proteger en la memoria de la máquina de confianza. Sin embargo, si un atacante tiene acceso a la máquina de manera que pueda hojear la memoria del proceso de copia de seguridad, probablemente ya tenga acceso a los datos para estar protegidos por estos secretos.
Paso 3: Comprimir y cifrar
Los datos se comprimirán antes del cifrado. De todos modos, se requiere compresión, por lo que considero una buena idea hacerlo antes de cifrar los datos:
EDITAR: se eliminó la sal según el comentario de Ctulhu
data-generator | xz -zc | openssl enc -e -aes-256-ctr -K $key -iv $iv -out message.enc
Paso 4: generar HMAC y empaquetar las claves
El último paso sería generar un HMAC de los datos simplemente encriptados y empaquetarlos junto con los secretos de la sesión en un archivo encriptado RSA, utilizando la clave pública para el cifrado:
EDITAR: se eliminó la sal según el comentario de Ctulhu
hmac=$(openssl dgst -sha512 -hmac $hmacpw -hex message.enc | sed 's/^.*=[[:space:]]//g')
echo "${key}:${iv}:${hmacpw}:${hmac}" | openssl rsautl -inkey public.key -pubin -encrypt -out message.key
Y aquí resulta obvio por qué la clave RSA tenía que ser tan grande: los dígitos hexadecimales en las cadenas de shell se cuentan como un byte cada uno, no como medio byte. Por lo tanto, el tamaño de la cadena de clave suma hasta (2x32 + 2x16 + 64 + 128 + 5) = 293 bytes = 2344 bits.
Una vez más, soy consciente de que exponer secretos en una línea de comando generalmente es una mala idea, pero dadas las circunstancias, no veo cómo esto pueda comprometer la confidencialidad o integridad de los datos que están disponibles en texto sin formato en dicho sistema. donde un atacante podría comprometer mucho más su privacidad o integridad. Sin embargo, si realmente quisiera implementar esto, preferiría utilizar canalizaciones con nombre para evitar que los secretos aparezcan en la lista de procesos.
Ahora mis preguntas:
- ¿Qué me perdí? ¿Ves algún defecto o posible vector de ataque que no haya notado todavía?
- ¿Sería más seguro crear una firma con
openssl dgst -sha512 -sign private.key -out message.sig message.enc
, dado que esto requeriría que la clave privada residiera en el sistema más o menos confiable?