¿Cómo implementar protección con contraseña para archivos individuales?

Estoy escribiendo una pequeña aplicación de escritorio que debería poder encriptar un archivo de datos y protegerlo con una contraseña (es decir, uno debe ingresar la contraseña correcta para descifrar). Quiero que el archivo de datos encriptados sea autónomo y portátil, por lo que la autenticación debe estar integrada en el archivo (o eso supongo).

Tengo una estrategia que parece viable y parece lógica en función de lo que sé (que probablemente sea lo suficiente como para ser peligroso), pero no tengo idea si realmente es un buen diseño o no. Entonces dime: ¿está loco? ¿Hay una mejor / mejor manera de hacerlo?

  • Paso 1: el usuario ingresa la contraseña de texto sin formato, por ejemplo, “MyDifficultPassword”
  • Paso 2: la aplicación codifica la contraseña de usuario y usa ese valor como la clave simétrica para cifrar / descifrar el archivo de datos. por ejemplo, “MyDifficultPassword” -> “HashedUserPwdAndKey”.
  • Paso 3: la aplicación evalúa el valor hash del paso 2 y guarda el nuevo valor en el encabezado del archivo de datos (es decir, la parte no encriptada del archivo de datos) y utiliza ese valor para validar la contraseña del usuario. por ejemplo, “HashedUserPwdAndKey” -> “HashedValueForAuthentication”

Básicamente estoy extrapolando de la forma común de implementar las contraseñas del sitio web (cuando no estás usando OpenID), que es almacenar el hash (salado) de la contraseña del usuario en tu DB y nunca guardar la contraseña real . Pero como utilizo la contraseña de usuario hash para la clave de cifrado simétrica, no puedo usar el mismo valor para la autenticación. Así que lo puse de nuevo, básicamente tratándolo como otra contraseña, y guardo el valor de doble hash en el archivo de datos. De esa forma, puedo llevar el archivo a otra PC y descifrarlo simplemente ingresando mi contraseña.

Entonces, ¿este diseño es razonablemente seguro o irremediablemente ingenuo o está en algún punto intermedio? ¡Gracias!

EDIT: aclaración y pregunta de seguimiento re: Salt.
Pensé que la sal debía mantenerse en secreto para ser útil, pero sus respuestas y enlaces implican que este no es el caso. Por ejemplo, esta especificación vinculada por erickson (abajo) dice:

Por lo tanto, la derivación de clave basada en contraseña como se define aquí es una función de una contraseña, una sal y un recuento de iteraciones, donde las dos últimas cantidades no necesitan mantenerse en secreto.

¿Esto significa que podría almacenar el valor de sal en el mismo lugar / archivo como la clave hash y aún así ser más seguro que si no usara sal en absoluto cuando hashing? ¿Cómo funciona?

Un poco más de contexto: el archivo encriptado no está destinado a ser compartido o descifrado por otros, en realidad se trata de datos de un solo usuario. Pero me gustaría implementarlo en un entorno compartido en computadoras que no controlo completamente (por ejemplo, en el trabajo) y poder migrar / mover los datos simplemente copiando el archivo (para poder usarlo en casa, en diferentes estaciones de trabajo, etc.).

Generación clave

Yo recomendaría usar un algoritmo reconocido como PBKDF2 definido en PKCS # 5 versión 2.0 para generar una clave a partir de su contraseña. Es similar al algoritmo que delinea, pero es capaz de generar claves simétricas más largas para usar con AES. Debería poder encontrar una biblioteca de código abierto que implemente generadores de claves PBE para diferentes algoritmos.

Formato de archivo

También podría considerar utilizar la syntax de mensajes criptográficos como formato para su archivo. Esto requerirá un poco de estudio por su parte, pero nuevamente hay bibliotecas existentes para usar, y abre la posibilidad de interoperar más fluidamente con otro software, como clientes de correo habilitados para S / MIME.

Validación de contraseña

En cuanto a su deseo de almacenar un hash de la contraseña, si utiliza PBKDF2 para generar la clave, puede usar un algoritmo de hash de contraseña estándar (gran cantidad de sal, mil rondas de hash) para eso y obtener diferentes valores.

Alternativamente, puede calcular un MAC en el contenido. Una colisión hash en una contraseña es más probable que sea útil para un atacante; una colisión hash en el contenido es probable que no tenga valor. Pero serviría para que un destinatario legítimo sepa que se utilizó la contraseña incorrecta para el descifrado.

Sal criptográfica

La sal ayuda a frustrar los ataques de diccionario precalculados.

Supongamos que un atacante tiene una lista de contraseñas probables. Él puede hacer hash cada uno y compararlo con el hash de la contraseña de su víctima, y ​​ver si coincide. Si la lista es grande, esto podría llevar mucho tiempo. Él no quiere pasar tanto tiempo en su próximo objective, por lo que registra el resultado en un “diccionario” donde un hash apunta a su entrada correspondiente. Si la lista de contraseñas es muy, muy larga, puede usar técnicas como una Tabla Arcoíris para ahorrar espacio.

Sin embargo, supongamos que su próximo objective sacó su contraseña. Incluso si el atacante sabe cuál es la sal, su tabla precalculada no tiene ningún valor: la sal cambia el hash resultante de cada contraseña. Él tiene que volver a codificar todas las contraseñas en su lista, fijando la sal del objective a la entrada. Cada sal diferente requiere un diccionario diferente, y si se usan suficientes sales, el atacante no tendrá espacio para almacenar diccionarios para todos ellos. El espacio de negociación para ahorrar tiempo ya no es una opción; el atacante debe recurrir a hash cada contraseña en su lista para cada objective que quiere atacar.

Por lo tanto, no es necesario mantener la sal en secreto. Asegurar que el atacante no tenga un diccionario precalculado correspondiente a esa sal en particular es suficiente.

Como dijo Niyaz, el enfoque parece razonable si usas una implementación de calidad de algoritmos potentes, como SHA-265 y AES para hashing y encriptación. Además, recomendaría usar un Salt para reducir la posibilidad de crear un diccionario de todos los hash de contraseñas.

Por supuesto, leer la Criptografía Aplicada de Bruce Schneier nunca está mal.

Si está utilizando un algoritmo hash fuerte (SHA-2) y un fuerte algoritmo de cifrado (AES), le irá bien con este enfoque.

¿Por qué no usar una biblioteca de compresión que admita archivos protegidos con contraseña? He utilizado un archivo zip protegido con contraseña que contiene contenido XML en el pasado:}

¿Es realmente necesario guardar la contraseña hash en el archivo? ¿No puedes usar la contraseña (o la contraseña hash) con algo de sal y luego encriptar el archivo con ella? Cuando descifre, intente descifrar el archivo con la contraseña + salt. Si el usuario da una contraseña incorrecta, el archivo descifrado no es correcto.

Los únicos inconvenientes que puedo pensar es que si el usuario ingresa accidentalmente una contraseña incorrecta y el descifrado es lento, tiene que esperar para volver a intentarlo. Y, por supuesto, si se olvida la contraseña, no hay forma de descifrar el archivo.