Blog de Jorge Pedraza

enero 29, 2011

Funcion Cifrado .NET

Filed under: Seguridad — JorgePedraza @ 10:50 am
Tags: , , , ,

FUNCIÓN CIFRADO .NET

El Presente articulo explica como crear y utilizar una función genérica dentro de la Plataforma .NET para encriptar y desencriptar datos, en general cadena de caracteres; utilizando un algoritmo genérico que hace uso de los diferentes estándares de servicio de cifrado que proporciona el .NET Framework.

  • DES (Digital Encryption Standard)
  • Triple Des
  • Rijndael
  • RC2

Indice:

Requerimientos:

  • Sistema Operativo Windows XP o Superior
  • Sistema de Desarrollo Microsoft Visual Studio 2010

La Función descrita en este articulo, solo se requiere que tenga conocimiento de la herramienta de desarrollo Microsoft Visual Basic 2010

Introducción:

La criptografía impide la visión o modificación de los datos, y ofrece canales de comunicación seguros que de lo contrario resultarían inseguros. Por ejemplo, los datos pueden cifrarse usando un algoritmo criptográfico estandar, durante la transmisión de información en la red. Sí un tercero intercepta los datos cifrados, le resultará difícil descifrarlos.

Primitiva criptográfica
Uso
Cifrado de clave secreta (criptografía simétrica) Realiza la transformación de los datos, impidiendo que terceros los lean. Este tipo de cifrado utiliza una clave secreta compartida para cifrar y descifrar los datos.
Cifrado de clave pública (criptografía asimétrica) Realiza la transformación de los datos, impidiendo que terceros los lean. Este tipo de cifrado utiliza un par de claves pública y privada para cifrar y descifrar los datos.
Firmas criptográficas Garantiza que los datos se originen en una parte específica mediante la creación de una firma digital única para esa parte. En este proceso también se usan funciones hash.
Valores hash criptográficos Asigna datos de cualquier longitud a una secuencia de bytes de longitud fija. Los valores hash son únicos estadísticamente; el valor hash de una secuencia de dos bytes distinta no será el mismo.

Para el caso del algoritmo de nuestra Función Cifrado.NET, se utilizo la modalidad Cifrado de clave secreta (criptografía simétrica).

Los algoritmos de cifrado de clave secreta utilizan una clave secreta única para cifrar y descifrar datos. Debe asegurarse de que agentes no autorizados no obtengan acceso a la clave puesto que podrían utilizarla para descifrar los datos.

Normalmente, los algoritmos de clave secreta, denominados de cifrado de bloques, se utilizan para cifrar un bloque de datos a la vez. Los cifrados de bloques (como RC2, DES, TripleDES y Rijndael) transforman criptográficamente un bloque de entrada de n bytes en un bloque de salida de bytes cifrados. Si desea cifrar o descifrar una secuencia de bytes, debe hacerlo bloque a bloque. Como el tamaño de n es pequeño (n = 8 bytes para RC2, DES y TripleDES; n = 16, el valor predeterminado; n = 24; o n = 32 bytes para Rijndael), los valores mayores que n deben cifrarse en un bloque cada vez.

Las clases de cifrado de bloques que ofrece la biblioteca de clases base utilizan un modo de encadenamiento denominado CBC (Cipher Block Chaining, encadenamiento de bloques de cifrado), que utiliza una clave y un IV (Initialization Vector, vector de inicialización) para realizar transformaciones criptográficas en los datos.

.NET Framework proporciona las siguientes clases que implementan algoritmos de cifrado de clave secreta:

  • DESCryptoServiceProvider
  • RC2CryptoServiceProvider
  • RijndaelManaged
  • TripleDESCryptoServiceProvider

Objetivo General :

Para que el algoritmo funcione correctamente dentro de la Plataforma .NET; se necesita de lo siguiente: El uso de una Cadena de Caracteres de entrada de Datos, Key (Clave secreta) y IV (Initialization Vector), Cuya longitud debe variar de acuerdo con el servicio de cifrado que haya seleccionado dentro la función; por que de lo contrario se producirá una excepción.

Tabla de Longitudes de los Parametros

Clase
Soporte de Longitud Key (Clave Secreta)
Ejemplo

DES

8 Byte

  • Key = «Password»
  • IV = «Password»

Triple Des

16 o 24 Byte

  • Key = «password12345678»
  • IV = «password12345678»

Rijndael

16 Byte

  • Key = «1234567891234567»
  • IV = «1234567891234567»

RC2

16 Byte

  • Key = «1234567891234567»
  • IV = «1234567891234567»

Especificación del Algoritmo:

Para efecto de nuestro algoritmo; se debe utilizar las siguientes clase del Framework:

  • System.Security.Cryptography (El espacio de nombres System.Security.Cryptography proporciona servicios de cifrado, entre los que se incluyen la codificación y descodificación segura de los datos y otras muchas operaciones como, por ejemplo, la técnica de dispersión o hashing, la generación de números aleatorios y la autenticación de mensajes).
  • System.Text (El espacio de nombres System.Text contiene clases que representan codificaciones de caracteres ASCII, Unicode, UTF-7, y UTF-8; clases base abstractas para la conversión de bloques de caracteres en bloques de bytes y viceversa; y una clase auxiliar que manipula y da formato a objetos String sin necesidad de crear instancias intermedias de String).
  • System.IO (El espacio de nombres System.IO contiene tipos que permiten leer y escribir en los archivos y secuencias de datos, así como tipos que proporcionan compatibilidad básica con los archivos y directorios. )
Clase
Descripción
System.Security.Cryptography
  • ICryptoTransform: Define las operaciones básicas de las transformaciones criptográficas.
  • CryptoStream: Especifica el modo de una secuencia criptográfica
System.Text
  • Encoding:Representa una codificación de carácter.
System.IO
  • MemoryStream:Crea una secuencia cuyo almacén de respaldo es la memoria.

Demostración del Código:

Breve configuración de la función Cifrado:

  • Modo:
    • 1 es Encriptar;
    • 2 es Desencriptar
  • Algoritmo:
    • 1 es DES
    • 2 es TripleDES
    • 3 es RC2
    • 4 es Rijndael
  • Cadena: Es la Cadena de Caracter a Cifrar.
  • Key: Clave Secreta
  • VecI: Vector de Inicialización

Imports System.Security.Cryptography

Imports System.Text

imports System.IO

Module Module1

Public Function Cifrado(ByVal modo As Byte, ByVal Algoritmo As Byte, ByVal cadena As String, ByVal key As String, ByVal VecI As String) As String

Dim plaintext() As Byte

If modo = 1 Then

plaintext = Encoding.ASCII.GetBytes(cadena)

ElseIf modo = 2 Then

plaintext = Convert.FromBase64String(cadena)

End If

Dim keys() As Byte = Encoding.ASCII.GetBytes(key)

Dim memdata As New MemoryStream

Dim transforma As ICryptoTransform

Select Case Algoritmo

Case 1

Dim des As New DESCryptoServiceProvider ‘ DES

des.Mode = CipherMode.CBC

If modo = 1 Then

transforma = des.CreateEncryptor(keys, Encoding.ASCII.GetBytes(VecI))

ElseIf modo = 2 Then

transforma = des.CreateDecryptor(keys, Encoding.ASCII.GetBytes(VecI))

End If

Case 2

Dim des3 As New TripleDESCryptoServiceProvider ‘TripleDES

des3.Mode = CipherMode.CBC

If modo = 1 Then

transforma = des3.CreateEncryptor(keys, Encoding.ASCII.GetBytes(VecI))

ElseIf modo = 2 Then

transforma = des3.CreateDecryptor(keys, Encoding.ASCII.GetBytes(VecI))

End If

Case 3

Dim rc2 As New RC2CryptoServiceProvider ‘RC2

rc2.Mode = CipherMode.CBC

If modo = 1 Then

transforma = rc2.CreateEncryptor(keys, Encoding.ASCII.GetBytes(VecI))

ElseIf modo = 2 Then

transforma = rc2.CreateDecryptor(keys, Encoding.ASCII.GetBytes(VecI))

End If

Case 4

Dim rj As New RijndaelManaged ‘Rijndael

rj.Mode = CipherMode.CBC

If modo = 1 Then

transforma = rj.CreateEncryptor(keys, Encoding.ASCII.GetBytes(VecI))

ElseIf modo = 2 Then

transforma = rj.CreateDecryptor(keys, Encoding.ASCII.GetBytes(VecI))

End If

End Select

Dim encstream As New CryptoStream(memdata, transforma, CryptoStreamMode.Write)

encstream.Write(plaintext, 0, plaintext.Length)

encstream.FlushFinalBlock()

encstream.Close()

If modo = 1 Then

cadena = Convert.ToBase64String(memdata.ToArray)

ElseIf modo = 2 Then

cadena = Encoding.ASCII.GetString(memdata.ToArray)

End If

Return cadena ‘Aquí Devuelve los Datos Cifrados

End Function

End Module


screenshotCDCS

Cadena1 = Cifrado(1,1,»Usuario Demo»,»eeaataxz»,»eeaataxz») –> Aquí se Encripta

Cadena2 = Cifrado(2,1,Cadena1,»eeaataxz»,eeaataxz») –> Aquí se Desencripta

Cadena1 = «mR6A9ViW8f5BHtJgNEK78w==»

Cadena2 = «Usuario Demo»


Código Fuente:

Conclusión: Con esta función se pretende obtener una mayor ventaja de las herramientas que proporciona el .NET framework, de una forma rápida y segura para aquellos que desean cifrar información con tan solo invocar una instrucción previamente configurada.

Fuentes:

19 comentarios »

  1. Gracias por el código, me ha sido de gran utilidad. Un saludo

    Comentarios por Carlos — May 26, 2011 @ 11:50 am | Responder

  2. Hola!
    Me sirvió de mucho el código. Gracias por el aporte!!!

    Comentarios para tener en cuenta: la key de 3DES (password123345678), tiene 1 byte de más y tira error.
    Descargué el código y en el desecriptar (modo = 2): usa Convert.FromBase64String(CADENA) y debería tomar el dato encriptado. Quedaría: Convert.FromBase64String(form1.textbox4.text)

    Saludos

    Comentarios por Betsabé — junio 13, 2011 @ 11:56 am | Responder

    • Hola Betsabé.

      Gracias por tan excelente observación, Ya actualice el articulo con la longitud correcta para 3DES.
      Y si efectivamente para el modo = 2, debe pasar el dato ya cifrado por el algoritmo especifico, sino té presentara una excepción dentro del proceso!

      Nuevamente Gracias! 😉
      Saludos.

      Comentarios por jorgepedraza — junio 13, 2011 @ 12:45 pm | Responder

  3. en algoritmo RijndaelManaged Marca este error:
    Case 4
    transforma = rj.CreateEncryptor(keys, Encoding.UTF8.GetBytes(VecI))
    {«Specified initialization vector (IV) does not match the block size for this algorithm.»}

    Me puedes ayudar a corregirlo plis, desde muchas gracias

    Comentarios por Jose Horacio Gallardo — agosto 3, 2011 @ 2:54 pm | Responder

    • Hola Jose

      Gracias por el comentario.
      Ahora con respecto al error que esta presentado; se debe a que no está introduciendo correctamente: longitud(es) o valor(es) de entrada(s) de la función para el caso del algoritmo Rijndael.

      Te expongo un ejemplo: Encriptar :
      Entrada de datos: Jorge
      Clave : 1234567891234567
      Vector: 1234567891234567
      Resultado: 0qB3XxP2+Af2y1iUzQZjGw==

      Ahora Desencriptar:
      Entrada de datos: 0qB3XxP2+Af2y1iUzQZjGw==
      Clave : 1234567891234567
      Vector: 1234567891234567
      Resultado: Jorge

      Clave y Vector = 1234567891234567 (16 Byte)
      Recuerda que la función es sensible a errores cuando introduce un valor que no coincide con el algoritmo. En estos casos deberías blindar la función con captura de Excepción Try .. Catch.

      Espero que esto sea de ayuda!

      Saludos.

      Comentarios por jorgepedraza — agosto 3, 2011 @ 6:07 pm | Responder

  4. Jorge, muchisimas gracias por la explicación y el código es super sencillo entenderlo. Lo necesito porque debo encriptar un password que va a ser guardado en un XML. Saludos

    Comentarios por Ezequiel Sierra — May 8, 2012 @ 10:54 am | Responder

  5. Muy buen artículo, he usado variaciones de las librerías con tu código más otros algoritmos tanto para cifrar como descifrar y me ha sido de mucha ayuda. Gracias.

    Comentarios por 1quark1 — septiembre 4, 2012 @ 7:09 am | Responder

  6. Cordial saludo. Inicio en el mundo de la seguridad para almacenar claves de usuario, y después de muchas búsquedas y documentación en la Web, encontré su código, lo probé y excelente en todo (aplicando obviamente las pequeñas correcciones aquí descritas). Como principiante en este mundo, me decidí a usar solo AES debido a que en toda parte dicen que es el mejor, más rápido y fiable. A una pequeña aplicación en Visual Basic que tengo para el almacenamiento de claves, le incorporé su código dejando únicamente lo concerniente al algoritmo AES, empece a probarlo y todo perfecto. Cuando hago un análisis más profundo (pues no se trata de copiar y pegar, sino de entender), me dí cuenta que se utiliza ASCII para la transformación criptográfica y los resultados se ven afectados al descifrarlos, pues las tildes y las ñ, son reemplazadas por caracteres no concordantes. Volví a investigar y encontre que se pueden usar páginas de codificación como UNICODE, UTF8 y UTF16 (la usada por el .Net Framework), me decide por la UTF8 que es como más estándar y cambie en su código ASCII por UTF8 y empece a probar y me perdí definitivamente. Ejecuto el programa y me salen errores con la clave y el vector de inicialización, me dice que no son correctas las longitudes. De lo poco que entiendo es que la longitud de las mismas debe ser de 16 caracteres. Este es el código adaptado a mi necesidad:

    Dim pwd As String = «Hg7$x#m5K3Ñ>c@v4»
    Dim VI As String = «Hg7$x#m5K3Ñ>c@v4»

    Para encriptar:
    TextBox2.Text = Cifrado(1, TextBox1.Text, pwd, VI)

    Para desencriptar:
    TextBox3.Text = Cifrado(2, TextBox2.Text, pwd, VI)

    Public Function Cifrado(ByVal modo As Byte, ByVal cadena As String, ByVal key As String, ByVal VecI As String) As String
    Dim plaintext() As Byte

    If modo = 1 Then
    plaintext = Encoding.UTF8.GetBytes(cadena)
    ElseIf modo = 2 Then
    plaintext = Convert.FromBase64String(cadena)
    End If

    Dim keys() As Byte = Encoding.UTF8.GetBytes(key)
    Dim memdata As New MemoryStream
    Dim transforma As ICryptoTransform

    Dim rj As New RijndaelManaged
    rj.Mode = CipherMode.CBC
    If modo = 1 Then
    transforma = rj.CreateEncryptor(keys, Encoding.UTF8.GetBytes(VecI))
    ElseIf modo = 2 Then
    transforma = rj.CreateDecryptor(keys, Encoding.UTF8.GetBytes(VecI))
    End If

    Dim encstream As New CryptoStream(memdata, transforma, CryptoStreamMode.Write)
    encstream.Write(plaintext, 0, plaintext.Length)

    encstream.FlushFinalBlock()
    encstream.Close()
    If modo = 1 Then
    cadena = Convert.ToBase64String(memdata.ToArray)
    ElseIf modo = 2 Then
    cadena = Encoding.UTF8.GetString(memdata.ToArray)
    End If

    Return cadena
    End Function

    Podrías por favor ayudarme a entender donde esta el error, o sugerirme dónde encontrar la documentación un poco más profunda. Mil gracias.

    Comentarios por Fabio Vanegas — septiembre 16, 2012 @ 10:31 am | Responder

    • Hola.
      Gracias por tan interesante observación y escenario que planteas!
      La respuesta es: usa System.Text.Encoding.Default para la codificación.

      De toda manera aquí adjunto código fuente actualizado en Framework 3.5 Cifrado-NET2

      Saludos.

      Comentarios por JorgePedraza — septiembre 17, 2012 @ 2:58 am | Responder

      • Mil gracias, aplique tu sugerencia para corregir mi error y todo perfecto. Excelente forma de enseñar y explicar. Un abrazo.

        Comentarios por Fabio Vanegas — septiembre 17, 2012 @ 9:01 am

      • Es un placer ayudar 😉

        Saludos.

        Comentarios por JorgePedraza — septiembre 17, 2012 @ 3:02 pm

  7. Hola, quisiera saber si este codigo esta en C# es que tengo problemas con el cogido cuando lo intento hacer en C# podria publicarte el codigo pero no se si puedas ayudarme, es urgente ojala puedas ayudarme.

    Comentarios por radx182 — diciembre 14, 2012 @ 2:52 pm | Responder

    • Hola.
      Gracias por el comentario.
      Ya esta listo 🙂

      Saludos.

      Comentarios por JorgePedraza — diciembre 15, 2012 @ 8:28 am | Responder

      • Entiendo, pero me preguntaba si me podrías ayudar con eso, el detalle es que tengo un código que recogí de internet pero no lo hago funcionar, no se como declarar las instancias correspondientes no son muy hábil y quisiera que por favor me ayudes. Este es el código donde tengo que declarar las instancias y mas abajo el código completo:

        private SymmetricAlgorithm InitializeSymmetricAlgorithm()
        {
        //TODO: Add code to create instance of a SymmetricAlgorithm
        //based on the user selection

        }
        ***************************************************************************************************************************

        using System;
        using System.Drawing;
        using System.Collections;
        using System.ComponentModel;
        using System.Windows.Forms;
        using System.Data;
        using System.Text;
        using System.IO;
        using System.Security.Cryptography;

        namespace SymmetricCiphers
        {
        ///
        /// Summary description for Form1.
        ///
        public class MainForm : System.Windows.Forms.Form
        {
        internal System.Windows.Forms.Label Label3;
        internal System.Windows.Forms.Label Label2;
        internal System.Windows.Forms.Label Label1;
        internal System.Windows.Forms.TextBox textRecoveredPlaintext;
        internal System.Windows.Forms.TextBox textPlaintext;
        internal System.Windows.Forms.TextBox textCiphertext;
        internal System.Windows.Forms.TextBox textBoxKey;
        internal System.Windows.Forms.TextBox textBoxIV;
        internal System.Windows.Forms.Button buttonEncrypt;
        internal System.Windows.Forms.Button buttonDecrypt;
        internal System.Windows.Forms.Button buttonGenKey;
        internal System.Windows.Forms.Button buttonGenIV;
        internal System.Windows.Forms.GroupBox GroupBox1;
        internal System.Windows.Forms.RadioButton radioRjindael;
        internal System.Windows.Forms.RadioButton radioRC2;
        internal System.Windows.Forms.RadioButton radio3DES;
        internal System.Windows.Forms.RadioButton radioDES;
        ///
        /// Required designer variable.
        ///
        private System.ComponentModel.Container components = null;
        Byte[] Key;
        Byte[] IV;
        Byte[] cipherbytes;

        public MainForm()
        {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        //
        //
        }

        ///
        /// Clean up any resources being used.
        ///
        protected override void Dispose( bool disposing )
        {
        if( disposing )
        {
        if (components != null)
        {
        components.Dispose();
        }
        }
        base.Dispose( disposing );
        }

        ///
        /// The main entry point for the application.
        ///
        [STAThread]
        static void Main()
        {
        Application.Run(new MainForm());
        }

        private void MainForm_Load(object sender, System.EventArgs e)
        {
        GenIV();
        GenKey();
        }

        private void GenKey()
        {
        SymmetricAlgorithm sa = InitializeSymmetricAlgorithm();
        sa.GenerateKey();
        Key = sa.Key;

        UpdateKeyTextBox();
        ClearOutputFields();
        }

        private void GenIV()
        {
        SymmetricAlgorithm sa = InitializeSymmetricAlgorithm();
        sa.GenerateIV();
        IV = sa.IV;

        UpdateIVTextBox();
        ClearOutputFields();
        }

        private void UpdateIVTextBox()
        {
        StringBuilder sb= new StringBuilder();
        int i;
        for (i = 0; i <= IV.Length – 1;i++)
        {
        sb.Append(String.Format("{0:X2} ", IV[i]));
        }
        textBoxIV.Text = sb.ToString();
        }

        private void UpdateKeyTextBox()
        {
        StringBuilder sb= new StringBuilder();
        int i;
        for (i = 0; i <= Key.Length – 1;i++)
        {
        sb.Append(String.Format("{0:X2} ", Key[i]));
        }
        textBoxKey.Text = sb.ToString();
        }

        private void ClearOutputFields()
        {
        textCiphertext.Text = "";
        textRecoveredPlaintext.Text = "";
        }

        private void buttonEncrypt_Click(object sender, System.EventArgs e)
        {
        ClearOutputFields();

        //TODO: Add code to create appropriate symmetric algorithm
        SymmetricAlgorithm sa = TripleDES.Create();

        //use current key and iv
        sa.Key = Key;
        sa.IV = IV;

        //TODO: Add code to create memory and crypto stream
        MemoryStream ms = new MemoryStream(cipherbytes);
        CryptoStream cs = new CryptoStream(
        ms,
        sa.CreateDecryptor(),
        CryptoStreamMode.Read);

        //TODO: Add code to write plaintext bytes to crypto stream
        Byte[] plainbytes = new Byte[cipherbytes.Length];
        cs.Read(plainbytes, 0, cipherbytes.Length);
        cs.Close();
        ms.Close();

        buttonEncrypt.Enabled = false;
        buttonDecrypt.Enabled = true;
        buttonGenKey.Enabled = false;
        buttonGenIV.Enabled = false;
        }

        private SymmetricAlgorithm InitializeSymmetricAlgorithm()
        {
        //TODO: Add code to create instance of a SymmetricAlgorithm
        //based on the user selection

        }

        private void buttonDecrypt_Click(object sender, System.EventArgs e)
        {
        //TODO: Add code to create appropriate symmetric algorithm
        SymmetricAlgorithm sa = TripleDES.Create();

        //use current key and iv
        sa.Key = Key;
        sa.IV = IV;

        //TODO: Add code to create memory and crypto stream
        MemoryStream ms = new MemoryStream(cipherbytes);
        CryptoStream cs = new CryptoStream(
        ms,
        sa.CreateDecryptor(),
        CryptoStreamMode.Read);

        //TODO: Add code to read plaintext bytes from crypto stream
        Byte[] plainbytes = new Byte[cipherbytes.Length];
        cs.Read(plainbytes, 0, cipherbytes.Length);
        cs.Close();
        ms.Close();

        buttonDecrypt.Enabled = false;
        buttonEncrypt.Enabled = true;
        buttonGenKey.Enabled = true;
        buttonGenIV.Enabled = true;

        }

        private void buttonGenKey_Click(object sender, System.EventArgs e)
        {
        GenKey();
        }

        private void buttonGenIV_Click(object sender, System.EventArgs e)
        {
        GenIV();
        }

        private void radioDES_CheckedChanged(object sender, System.EventArgs e)
        {
        if (radioDES.Checked == true)
        {
        SymmetricAlgorithm sa = DES.Create();
        Key = sa.Key;
        IV = sa.IV;

        UpdateKeyTextBox();
        UpdateIVTextBox();
        ClearOutputFields();
        }
        }

        private void radio3DES_CheckedChanged(object sender, System.EventArgs e)
        {
        if (radio3DES.Checked == true)
        {
        SymmetricAlgorithm sa = TripleDES.Create();
        Key = sa.Key;
        IV = sa.IV;

        UpdateKeyTextBox();
        UpdateIVTextBox();
        ClearOutputFields();
        }

        }

        private void radioRC2_CheckedChanged(object sender, System.EventArgs e)
        {
        if (radioRC2.Checked == true)
        {
        SymmetricAlgorithm sa = RC2.Create();
        Key = sa.Key;
        IV = sa.IV;

        UpdateKeyTextBox();
        UpdateIVTextBox();
        ClearOutputFields();
        }

        }

        private void radioRjindael_CheckedChanged(object sender, System.EventArgs e)
        {
        if (radioRjindael.Checked == true)
        {
        SymmetricAlgorithm sa = Rijndael.Create();
        Key = sa.Key;
        IV = sa.IV;

        UpdateKeyTextBox();
        UpdateIVTextBox();
        ClearOutputFields();
        }

        }

        }
        }

        Comentarios por radx182 — diciembre 15, 2012 @ 10:49 am

  8. Excelente trabajo. Funciona perfecto !!!! Muchas gracias

    Comentarios por Ariel — febrero 11, 2013 @ 11:57 pm | Responder


RSS feed for comments on this post. TrackBack URI

Replica a Jose Horacio Gallardo Cancelar la respuesta

Crea un blog o un sitio web gratuitos con WordPress.com.