Tengo un plan para manejar la autenticación y el cifrado de mensajes, y me gustaría que otros con más conocimientos de criptografía me informen si mi enfoque es correcto o cómo debería mejorarse.
Las aplicaciones del cliente se conectarán a mi servidor a través de un socket TCP / IP. Me gustaría evitar SSL / TLS. En el servidor, almacenaré para cada usuario (y cambiaré cada vez que el usuario cambie su contraseña): un salt aleatorio, el hash del UTF-8 de su contraseña más el salt, una clave simétrica y, posiblemente, el cifrado de esa clave simétrica (si la clave simétrica es aleatoria, en lugar de derivarse de la contraseña / salt).
Cuando los usuarios inician sesión, el cliente enviará el nombre de usuario, el servidor devolverá el salt y el cliente enviará el hash. Si el hash coincide, entonces el servidor enviará la clave simétrica cifrada, y el cliente la descifrará, y usará la clave simétrica descifrada para los mensajes cifrados posteriores. Si puedo derivar la clave simétrica a partir de la contraseña / sal, entonces no habrá necesidad de calcular y almacenar la clave simétrica cifrada, o enviarla al cliente para su descifrado.
Para el hashing de la contraseña / sal, había estado planeando simplemente usar SHA-384, pero se me indicó (por zaph) que debería "repetir una HMAC con una sal al azar durante aproximadamente 100 ms de duración y Salva la sal con el hachís ". No quiero usar otra biblioteca, entonces estoy pensando que esto es lo que quiso decir:
int
i;
byte[]
passwordBytes,
saltBytes,
saltedPasswordBytes,
hash;
passwordBytes = Encoding.UTF8.GetBytes( "MyPass" );
using ( RNGCryptoServiceProvider rngProvider = new RNGCryptoServiceProvider() )
{
saltBytes = new byte[ 128 ];
rngProvider.GetBytes( saltBytes );
}
// Salt the password.
saltedPasswordBytes = new byte[ passwordBytes.Length + saltBytes.Length ];
System.Buffer.BlockCopy(
passwordBytes, 0, saltedPasswordBytes, 0, passwordBytes.Length );
System.Buffer.BlockCopy(
saltBytes, 0, saltedPasswordBytes, passwordBytes.Length, saltBytes.Length );
using ( HMACSHA512 hmac = new HMACSHA512( saltedPasswordBytes ) )
{
// Get the hash of the salted password.
hash = hmac.ComputeHash( saltedPasswordBytes );
// Iterate, getting the hash of the hash.
for ( i = 0; i < 2000; i++ )
hash = hmac.ComputeHash( hash );
}
Otra gran preocupación es cómo cifrar la clave simétrica. Originalmente estaba pensando en AES y enviando la clave simétrica cifrada al cliente, pero me pregunto si debería usar algo como el anterior, pero varió ligeramente, como poner un cero en la parte frontal de los bytes de la contraseña con sal antes de hacer hashing y luego tomar los primeros 32 bytes de eso para la clave. De esa manera, ni siquiera necesitaré cifrar la clave simétrica.
Mi siguiente problema es el cifrado de mensajes. Después de leer la publicación NIST 800-38A y las publicaciones del blog, estoy tan confundido acerca de los modos de cifrado. CTR suena bien, pero no es compatible con .NET, y no quiero usar bibliotecas de terceros. Dado que mis mensajes pueden ser bastante largos y tener muchas porciones repetitivas, el BCE parece una mala elección. Usando otro, como CBC, ahora necesito lidiar con el IV. Un método que leí es usar un nuevo IV aleatorio con cada mensaje y enviarlo antes de los bytes cifrados. Eso suena bien para el primer mensaje enviado, pero me gustaría mantener los mensajes pequeños, por lo que estaba pensando en alterar el IV para cada mensaje subsiguiente, usando un algoritmo que se aplica a ambos lados. Leí que la IV para los mensajes subsiguientes debe ser impredecible, por lo que supongo que debo tomar el hash de un contador y XOR a la IV anterior o algo así.
Entonces, expertos, ¿voy a hacer esto de la manera correcta? ¿Es esa iteración correcta? ¿Es una buena idea usar ese enfoque (varió levemente, como con un cero delante de la contraseña y la sal) para determinar la clave simétrica? ¿Qué modo de cifrado (y método IV, si corresponde) debo usar para cifrar mensajes?
Gracias.