27 noviembre 2023

La encriptadera en C#

Los datos son importantes y, como programadores, es nuestro deber mantenerlos seguros. En este post en mi blog voy a explicar cómo usar algunos métodos de la librería System.Security.Cryptography para encriptar y desencriptar datos de texto.

Hice un programa que lo ejemplifica (pongo todo le código al final), pero como dijo Jack el Destripador, vámonos por partes.

Al principio debo importar la librería System.Security.Cryptography para que todo esto funcione. Por eso al principio del programa lo incluyo con el using:

using System.Security.Cryptography;

Pero esto no basta para que funcione. Para que tu programa pueda hacer uso de las clases y objetos de esta librería, se tiene que incluir al proyecto una referencia al framework que contiene lo de seguridad. En su proyecto, hagan clic derecho en la ventana de su solución donde dice referencias, como se ve en esta imagen:


Del menú contextual que aparece, seleccionen Agregar referencia... y aparece una ventana con todas las referencias, que por default están apareciendo las referencias de NET Framework. Allí pon una tache en la que dice System.Security como se ve en la siguiente imagen:


El método encriptar toma una cadena (textoNormal) y un arreglo de bytes opcional (entropia) para agregar aleatoriedad al proceso de encriptación. Convierte el texto a bytes y utiliza el método ProtectedData.Protect para encriptar los datos. El resultado es un arreglo de bytes que representa el texto encriptado. Éste es el código simplificado:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
/// <summary>
/// Función que encripta un texto
/// </summary>
/// <param name="textoNormal">cadena que se desea encriptar</param>
/// <param name="entropia">arreglo de bytes que añaden aleatoriedad a la función</param>
/// <returns></returns>
public static byte[] encriptar(string textoNormal, byte[] entropia = null)
{
    // Creo arreglo de bytes del texto normal
    byte[] bytesNormal = Encoding.UTF8.GetBytes(textoNormal);

    // Devuelvo el texto encriptado
    return ProtectedData.Protect(bytesNormal, entropia, DataProtectionScope.CurrentUser);
}
El método desecriptar toma un arreglo de bytes (bytesEncriptados) y un arreglo de bytes opcional (entropia). Desencripta los datos utilizando el método ProtectedData.Unprotect y convierte los bytes resultantes de nuevo a una cadena utilizando la codificación UTF-8.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
        /// <summary>
        /// Desencripta un arreglo de bytes que han sido encriptados
        /// </summary>
        /// <param name="bytesEncriptados">bytes de datos encriptados</param>
        /// <param name="entropia">arreglo de bytes que añaden aleatoriedad a la función</param>
        /// <returns>cadena con el texto original</returns>
        public static string desencriptar(byte[] bytesEncriptados, byte[] entropia = null)
        {
            // Desencripto el arreglo de bytes y lo almaceno en otro
            byte[] bytesNormales = ProtectedData.Unprotect(bytesEncriptados, entropia, 
                DataProtectionScope.CurrentUser);

            // Devuelvo el arreglo convertido a cadena
            return Encoding.UTF8.GetString(bytesNormales);
        }

Y hacer clic en el botón solo agarra el texto que está en la caja de texto, lo encripte, muestra el arreglo de bytes encriptados en una etiqueta, desencripta el arreglo de bytes y muestra el resultado en otra etiqueta.

Pero antes de mostrar el código, ¿qué hace ProtectedData.Protect? Ésta función permite tomar información y convertirla en una forma que es difícil de entender para cualquier persona que no tenga la clave adecuada para descifrarla.

Al usarlo le proporcionas la información que deseas cifrar (por ejemplo, una contraseña) y, opcionalmente, también puedes proporcionar algo llamado "entropía". La entropía es como agregar un toque adicional de aleatoriedad para hacer que el cifrado sea aún más seguro. Y la función devuelve los datos cifrados, que luego puedes almacenar de forma segura en tu aplicación o base de datos.

Y ProtectedData.Unprotect es una función en C# que hace el proceso inverso de lo que hace ProtectedData.Protect. Es decir, toma datos cifrados (información que ha sido previamente encriptada) y los descifra para devolverlos a su forma original o legible.

Ahora sí, aquí va el código completo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Collections;
using System.Security.Cryptography.Xml;

namespace Encripcion
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnSalir_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        /// <summary>
        /// Función que encripta un texto
        /// </summary>
        /// <param name="textoNormal">cadena que se desea encriptar</param>
        /// <param name="entropia">arreglo de bytes que añaden aleatoriedad a la función</param>
        /// <returns></returns>
        public static byte[] encriptar(string textoNormal, byte[] entropia = null)
        {
            // Creo arreglo de bytes del texto normal
            byte[] bytesNormal = Encoding.UTF8.GetBytes(textoNormal);

            // Devuelvo el texto encriptado
            return ProtectedData.Protect(bytesNormal, entropia, DataProtectionScope.CurrentUser);
        }

        /// <summary>
        /// Desencripta un arreglo de bytes que han sido encriptados
        /// </summary>
        /// <param name="bytesEncriptados">bytes de datos encriptados</param>
        /// <param name="entropia">arreglo de bytes que añaden aleatoriedad a la función</param>
        /// <returns>cadena con el texto original</returns>
        public static string desencriptar(byte[] bytesEncriptados, byte[] entropia = null)
        {
            // Desencripto el arreglo de bytes y lo almaceno en otro
            byte[] bytesNormales = ProtectedData.Unprotect(bytesEncriptados, entropia, 
                DataProtectionScope.CurrentUser);

            // Devuelvo el arreglo convertido a cadena
            return Encoding.UTF8.GetString(bytesNormales);
        }

        private void btnEncriptar_Click(object sender, EventArgs e)
        {
            // Éste es el texto a encriptar
            string textoOriginal = txtTextoOrig.Text;

            // Obtengo el arreglo de bytes encriptados
            byte[] resultadoEncriptado = encriptar(textoOriginal);

            // Muestro los bytes encriptados
            lblEncript.Text = "";
            for (int i=0;i < resultadoEncriptado.Length; i++)
            {
                lblEncript.Text = lblEncript.Text + " " + resultadoEncriptado[i];
            }

            // Desecripto los datos y los pongo en la etiqueta
            lblNormal.Text = desencriptar(resultadoEncriptado);
        }
    }
}

Espero que les haya sido de utilidad. ¡Saludos!

P.D. La imagen de encripción al principio del post fue tomada de este sitio: https://www.pcworld.com/article/540005/what-is-encryption.html

14 noviembre 2023

Archivos CSV en C#

Los archivos de texto siguen siendo de mucha utilidad (como comentaba en mi post anterior sobre archivos txt en C++). Pero son aún más útiles si con CSV (para una explicación completa sobre archivos CSV, haz clic aquí). Y en este post te voy a platicar cómo crearlos en C#.

En resumidas cuentas, un archivo CSV es un archivo de texto que contiene datos los cuales están separados con comas (por eso CSV, comma separated values). Son muy útiles porque se abren de forma nativa en Excel y desde allí puedes hacer mil cosas con los datos).

Para escribir en un archivo de texto, necesitamos objetos de las clases StreamWriter (para escribir al archivo) y StreamReader (para leer datos). 

Para este ejemplo, hice un nuevo proyecto Aplicación de Windows Forms (.NET Framework) e hice la siguiente interfaz:


En esta ventana voy a pedir nombre, edad y semestre en cajas de texto, selecciona un tipo de bachillerato de un combobox. Abajo hay una lista donde voy a poner los alumnos registrados. Los botones tienen las siguientes funciones: agregar el alumno (sus datos están en las cajas de texto y combobox) a la lista. Otro botón va a eliminar todo lo que está en la lista. Otro botón guarda lo que está en la lista al archivo, mientras que otro lee del archivo y pone en la lista los datos tal y como están guardados. Y al final un botón para salir del programa.

Me voy a enfocar en el código que manipula el archivo (escribir datos en él y leer datos de él). Al final pongo el código completo.

Éste es el método que se encarga de guardar todo lo que está en la lista al archivo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/// <summary>
/// Guarda  lo que está en el stringbuilder a un archivo
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGuardar_Click(object sender, EventArgs e)
{
    // Creo el StreamWriter y abro el archivo para escritura
    StreamWriter streamWriter = new StreamWriter("miarchivo.csv");

    // Escribo todo lo del StringBuilder al archivo
    for (int i=0;i<lstAlumnos.Items.Count;i++)
    {
        streamWriter.WriteLine(lstAlumnos.Items[i].ToString());
    }

    // Cierro el archivo
    streamWriter.Close();
}
Aquí estamos abriendo el archivo para escritura en un objeto llamado streamWriter (que es de la clase StreamWriter). El constructor de StreamWriter abre el archivo y se encarga de todo lo necesario. Luego hay un ciclo que va por todos los elementos de la lista, escribiendo línea por línea al archivo. Al final cierras el archivo y todos contentos.

Este es el método para leer línea por línea del archivo y ponerlo en la lista. Éste es el código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/// <summary>
/// Elimina todo de la lista, lee el archivo línea por línea y lo va agregando a la
/// lista.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLeer_Click(object sender, EventArgs e)
{
    // Elimino todo en la lista
    lstAlumnos.Items.Clear();

    // Abro archivo para lectura
    StreamReader streamReader = new StreamReader("miarchivo.csv");

    // Voy por todos los registros, leyendo una línea y agregándolo a la lista
    // Mientras no llego al final del archivo...
    while (!streamReader.EndOfStream)
    {
        // Leo una línea del archivo
        string linea = streamReader.ReadLine();
        // Agrego esta línea a la lista
        lstAlumnos.Items.Add(linea);
    }

    // Cierro el archivo
    streamReader.Close();
}
Empiezo eliminando todos los elementos de la lista. Luego creo el objeto StreamReader (y al crearlo abre el archivo). Con el archivo abierto, tengo un ciclo que lee línea por línea (EndOfStream me dice si llegué al final del archivo y el ReadLine lee toda una línea y devuelve un string con lo que leí). Y esa cadena lo agrego a la lista. Al terminar el ciclo, cierro el archivo y ya.

Aquí les pongo el código de toda la ventana, esperando que sea de utilidad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
using System;
using System.IO;
using System.Windows.Forms;

namespace ArchivosCSV
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Pregunta si desea salir y termina la ejecución del programa
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSalir_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("¿Desea salir?","CSV",MessageBoxButtons.YesNo,
                MessageBoxIcon.Question) == DialogResult.Yes)
            {
                Application.Exit();
            }
        }

        /// <summary>
        /// Agrega los datos a un StringBuilder
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAgregar_Click(object sender, EventArgs e)
        {
            // Creo la cadena con todos los valores separados por comas
            string linea = txtNombre.Text + "," + txtEdad.Text + "," + txtSemestre.Text + ","
                + cboBach.Items[cboBach.SelectedIndex];

            // A la listbox le agrego esa línea
            lstAlumnos.Items.Add(linea);

            // Dejo en blanco todos los campos
            txtEdad.Text = "";
            txtNombre.Text = "";
            txtSemestre.Text = "";
            cboBach.SelectedIndex = 0;
        }

        // Elimina todos los elementos de la lista y del stringbuilder
        private void btnLimpiar_Click(object sender, EventArgs e)
        {
            // Elimino todo lo de la lista
            lstAlumnos.Items.Clear();
        }

        /// <summary>
        /// Selecciona el primer elemento del combobox
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            cboBach.SelectedIndex = 0;
        }

        /// <summary>
        /// Guarda  lo que está en el stringbuilder a un archivo
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGuardar_Click(object sender, EventArgs e)
        {
            // Creo el StreamWriter y abro el archivo para escritura
            StreamWriter streamWriter = new StreamWriter("miarchivo.csv");

            // Escribo todo lo del StringBuilder al archivo
            for (int i=0;i<lstAlumnos.Items.Count;i++)
            {
                streamWriter.WriteLine(lstAlumnos.Items[i].ToString());
            }

            // Cierro el archivo
            streamWriter.Close();
        }

        /// <summary>
        /// Elimina todo de la lista, lee el archivo línea por línea y lo va agregando a la
        /// lista.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLeer_Click(object sender, EventArgs e)
        {
            // Elimino todo en la lista
            lstAlumnos.Items.Clear();

            // Abro archivo para lectura
            StreamReader streamReader = new StreamReader("miarchivo.csv");

            // Voy por todos los registros, leyendo una línea y agregándolo a la lista
            // Mientras no llego al final del archivo...
            while (!streamReader.EndOfStream)
            {
                // Leo una línea del archivo
                string linea = streamReader.ReadLine();
                // Agrego esta línea a la lista
                lstAlumnos.Items.Add(linea);
            }

            // Cierro el archivo
            streamReader.Close();
        }
    }
}
Espero que les haya sido de utilidad. ¡Hasta la próxima!

07 noviembre 2023

Archivos de texto en C++

Los archivos de texto son muy usados en estos tiempos, a pesar de que hay sistemas DBMS y mil otras cosas para almacenar datos. Pero, a pesar de todo, siguen siendo muy útiles. Y programar archivos de texto en C++ es muy sencillo y eso te voy a mostrar en este post de mi blog donde quiero mostrarte como abrir, escribir a, leer de y cerrar archivos en este lenguaje.

Abrir un archivo
Antes de poder hacer cualquier otra cosa, hay que abrir el archivo. Para hacerlo, utilizamos la clase fstream. Puedes abrir un archivo para escritura (ofstream), lectura (ifstream), o ambas (fstream). Aquí tienes un ejemplo de cómo abrir un archivo para escritura:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include <iostream>
#include <fstream>

using namespace std;

int main() {
    ofstream archivo_salida("mi_archivo.txt");

    if (archivo_salida.is_open()) {
        cout << "El archivo se abrió exitosamente.\n";
        // Realiza operaciones de escritura aquí
        archivo_salida.close(); // Cierra el archivo
    } else {
        cout << "Error al abrir el archivo.\n";
    }
}

Escritura en un archivo de texto
Ya que esté abierto el archivo, se escribe texto en él usando el operador << como se ve en este ejemplo:

1 2 3 4 5 6 7 8
ofstream archivo_salida("mi_archivo.txt");

if (archivo_salida.is_open()) {
    archivo_salida << "Hola, esto es un ejemplo de escritura en un archivo.\n";
    archivo_salida.close(); // No olvides cerrar el archivo
} else {
    cout << "Error al abrir el archivo.\n";
}

Leer del archivo
Para leer desde un archivo, primero abrimos el archivo en modo lectura (ifstream) y luego usamos el operador >> o la función getline para leer los datos. Aquí hay un ejemplo de lectura utilizando getline:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    ifstream archivo_entrada("mi_archivo.txt");
    string linea;

    if (archivo_entrada.is_open()) {
        while (getline(archivo_entrada, linea)) {
            cout << linea << endl;
        }

        archivo_entrada.close(); // Cierra el archivo después de leer
    } else {
        cout << "Error al abrir el archivo.\n";
    }

    return 0;
}

Cerrar el archivo
Es importante recordar cerrar el archivo una vez que hayamos terminado de trabajar con él. Esto garantiza que todos los datos se escriban o lean correctamente y que el archivo quede en un estado consistente.

Ejemplo completo
Aquí está un ejemplo que muestra cómo abrir un archivo, escribir en él y luego leer su contenido:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    // Apertura del archivo para escritura
    ofstream archivo_salida("mi_archivo.txt");

    if (archivo_salida.is_open()) {
        archivo_salida << "Hola, esto es un ejemplo de escritura en un archivo." << endl;
        archivo_salida.close();
    } else {
        cout << "Error al abrir el archivo para escritura.\n";
        return 1; // Salir del programa con código de error
    }

    // Apertura del archivo para lectura
    ifstream archivo_entrada("mi_archivo.txt");
    string linea;

    if (archivo_entrada.is_open()) {
        while (getline(archivo_entrada, linea)) {
            cout << linea << endl;
        }

        archivo_entrada.close();
    } else {
        cout << "Error al abrir el archivo para lectura.\n";
        return 1; // Salir del programa con código de error
    }

    return 0; // El programa termina con éxito
}

Espero que todo esto te sea útil. ¡Hasta la próxima!


El Tony y sus ondas...

Related Posts Plugin for WordPress, Blogger...