Aprende a cifrar y descifrar archivos mediante el algoritmo estándar de cifrado avanzado con C#.

Cómo cifrar y descifrar archivos utilizando el algoritmo de cifrado AES en C#

Si los archivos de sus usuarios contienen información confidencial, puede cifrarla para que nadie pueda abrir ese archivo, excepto el propio usuario. Cifrar sus archivos dificulta que cualquiera pueda acceder a ellos y leerlos sin su contraseña. Si le gusta el tema de encriptación en su proyecto, en este artículo le mostraremos cómo encriptar y desencriptar archivos usando el algoritmo AES fácilmente.

Nota

Este artículo le muestra una forma de encriptar y desencriptar archivos de manera fácil y rápida utilizando métodos simples como encriptar y desencriptar. Son el resultado de una recopilación de información de diferentes fuentes como Stack Overflow, Security Exchange y el sitio web oficial de MSDN.

1. Importar tipos obligatorios

Para manejar el algoritmo de cifrado AES en su proyecto para cifrar y descifrar archivos, importe los 2 siguientes tipos obligatorios:

using System.Security.Cryptography;
using System.Runtime.InteropServices;

La referencia a InteropServices en la parte superior de su clase le permitirá usar más adelante el DllImportmétodo en nuestra clase.

2. Cree métodos de cifrado y descifrado

Deberá agregar los siguientes 3 métodos a su clase (o crearlos en una nueva clase y luego importarlos en la suya):

  • GenerateRandomSalt: este método crea una sal aleatoria. Esta función es personalizable y puede modificarla para crear su propia sal si es necesario.
  • FileEncrypt: este método cifra un archivo existente con una contraseña simple.
  • FileDecrypt: este método descifra un archivo previamente cifrado con el método FileEncrypt utilizando la contraseña simple como argumento.
  • ZeroMemory: este método se utilizará para eliminar la contraseña de la memoria, aumentando la seguridad del cifrado. No es necesario, pero se recomienda su uso.

El método se utilizará y explicará en el paso # 3, por ahora, copie e incluya los métodos en su proyecto:

//  Llame a esta función para eliminar la clave de la memoria después de su uso por motivos de seguridad
[DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
public static extern bool ZeroMemory(IntPtr Destination, int Length);

/// <summary>
/// Crea una sal aleatoria que se utilizará para cifrar su archivo. Este método es necesario en FileEncrypt.
/// </summary>
/// <returns></returns>
public static byte[] GenerateRandomSalt()
{
    byte[] data = new byte[32];

    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        for (int i = 0; i < 10; i++)
        {
            // Fille the buffer with the generated data
            rng.GetBytes(data);
        }
    }

    return data;
}

/// <summary>
/// Cifra un archivo de su ruta y una contraseña simple.
/// </summary>
/// <param name="inputFile"></param>
/// <param name="password"></param>
private void FileEncrypt(string inputFile, string password)
{
    //http://stackoverflow.com/questions/27645527/aes-encryption-on-large-files

    // generar salt aleatorio
    byte[] salt = GenerateRandomSalt();

    //create output file name
    FileStream fsCrypt = new FileStream(inputFile + ".aes", FileMode.Create);

    // convertir cadena de contraseña en matriz de bytes
    byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);

    // Establecer el algoritmo de cifrado simétrico de Rijndael
    RijndaelManaged AES = new RijndaelManaged();
    AES.KeySize = 256;
    AES.BlockSize = 128;
    AES.Padding = PaddingMode.PKCS7;

    //http://stackoverflow.com/questions/2659214/why-do-i-need-to-use-the-rfc2898derivebytes-class-in-net-instead-of-directly
    //"Lo que hace es hashear repetidamente la contraseña del usuario junto con la sal." High iteration counts.
    var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
    AES.Key = key.GetBytes(AES.KeySize / 8);
    AES.IV = key.GetBytes(AES.BlockSize / 8);

    // Modos de cifrado: http://security.stackexchange.com/questions/52665/which-is-the-best-cipher-mode-and-padding-mode-for-aes-encryption
    AES.Mode = CipherMode.CFB;

    //escriba salt al principio del archivo de salida, por lo que en este caso puede ser aleatorio cada vez
    fsCrypt.Write(salt, 0, salt.Length);

    CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);

    FileStream fsIn = new FileStream(inputFile, FileMode.Open);

    // crear un búfer (1 MB) para que solo esta cantidad se asigne en la memoria y no el archivo completo
    byte[] buffer = new byte[1048576];
    int read;

    try
    {
        while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
        {
            Application.DoEvents(); // -> for responsive GUI, using Task will be better!
            cs.Write(buffer, 0, read);
        }

        // cerrar
        fsIn.Close();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error: " + ex.Message);
    }
    finally
    {
        cs.Close();
        fsCrypt.Close();
    }
}

/// <summary>
/// Descifra un archivo cifrado con el método FileEncrypt a través de su ruta y la contraseña simple.
/// </summary>
/// <param name="inputFile"></param>
/// <param name="outputFile"></param>
/// <param name="password"></param>
private void FileDecrypt(string inputFile, string outputFile, string password)
{
    byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
    byte[] salt = new byte[32];

    FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
    fsCrypt.Read(salt, 0, salt.Length);

    RijndaelManaged AES = new RijndaelManaged();
    AES.KeySize = 256;
    AES.BlockSize = 128;
    var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
    AES.Key = key.GetBytes(AES.KeySize / 8);
    AES.IV = key.GetBytes(AES.BlockSize / 8);
    AES.Padding = PaddingMode.PKCS7;
    AES.Mode = CipherMode.CFB;

    CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);

    FileStream fsOut = new FileStream(outputFile, FileMode.Create);

    int read;
    byte[] buffer = new byte[1048576];

    try
    {
        while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
        {
            Application.DoEvents();
            fsOut.Write(buffer, 0, read);
        }
    }
    catch (CryptographicException ex_CryptographicException)
    {
        Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error: " + ex.Message);
    }

    try
    {
        cs.Close();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error by closing CryptoStream: " + ex.Message);
    }
    finally
    {
        fsOut.Close();
        fsCrypt.Close();
    }
}

No son necesariamente perfectos y pueden (y deben) modificarse para manejar más excepciones en caso de que aparezcan y cómo trabaja con su aplicación.

3. Usando los métodos

De los métodos requeridos, solo necesitará usar 2 de ellos (FileEncrypt y FileDecrypt) obviamente y 1 de ellos opcional, el cuarto (GenerateRandomSalt) es usado internamente por el FileEncryptmétodo.

Cifrar archivo

Cifre un archivo utilizando el método FileEncrypt que espera como primer argumento la ruta al archivo que se cifrará y como segundo argumento la contraseña que se utilizará para cifrarlo. La contraseña se puede utilizar para descifrar el archivo más tarde. Para hacer todo bien, le recomendamos que elimine la contraseña de la memoria utilizando el método ZeroMemory. Llame a esta función para eliminar la clave de la memoria después de su uso por motivos de seguridad:

string password = "ThePasswordToDecryptAndEncryptTheFile";

// Para mayor seguridad, fija la contraseña de tus archivos
GCHandle gch = GCHandle.Alloc(password, GCHandleType.Pinned);

// Cifre el archivo
FileEncrypt(@"C:\Users\username\Desktop\wordFileExample.doc", password);

// Para aumentar la seguridad del cifrado, ¡elimine la contraseña dada de la memoria!
ZeroMemory(gch.AddrOfPinnedObject(), password.Length * 2);
gch.Free();

// Puede verificarlo mostrando su valor más adelante en la consola (la contraseña no aparecerá)
Console.WriteLine("La contraseña dada seguramente no es nada:" + password);

El método FileEncrypt generará un archivo en el mismo directorio del archivo original con la extensión aes (por ejemplo wordFileExample.doc).

Descifrar archivo

Para descifrar el archivo, seguiremos el mismo proceso pero usando FileDecrypt en su lugar. Este método espera como primer argumento la ruta al archivo cifrado y como segundo argumento la ruta donde se debe colocar el archivo descifrado. Como tercer argumento, debe proporcionar la cadena que se utilizó para cifrar el archivo originalmente:

string password = "ThePasswordToDecryptAndEncryptTheFile";

// Para mayor seguridad, fija la contraseña de tus archivos
GCHandle gch = GCHandle.Alloc(password, GCHandleType.Pinned);

// Descifrar el archivo
FileDecrypt(@"C:\Users\sdkca\Desktop\example.doc.aes", @"C:\Users\sdkca\Desktop\example_decrypted.doc", password);

// Para aumentar la seguridad del descifrado, ¡elimine la contraseña utilizada de la memoria!
ZeroMemory(gch.AddrOfPinnedObject(), password.Length * 2);
gch.Free();

// Puede verificarlo mostrando su valor más adelante en la consola (la contraseña no aparecerá)
Console.WriteLine("The given password is surely nothing: " + password);

Notas finales

  • El proceso de cifrado / descifrado consume memoria y lleva tiempo, por lo que es recomendable ejecutar esas tareas en otro hilo para evitar que la interfaz de usuario principal se congele.
  • La extensión aes se puede cambiar por la extensión que desees

Que te diviertas ❤️!


Interesado en la programación desde los 14 años, Carlos es un programador autodidacta, fundador y autor de la mayoría de los artículos de Our Code World.

Conviertete en un programador más sociable

Patrocinadores