Calcular Valores Hash
Para cadenas y archivos.

Fecha: 09/Sep/2004 (04/Septiembre/2004)
Autor: Gonzalo Antonio Sosa M.  e-mail

 


INTRODUCCION

Los valores Hash son el resultado de aplicar funciones de un sólo sentido (denominadas funciones Hash) a un bloque de datos. Las funciones Hash transforman este bloque de datos en una cadena de longitud fija, por lo que pequeñas modificaciones en los datos alteran significativamente el resultado obtenido. De ahí que no sea posible que dos conjuntos de datos distintos generen el mismo resultado.

Los valores Hash son utilizados ampliamente en el envío de mensajes a través de las redes. Un ejemplo de esto lo tenemos en el uso que las redes edonkey y bittorrent hacen de estos valores. Son parte importante de sus protocolos de transferencia de archivos, al permitir verificar que la información que se envía es la misma de la que se recibe. Aunque trabajan de modos distintos; mientras que edonkey integra el Hash del archivo completo dentro del URL de descarga, bittorrent divide el archivo a enviar en fragmentos de longitud fija, calcula el hash para cada fragmento y lo anexa dentro de archivos de meta información que envía junto con dichos fragmentos.

La plataforma .NET nos proporciona un amplio soporte para funciones Hash. El espacio de nombres System.Cryptography, contiene las clases para manejar los algoritmos Hash soportados.

Los más usados actualmente son MD5 (Message Digest 5) y SHA (Secure Hash Algorithm). Aquí algunas precisiones de los algoritmos Hash disponibles en punto NET.

Algoritmo Descripción
MD5

El MD5 fue desarrollado en 1991 por Rivest. El mensaje se rellena para que su longitud en bits más 448 sea divisible por 512. El mensaje se procesa iterativamente en bloques de 512 bits y cada bloque es procesado en tres rotaciones distintas. Genera Una cadena de salida de 128 Bits de longitud.

SHA1

El SHA es un algoritmo de resumen seguro desarrollado por el NIST. SHA-1 es una versión corregida del algoritmo publicado en 1994. El algoritmo es un estándar ANSI. El algoritmo toma un mensaje de menos de 264 bits y genera un resumen de 160 bits. Es más lento que el MD5, pero la mayor longitud de clave lo hace más resistente a ataques de colisión por fuerza bruta y de inversión.

SHA256

La función SHA256 funciona en un bloque de mensajes de 512 bits y un valor Hash intermedio de 256 bits. Es esencialmente un algoritmo de cifrado de bloques de 256 bits que cifra el valor intermedio usando el bloque del mensaje como llave.

SHA512 La función SHA512 funciona en un bloque de mensajes de 1024 bits y un valor Hash intermedio de 512 Bits. Es esencialmente un algoritmo de cifrado de bloques de 512 bits que cifra el valor intermedio usando el bloque del mensaje como llave.
SHA384

Se define de la misma manera que el SHA512, con dos excepciones: el valor inicial y en que el final es truncado en menos de 384 bits.


CLASE CHECKSUM

La clase Checksum que vamos a escribir, nos ayudará a calcular los valores Hash de cadenas y archivos. Permitiremos la elección del algoritmo de cifrado mediante una enumeración.

A continuación el código completo y documentado de la clase.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace CalcularValoresHashCs
{
    /// <summary>
    /// Algoritmos permitidos
    /// </summary>
    public enum Algorithm
    {
        MD5,
        SHA1,
        SHA256,
        SHA384,
        SHA512
    }

    /// <summary>
    /// Permite calcular valores Hash de cadenas y archivos.
    /// </summary>
    public class Checksum 
    {
        /// <summary>
        /// Calcula el valor Hash para la cadena pasada como parámetro.
        /// </summary>
        /// <param name="cadena">Cadena a procesar.</param>
        /// <param name="alg">Algoritmo que vamos a utilizar.</param>
        /// <returns>Cadena representando el valor Hash Obtenido.</returns>
        public string CalculateStringHash( string cadena, Algorithm alg )
        {
	        // del algoritmo seleccionado obtenemos un proveedor de cifrado. 
        	HashAlgorithm hash = GetHashProvider( alg );
	        // obtenemos un array de bytes de los caracteres dentro de la cadena.
        	byte[] tempSource = Encoding.ASCII.GetBytes( cadena );

	        // el método ComputeHash calcula el valor Hash y devuelve un array de bytes.
        	// convertimos el array a string antes de devolverlo.
		return ArrayToString( hash.ComputeHash(tempSource) );
    	}	

        /// <summary>
        /// Calcula el valor Hash del archivo pasado como parámetro.
        /// </summary>
        /// <param name="filename">Nombre completo del archivo a procesar.</param>
        /// <param name="alg">Algoritmo que vamos a utilizar.</param>
        /// <returns>Cadena representando el valor Hash del archivo.</returns>
        public string CalculateFileHash( string filename, Algorithm alg )
        {
            // del algoritmo seleccionado obtenemos un proveedor de cifrado.
            HashAlgorithm hash = GetHashProvider( alg );
            // creamos un objeto stream con el archivo especificado.
            FileStream fs = new FileStream( filename, FileMode.Open, FileAccess.Read );

            // el método ComputeHash calcula el valor Hash del flujo 
            // que representa al archivo. convertimos el array a string y lo asignamos
            // a una variable
            string resul = ArrayToString( hash.ComputeHash(fs) );
            fs.Close(); // importante!! cerramos el flujo.

            return resul; // devolvemos el valor de la variable de cadena.
        }

        /// <summary>
        /// Convierte un Array de bytes en una cadena de caracteres.
        /// </summary>
        /// <param name = "byteArray">Byte array origen.</param>
        /// <returns>Cadena de caracteres obtenida del array.</returns>
        private string ArrayToString( byte[] byteArray )
        {
            // usamos un objeto stringbuilder por su mejor rendimiento con operaciones
            // de cadenas.
            StringBuilder sb = new StringBuilder( byteArray.Length );

            // por cada byte en el array
            for ( int i = 0; i < byteArray.Length; i++ )
            {
                // obtenemos su valor hexadecimal y lo agregamos al objeto
                // stringbuilder.
                sb.Append( byteArray[i].ToString("X2") );
            }

            // devolvemos el objeto stringbuilder, formateado a string
            return sb.ToString();
        }

        /// <summary>
        /// Obtiene un proveedor HashAlgorithm de la enumeración utilizada.
        /// </summary>
        /// <param name="alg">Algoritmo seleccionado.</param>
        /// <returns>Proveedor Hash correpondiente al algoritmo deseado.</returns>
        private HashAlgorithm GetHashProvider( Algorithm alg )
        {
            switch ( alg )
            {
                case Algorithm.MD5:
                    return new MD5CryptoServiceProvider();
                case Algorithm.SHA1:
                    return new SHA1Managed();
                case Algorithm.SHA256:
                    return new SHA256Managed(); 
                case Algorithm.SHA384:
                    return new SHA384Managed();
                case Algorithm.SHA512:
                    return new SHA512Managed();
                default: // sino se ha encontrado un valor correspondiente en la
                // enumeración, generamos un excepción para indicarlo.
                    throw new Exception( "Invalid Provider." );
            }
        }
    }
}

Esta clase nos ayuda a calcular los valores Hash de los datos que pasamos como parámetros de sus métodos y nos retorna una cadena hexadecimal correspondiente al resultado obtenido. Aunque su uso es bastante intuitivo, en el archivo con el código de ejemplo se incluye una que muestra de como implementarla.

REFERENCIAS

ir al índice

Fichero con el código de ejemplo:

Gonzalo_CalcularValoresHashCs - 10 KB
Gonzalo_CalcularValoresHashVB - 10 KB