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
- Introducción
- Objetivo General
- Especificación del Algoritmo
- Demostración del Código
- Código Fuente
- Conclusión
- Fuentes
- 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
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
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 |
|
Triple Des |
16 o 24 Byte |
|
Rijndael |
16 Byte |
|
RC2 |
16 Byte |
|
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 |
|
System.Text |
|
System.IO |
|
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
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»
- Cifrado-NET
- Cifrado-NET2 (Framework 3.5 y soporte de AES)
- Cifrado-NET2_CSharp (Framework 4.0, C#, Visual Studio 2012)
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.
Gracias por el código, me ha sido de gran utilidad. Un saludo
Comentarios por Carlos — May 26, 2011 @ 11:50 am |
Hola Carlos.
Gracias por el comentario 😉
Saludos.
Comentarios por jorgepedraza — May 26, 2011 @ 12:36 pm |
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 |
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 |
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 |
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 |
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 |
Hola, Gracias por el comentario!
Es un placer ayudar 😉 !
Saludos.
Comentarios por JorgePedraza — May 8, 2012 @ 11:42 am |
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 |
Hola.
Gracias por el comentario 🙂
Saludos.
Comentarios por JorgePedraza — septiembre 4, 2012 @ 11:23 am |
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 |
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 |
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
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 |
Hola.
Gracias por el comentario.
Ya esta listo 🙂
Saludos.
Comentarios por JorgePedraza — diciembre 15, 2012 @ 8:28 am |
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
Excelente trabajo. Funciona perfecto !!!! Muchas gracias
Comentarios por Ariel — febrero 11, 2013 @ 11:57 pm |
Hola.
Gracias por tu comentario!
Saludos.
Comentarios por JorgePedraza — febrero 12, 2013 @ 10:13 am |