Instalar el módulo para DNIe en un servidor Apache

Estos días estoy realizando el curso de INTECO de desarrollo de aplicaciones del DNIe (os recomiendo que si estáis en paro os paséis por la sección de formación de su web, hay múltiples cursos gratis, que quieras que no ayudan a engordar el CV y a ampliar conocimientos) y, como ejercicio práctico, me he puesto a instalar el módulo para autenticación con DNIe en un servidor Apache.

La idea es instalar el módulo mod_ssl para que apache trabaje con certificados digitales para la autenticación cliente/servidor.

La idea es descargar el módulo, guardarlo en la carpeta /libexec de Apache y ejecutar estos comandos:

cd usr/local/modssl
./configure\
-- with-apache=../apache\
-- with-ssl=../openssl\
-- enable-shared=ssl\

make
make install

Se recompila y reinstala Apache con la opción –enable-module=modssl y ya está. A partir de ahora, para arrancar Apache con ssl se ejecutará el comando

/usr/local/apache/bin/apachectl startssl

Tras esto toca configurar el Apache. Debéis buscar el fichero http.conf, donde estarán todas las directivas de MOD_SSL para su gestión.

La configuración de SSL estará entre una etiqueta ifDefine SSL, dentro de la cual debéis comprobar si está habilitado el protocolo SSL (SSLEngine on). Comprobad por allí que está definido el certificado de servidor y su clave privada (SSLCertificateFile /usr/local/apache/conf/ssl.crt/certificatSERV.crt para el certificado y SSLCertificateKeyFile /usr/local/apache/conf/ssl.key/clausSERV.key ). Luego hay que indicar el directorio donde se guarda la CA (autoridad de certificación) del DNIe, directiva que se utiliza para comprobar si se confía en el emisor del certificado cliente. Busca SSLCACertificateFile acraiz-dnie.cer y SSLCACertificatePath /usr/local/apache/conf/ssl.crt/, el primero es el nombre del certificado y el segundo la dirección donde debe estar guardado. Para cargar el resto de Autoridades de Certificación subordinadas en las cuales se confiará, el procedimiento es el siguiente:

  • Ubicar el certificado en el directorio antes indicado en formato PEM
  • Crear un nombre simbólico de la forma “hash.N” para cada certificado. Se puede hacer con el comando Makefile que proporciona el mismo mod_ssl

Para acabar comprueba que la autenticación del cliente está habilitada (SSLVerifyClient require) y la profundidad de verificación, que para el caso del DNIe se encuentra en las CA subordinadas y la CA Raiz (SSLVerifyDepth 2).

Con todo esto ya tienes el módulo instalado para usar una conexión segura SSL en tu Apache, usando el DNIe para la verificación.

Encriptar una columna en MS-SQL Server con clave simétrica

Siguiendo con la racha de entradas sobre SQL, vamos a ver cómo encriptar una columna de una tabla SQL-Server usando una clave simétrica.

Bueno, lo primero, tras haber seleccionado la base de datos que queremos usar, es crear una clave maestra si no existe ya, con un password seguro (largo, con mayúsculas, minúsculas, números y símbolos), luego crear un certificado y finalmente una clave simétrica eligiendo el algoritmo a usar (en este caso AES256, robusto y seguro):

IF NOT EXISTS 
    (SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
    CREATE MASTER KEY ENCRYPTION BY 
    PASSWORD = 'PoNGo@1#pa55w0rd!fuEr73@para½q@no$me()lo_tAngUen'
GO

CREATE CERTIFICATE NombreDelCertificado
   WITH SUBJECT = 'Certificado Para Ejemplo';
GO

CREATE SYMMETRIC KEY SSN_Clave_Simétrica_01
    WITH ALGORITHM = AES_256
    ENCRYPTION BY CERTIFICATE NombreDelCertificado;
GO

Ok, con esto ya tenemos nuestra clave creada, con su contraseña. Un dato, si sois usuarios de Windows XP o de Windows Server 2000 no podéis usar AES, esos sistemas operativos no lo soportan, tenéis que usar DES (menos robusto y seguro, pero es lo que hay por usar sistemas sin actualizar). Ahora para introducir datos cifrados tienes que hacer lo siguiente:

-- Primero abre la clave simétrica.
OPEN SYMMETRIC KEY SSN_Clave_Simétrica_01
   DECRYPTION BY CERTIFICATE NombreDelCertificado;

--Luego introduce los datos
UPDATE BDEjemplos.Ejemplo
SET ColumnaDatosEncriptados = EncryptByKey(Key_GUID('SSN_Clave_Simétrica_01'), 'Una cadena cualquiera');
GO

En este caso usé un UPDATE pero podría haberlo hecho dentro de un INSERT. En todo caso, la función EncryptByKey() que recibe el identificador de la clave como primer parámetro y el dato a encriptar (String, Real, Float…) como segundo, es la clave de la operación.

Y para acceder a los datos cifados la cosa tampoco se torna muy compleja, es má o menos lo mismo, primero abrir y luego usar una función para desencriptar en la SELECT.En el ejemplo sacaríamos en una columna los datos encriptados y al lado el mismo dato desencriptado.

OPEN SYMMETRIC KEY SSN_Clave_Simétrica_01
   DECRYPTION BY CERTIFICATE NombreDelCertificado;
GO

SELECT ColumnaDatosEncriptados 
    AS 'Dato Encriptado',
    CONVERT(nvarchar, DecryptByKey(ColumnaDatosEncriptados)) 
    AS 'Dato Desencriptado'
    FROM BDEjemplos.Ejemplo;
GO

Existe la posibilidad de hacer esto también con un autenticador para fortalecer la seguridad. Si os interesa podéis mirarlo en la web de ayuda de Microsoft o presionarme para que publique aquí como hacerlo.

Tres herramientas libres para el cifrado de archivos

Como llevo unos días estudiando temas de seguridad (firma electrónica, VPN, algoritmos de encriptación, funciones de resumen…) es un buen momento para una corta entrada sobre herramientas para cifrar tus archivos. Voy a recomendar las que creo que son las tres mejores herramientas para encriptar tus archivos.

  • GPG o GNU Privacy Guard: Es la versión libre de PGP, y podéis descargarla desde este enlace. No sólo es ideal para el cifrado de archivos locales, e incluso de sistemas de discos enteros, sino también para el encriptado de comunicaciones. Permite el uso de pares de claves, firmas con tiempo limitado y esquemas de cifrado varios. Aunque en principio funciona en línea de comandos, existen varios plugins y entornos gráficos para el mismo, tanto en Linux como en Windows o MacOS.
  • True Crypt: Una de las más poderosas herramientas del mercado, se trata de una herramienta on-the-fly, que descrifa los archivos cuando los abres y los vuelve a cifrar cuando no están en uso. True Crypt es libre, gratis y está disponible para Linux, MacOS y Windows desde su web oficial. Está fuertemente optimizado para que la diferencia entre trabajar con un volumen cifrado y uno sin cifrar sea la menor posible. Permite cifrar tanto archivos como unidades enteras de disco, crear discos de seguridad virtuales y hasta crear volúmenes ocultos para mayor seguridad.
  • AxCrypt: En el caso de AxCrypt la utilidad es mayormente la de cifrar archivos sueltos o carpetas. Está disponible aquí para Windows y se integra en la shell del SO para que, con sólo hacer click derecho sobre un archivo, ya tengas la opción de Encriptar/Desencriptar. Permite además crear archivos autoextraíbles para transportarlos y transferirlos de forma segura, sin tener que tener instalado AxCrypt en el otro equipo.

Conste que si la idea es simplemente encriptar un archivo, el compresor 7zip te da la posibilidad de utilizar AES-256 para codificar el paquete ZIP, con lo que tus archivos estarán totalmente seguros.

En fin, con estas tres aplicaciones, no hay excusa para tener tus datos más confidenciales expuestos.

Usando SHA-1 en C y C++

Estaba trasteando en C estos días como ejercicio y se me ha ocurrido utilizar una función de resumen (hash) SHA-1. Como el tema es profundamente complejo, lo que he hecho ha sido buscar una ya creada, dado que al tratarde de un algoritmo creado en 1995 existen múltiples. Voy a compartir el que he encontrado por ahí que funciona bien:

/* sha1.c : Implementation of the Secure Hash Algorithm */

/* SHA: NIST's Secure Hash Algorithm */

/*	This version written November 2000 by David Ireland of 
	DI Management Services Pty Limited <code@di-mgt.com.au>

	Adapted from code in the Python Cryptography Toolkit, 
	version 1.0.0 by A.M. Kuchling 1995.
*/

/* AM Kuchling's posting:- 
   Based on SHA code originally posted to sci.crypt by Peter Gutmann
   in message <30ajo5$oe8@ccu2.auckland.ac.nz>.
   Modified to test for endianness on creation of SHA objects by AMK.
   Also, the original specification of SHA was found to have a weakness
   by NSA/NIST.  This code implements the fixed version of SHA.
*/

/* Here's the first paragraph of Peter Gutmann's posting:
   
The following is my SHA (FIPS 180) code updated to allow use of the "fixed"
SHA, thanks to Jim Gillogly and an anonymous contributor for the information on
what's changed in the new version.  The fix is a simple change which involves
adding a single rotate in the initial expansion function.  It is unknown
whether this is an optimal solution to the problem which was discovered in the
SHA or whether it's simply a bandaid which fixes the problem with a minimum of
effort (for example the reengineering of a great many Capstone chips).
*/

/* h files included here to make this just one file ... */

/* global.h */

#ifndef _GLOBAL_H_
#define _GLOBAL_H_ 1

/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER;

/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;

/* BYTE defines a unsigned character */
typedef unsigned char BYTE;

#ifndef TRUE
  #define FALSE	0
  #define TRUE	( !FALSE )
#endif /* TRUE */

#endif /* end _GLOBAL_H_ */

/* sha.h */

#ifndef _SHA_H_
#define _SHA_H_ 1

/* #include "global.h" */

/* The structure for storing SHS info */

typedef struct 
{
	UINT4 digest[ 5 ];            /* Message digest */
	UINT4 countLo, countHi;       /* 64-bit bit count */
	UINT4 data[ 16 ];             /* SHS data buffer */
	int Endianness;
} SHA_CTX;

/* Message digest functions */

void SHAInit(SHA_CTX *);
void SHAUpdate(SHA_CTX *, BYTE *buffer, int count);
void SHAFinal(BYTE *output, SHA_CTX *);

#endif /* end _SHA_H_ */

/* endian.h */

#ifndef _ENDIAN_H_
#define _ENDIAN_H_ 1

void endianTest(int *endianness);

#endif /* end _ENDIAN_H_ */

/* sha.c */

#include <stdio.h>
#include <string.h>

static void SHAtoByte(BYTE *output, UINT4 *input, unsigned int len);

/* The SHS block size and message digest sizes, in bytes */

#define SHS_DATASIZE    64
#define SHS_DIGESTSIZE  20


/* The SHS f()-functions.  The f1 and f3 functions can be optimized to
   save one boolean operation each - thanks to Rich Schroeppel,
   rcs@cs.arizona.edu for discovering this */

/*#define f1(x,y,z) ( ( x & y ) | ( ~x & z ) )          // Rounds  0-19 */
#define f1(x,y,z)   ( z ^ ( x & ( y ^ z ) ) )           /* Rounds  0-19 */
#define f2(x,y,z)   ( x ^ y ^ z )                       /* Rounds 20-39 */
/*#define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) )   // Rounds 40-59 */
#define f3(x,y,z)   ( ( x & y ) | ( z & ( x | y ) ) )   /* Rounds 40-59 */
#define f4(x,y,z)   ( x ^ y ^ z )                       /* Rounds 60-79 */

/* The SHS Mysterious Constants */

#define K1  0x5A827999L                                 /* Rounds  0-19 */
#define K2  0x6ED9EBA1L                                 /* Rounds 20-39 */
#define K3  0x8F1BBCDCL                                 /* Rounds 40-59 */
#define K4  0xCA62C1D6L                                 /* Rounds 60-79 */

/* SHS initial values */

#define h0init  0x67452301L
#define h1init  0xEFCDAB89L
#define h2init  0x98BADCFEL
#define h3init  0x10325476L
#define h4init  0xC3D2E1F0L

/* Note that it may be necessary to add parentheses to these macros if they
   are to be called with expressions as arguments */
/* 32-bit rotate left - kludged with shifts */

#define ROTL(n,X)  ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )

/* The initial expanding function.  The hash function is defined over an
   80-UINT2 expanded input array W, where the first 16 are copies of the input
   data, and the remaining 64 are defined by

        W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]

   This implementation generates these values on the fly in a circular
   buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
   optimization.

   The updated SHS changes the expanding function by adding a rotate of 1
   bit.  Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
   for this information */

#define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
                                                 W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )


/* The prototype SHS sub-round.  The fundamental sub-round is:

        a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
        b' = a;
        c' = ROTL( 30, b );
        d' = c;
        e' = d;

   but this is implemented by unrolling the loop 5 times and renaming the
   variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
   This code is then replicated 20 times for each of the 4 functions, using
   the next 20 values from the W[] array each time */

#define subRound(a, b, c, d, e, f, k, data) \
    ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )

/* Initialize the SHS values */

void SHAInit(SHA_CTX *shsInfo)
{
    endianTest(&shsInfo->Endianness);
    /* Set the h-vars to their initial values */
    shsInfo->digest[ 0 ] = h0init;
    shsInfo->digest[ 1 ] = h1init;
    shsInfo->digest[ 2 ] = h2init;
    shsInfo->digest[ 3 ] = h3init;
    shsInfo->digest[ 4 ] = h4init;

    /* Initialise bit count */
    shsInfo->countLo = shsInfo->countHi = 0;
}


/* Perform the SHS transformation.  Note that this code, like MD5, seems to
   break some optimizing compilers due to the complexity of the expressions
   and the size of the basic block.  It may be necessary to split it into
   sections, e.g. based on the four subrounds

   Note that this corrupts the shsInfo->data area */

static void SHSTransform( digest, data )
     UINT4 *digest, *data ;
    {
    UINT4 A, B, C, D, E;     /* Local vars */
    UINT4 eData[ 16 ];       /* Expanded data */

    /* Set up first buffer and local data buffer */
    A = digest[ 0 ];
    B = digest[ 1 ];
    C = digest[ 2 ];
    D = digest[ 3 ];
    E = digest[ 4 ];
    memcpy( (POINTER)eData, (POINTER)data, SHS_DATASIZE );

    /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
    subRound( A, B, C, D, E, f1, K1, eData[  0 ] );
    subRound( E, A, B, C, D, f1, K1, eData[  1 ] );
    subRound( D, E, A, B, C, f1, K1, eData[  2 ] );
    subRound( C, D, E, A, B, f1, K1, eData[  3 ] );
    subRound( B, C, D, E, A, f1, K1, eData[  4 ] );
    subRound( A, B, C, D, E, f1, K1, eData[  5 ] );
    subRound( E, A, B, C, D, f1, K1, eData[  6 ] );
    subRound( D, E, A, B, C, f1, K1, eData[  7 ] );
    subRound( C, D, E, A, B, f1, K1, eData[  8 ] );
    subRound( B, C, D, E, A, f1, K1, eData[  9 ] );
    subRound( A, B, C, D, E, f1, K1, eData[ 10 ] );
    subRound( E, A, B, C, D, f1, K1, eData[ 11 ] );
    subRound( D, E, A, B, C, f1, K1, eData[ 12 ] );
    subRound( C, D, E, A, B, f1, K1, eData[ 13 ] );
    subRound( B, C, D, E, A, f1, K1, eData[ 14 ] );
    subRound( A, B, C, D, E, f1, K1, eData[ 15 ] );
    subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) );
    subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) );
    subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) );
    subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) );

    subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) );
    subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) );
    subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) );
    subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) );
    subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) );
    subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) );
    subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) );
    subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) );
    subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) );
    subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) );
    subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) );
    subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) );
    subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) );
    subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) );
    subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) );
    subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) );
    subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) );
    subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) );
    subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) );
    subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) );

    subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) );
    subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) );
    subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) );
    subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) );
    subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) );
    subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) );
    subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) );
    subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) );
    subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) );
    subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) );
    subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) );
    subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) );
    subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) );
    subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) );
    subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) );
    subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) );
    subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) );
    subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) );
    subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) );
    subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) );

    subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) );
    subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) );
    subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) );
    subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) );
    subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) );
    subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) );
    subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) );
    subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) );
    subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) );
    subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) );
    subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) );
    subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) );
    subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) );
    subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) );
    subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) );
    subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) );
    subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) );
    subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) );
    subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) );
    subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) );

    /* Build message digest */
    digest[ 0 ] += A;
    digest[ 1 ] += B;
    digest[ 2 ] += C;
    digest[ 3 ] += D;
    digest[ 4 ] += E;
    }

/* When run on a little-endian CPU we need to perform byte reversal on an
   array of long words. */

static void longReverse(UINT4 *buffer, int byteCount, int Endianness )
{
    UINT4 value;

    if (Endianness==TRUE) return;
    byteCount /= sizeof( UINT4 );
    while( byteCount-- )
        {
        value = *buffer;
        value = ( ( value & 0xFF00FF00L ) >> 8  ) | \
                ( ( value & 0x00FF00FFL ) << 8 );
        *buffer++ = ( value << 16 ) | ( value >> 16 );
        }
}

/* Update SHS for a block of data */

void SHAUpdate(SHA_CTX *shsInfo, BYTE *buffer, int count)
{
    UINT4 tmp;
    int dataCount;

    /* Update bitcount */
    tmp = shsInfo->countLo;
    if ( ( shsInfo->countLo = tmp + ( ( UINT4 ) count << 3 ) ) < tmp )
        shsInfo->countHi++;             /* Carry from low to high */
    shsInfo->countHi += count >> 29;

    /* Get count of bytes already in data */
    dataCount = ( int ) ( tmp >> 3 ) & 0x3F;

    /* Handle any leading odd-sized chunks */
    if( dataCount )
        {
        BYTE *p = ( BYTE * ) shsInfo->data + dataCount;

        dataCount = SHS_DATASIZE - dataCount;
        if( count < dataCount )
            {
            memcpy( p, buffer, count );
            return;
            }
        memcpy( p, buffer, dataCount );
        longReverse( shsInfo->data, SHS_DATASIZE, shsInfo->Endianness);
        SHSTransform( shsInfo->digest, shsInfo->data );
        buffer += dataCount;
        count -= dataCount;
        }

    /* Process data in SHS_DATASIZE chunks */
    while( count >= SHS_DATASIZE )
        {
        memcpy( (POINTER)shsInfo->data, (POINTER)buffer, SHS_DATASIZE );
        longReverse( shsInfo->data, SHS_DATASIZE, shsInfo->Endianness );
        SHSTransform( shsInfo->digest, shsInfo->data );
        buffer += SHS_DATASIZE;
        count -= SHS_DATASIZE;
        }

    /* Handle any remaining bytes of data. */
    memcpy( (POINTER)shsInfo->data, (POINTER)buffer, count );
    }

/* Final wrapup - pad to SHS_DATASIZE-byte boundary with the bit pattern
   1 0* (64-bit count of bits processed, MSB-first) */

void SHAFinal(BYTE *output, SHA_CTX *shsInfo)
{
    int count;
    BYTE *dataPtr;

    /* Compute number of bytes mod 64 */
    count = ( int ) shsInfo->countLo;
    count = ( count >> 3 ) & 0x3F;

    /* Set the first char of padding to 0x80.  This is safe since there is
       always at least one byte free */
    dataPtr = ( BYTE * ) shsInfo->data + count;
    *dataPtr++ = 0x80;

    /* Bytes of padding needed to make 64 bytes */
    count = SHS_DATASIZE - 1 - count;

    /* Pad out to 56 mod 64 */
    if( count < 8 )
        {
        /* Two lots of padding:  Pad the first block to 64 bytes */
        memset( dataPtr, 0, count );
        longReverse( shsInfo->data, SHS_DATASIZE, shsInfo->Endianness );
        SHSTransform( shsInfo->digest, shsInfo->data );

        /* Now fill the next block with 56 bytes */
        memset( (POINTER)shsInfo->data, 0, SHS_DATASIZE - 8 );
        }
    else
        /* Pad block to 56 bytes */
        memset( dataPtr, 0, count - 8 );

    /* Append length in bits and transform */
    shsInfo->data[ 14 ] = shsInfo->countHi;
    shsInfo->data[ 15 ] = shsInfo->countLo;

    longReverse( shsInfo->data, SHS_DATASIZE - 8, shsInfo->Endianness );
    SHSTransform( shsInfo->digest, shsInfo->data );

	/* Output to an array of bytes */
	SHAtoByte(output, shsInfo->digest, SHS_DIGESTSIZE);

	/* Zeroise sensitive stuff */
	memset((POINTER)shsInfo, 0, sizeof(shsInfo));
}

static void SHAtoByte(BYTE *output, UINT4 *input, unsigned int len)
{	/* Output SHA digest in byte array */
	unsigned int i, j;

	for(i = 0, j = 0; j < len; i++, j += 4) 
	{
        output[j+3] = (BYTE)( input[i]        & 0xff);
        output[j+2] = (BYTE)((input[i] >> 8 ) & 0xff);
        output[j+1] = (BYTE)((input[i] >> 16) & 0xff);
        output[j  ] = (BYTE)((input[i] >> 24) & 0xff);
	}
}


unsigned char digest[20];
unsigned char message[3] = {'a', 'b', 'c' };
unsigned char *mess56 = 
	"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";

/* Correct solutions from FIPS PUB 180-1 */
char *dig1 = "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D";
char *dig2 = "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1";
char *dig3 = "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F";

/* Output should look like:-
 a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
 A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D <= correct
 84983e44 1c3bd26e baae4aa1 f95129e5 e54670f1
 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 <= correct
 34aa973c d4c4daa4 f61eeb2b dbad2731 6534016f
 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F <= correct
*/

main()
{
	SHA_CTX sha;
	int i;
	BYTE big[1000];

	SHAInit(&sha);
	SHAUpdate(&sha, message, 3);
	SHAFinal(digest, &sha);

	for (i = 0; i < 20; i++)
	{
		if ((i % 4) == 0) printf(" ");
		printf("%02x", digest[i]);
	}
	printf("\n");
	printf(" %s <= correct\n", dig1);

	SHAInit(&sha);
	SHAUpdate(&sha, mess56, 56);
	SHAFinal(digest, &sha);

	for (i = 0; i < 20; i++)
	{
		if ((i % 4) == 0) printf(" ");
		printf("%02x", digest[i]);
	}
	printf("\n");
	printf(" %s <= correct\n", dig2);

	/* Fill up big array */
	for (i = 0; i < 1000; i++)
		big[i] = 'a';

	SHAInit(&sha);
	/* Digest 1 million x 'a' */
	for (i = 0; i < 1000; i++)
		SHAUpdate(&sha, big, 1000);
	SHAFinal(digest, &sha);

	for (i = 0; i < 20; i++)
	{
		if ((i % 4) == 0) printf(" ");
		printf("%02x", digest[i]);
	}
	printf("\n");
	printf(" %s <= correct\n", dig3);

	return 0;
}

/* endian.c */

void endianTest(int *endian_ness)
{
	if((*(unsigned short *) ("#S") >> 8) == '#')
	{
		/* printf("Big endian = no change\n"); */
		*endian_ness = !(0);
	}
	else
	{
		/* printf("Little endian = swap\n"); */
		*endian_ness = 0;
	}
}

He dejado todos los comentarios, aunque se os pueda hacer coñazo, por respeto a los creadores originales. La función original la podéis consultar aquí.

Para C++ por otra parte, podéis disfrutar de la librería libre Crypto++ con múltiples algoritmos, que podéis encontrar en este enlace.

Algoritmos criptográficos más frecuentes

Un algoritmo criptográfico no es más (bueno, lo digo como si esto fuera poco… xD) que una función matemática usada en los procesos de encriptación y desencriptación. El algoritmo se sirve de una clave para encriptar y desencriptar datos.

Tal vez lo principal sea empezar por la diferencia entre sistemas de clave simétrica y asimétrica.  En el caso de la criptografía simétrica la explicación es simple: se usa la misma clave para encriptar y desencriptar, es decir, tanto el emisor como el receptor han de conocer la misma clave. La cuestión de la criptografía asimétrica es más compleja, utilizando dos claves: pública y privada, la privada sólo la tiene el receptor, y la pública todos los emisores. El emisor encripta el mensaje usando la clave pública, pero este sólo puede ser desencriptado con la clave privada (que sólo tiene el receptor y que nadie más ha de saber para que la seguridad sea eficiente).

Ahora, simplemente, incluyo una lista con los más utilizados.

  • DES:  Consistente en una serie de permutaciones y sustituciones, utiliza una clave simétrica de 64 bits, de los cuales 56 se usan para la encriptación y 8 para la paridad. Fue diseñado por IBM y declarado standar en 1977 por la NBS, y en esos tiempos el algoritmo estuvo rodeado de gran polémica: se consideraba que la longitud de clave era insuficiente y existían fuertes rumores de que la NSA había impuesto que este tuviera una puerta trasera. En 1992 se publicó el primer ataque teórico para romperlo, en 1998 una máquina diseñada específicamente para romperlo lo logró en 56 horas, y en 1999 un ordenador normal fue capaz de romperlo en 22 horas. A partir de ese momento se considera DES como inseguro, pero una nueva especificación, TripleDES o 3DES, una evolución de DES con clave de 128 bits y triple cifrado, lo sustituye. Desde 2001 se adopta como estandar AES, mucho más robusto.
  • RC5: Es la evolución de RC4, que consistía en hacer un XOR al mensaje con un vector aleatorio que se desprende de la clave. RC5 en cambio usa otra operación, llamada dependencia de datos. Como peculiaridad tiene un tamaño variable de bloque (16,32, 64 0 128 bits), de clave (de 0 a 2024 bits) y de número de vueltas (de 0 a 255)
  • IDEA: Trabaja con bloques de texto de 64 bits, operando siempre con números de 16 bits y usando operaciones como XOR, y suma y multiplicación de enteros. El algoritmo de desencriptación es muy similar al de encriptación, lo que lo hace muy rápido y fácil de programar. Actualmente se considera que es invulnerable a ataques por fuerza bruta, e incluso frente al criptoanálisis diferencial resiste muy bien, teniendo sólo vulnerabilidades las claves más cortas, pero con una clave fuerte es muy resistente. Además, se trata de un algoritmo de libre difusión, lo que permite que cualquiera pueda utilizarlo.
  • Diffie-Hellman: El protocolo criptográfico de Diffie-Hellman, llamado así por sus creadores, fue el punto de partida de los sistemas asimétricos, basados en clave pública y clave privada. Su validez para el intercambio de claves asimétricas hace que esté implementado en los diferentes sistemas seguros más habituales en Internet, como SSL o VPN. El algoritmo se basa en potencias de números y la función mod (módulo discreto), haciendo uso de las potencias discretas, fáciles de calcular pero siendo muy difícil obtener la función inversa. Este protocolo sí es sensible a ataques man in the middle, en los que alguien pueda interceptar la comunicación.
  • RSA: Aunque fue creado en 1977, RSA sigue siendo el sistema de clave pública más conocido y usado. Se trata de un sistema muy rápido y que se utiliza mucho en los sistemas mixtos (lo explicaré más abajo). Su seguridad radica en la dificultad de factorizar números enteros grandes. Los mensajes enviados se representan mediante números, y el funcionamiento se basa en el producto conocido de dos números primos grandes elegidos al azar. El cálculo de estas claves se realiza en secreto en la máquina en la que se va a guardar la clave privada.

Bueno, como ya comenté arriba, existen sistemas mixtos. Los sistemas de clave asimétrica requieren más coste de proceso que los de clave simétrica, este hecho ha llevado a buscar una solución híbrida que combine la seguridad de la transmisión de claves asimétricas con la mayor ligereza en el procesamiento de los sistemas simétricos. El concepto es sencillo: el mensaje se encripta utilizando una clave simétrica, la clave simétrica a su vez se encripta utilizando una clave pública y se envía todo en el mismo paquete. De esta forma sólo se requiere usar la clave privada para desencriptar la clave simétrica, y una vez se obtiene esta basta con utilizarla para descifrar el resto del mensaje.