domingo, 20 de mayo de 2007

Criptografía con GPG y GPGME I

GnuPG es una implementación libre del estándar OpenPGP y una conocida herramienta en criptografía que permite cifrar, descifrar, firmar, verificar firmas y administrar claves.

GnuPG Made Easy o GPGME es una librería que permite acceder a GnuPG de forma sencilla, proporcionando una API de alto nivel.

A continuación vamos a ver como cifrar un archivo. Para ello es necesario disponer de una clave, por lo que si no se dispone de una puede generarse con:
$ gpg --gen-key

Para ver una lista de las claves existentes podemos ejecutar:
$ gpg --list-keys

Y para cifrar un archivo con GPG:
$ gpg --encrypt -r id file
donde id es el identificador de la clave que deseamos usar y file es el fichero a cifrar.

Para hacer lo mismo mediante GPGME usaremos la siguiente función (NOTA: para usar GPGME es necesario incluir la cabecera "gpgme.h").


int gpg_encrypt(char *fingerprint, char *src_path, char *dst_path)
{
gpgme_ctx_t ctx;
gpgme_error_t err;
gpgme_data_t in, out;
gpgme_key_t key[2] = { NULL, NULL};
gpgme_encrypt_result_t result;
#define BUF_SIZE 512
char buf[BUF_SIZE + 1];
int ret;
FILE *f;

gpgme_check_version(NULL);
setlocale(LC_ALL, "");
gpgme_set_locale(NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
gpgme_set_locale(NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));

if( (err=gpgme_engine_check_version (GPGME_PROTOCOL_OpenPGP)) != 0)
{
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

if( (err=gpgme_new(&ctx)) != 0)
{
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

gpgme_set_armor(ctx, 1);

if( (err=gpgme_data_new_from_file (&in, src_path, 1)) != 0)
{
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

if( (err=gpgme_data_new(&out)) != 0)
{
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

if( (err=gpgme_get_key(ctx, fingerprint, &key[0], 0)) != 0)
{
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

if( (err=gpgme_op_encrypt(ctx, key, GPGME_ENCRYPT_ALWAYS_TRUST,
in,out)) !=0)
{
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

result = gpgme_op_encrypt_result(ctx);
if (result->invalid_recipients)
{
fprintf(stderr,"Invalid recipient: %s\n",result->invalid_recipients->fpr);
return EXIT_FAILURE;
}

if( (f=fopen(dst_path, "w+")) == NULL)
{
perror("fopen()");
return EXIT_FAILURE;
}

if( (ret=gpgme_data_seek (out, 0, SEEK_SET)) != 0)
{
err = gpgme_err_code_from_errno (errno);
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

while( (ret = gpgme_data_read (out, buf, BUF_SIZE)) > 0)
fwrite (buf, ret, 1, f);

if(ret<0)
{
err = gpgme_err_code_from_errno (errno);
fprintf(stderr, "%s: %s\n", gpgme_strsource(err), gpgme_strerror (err));
return EXIT_FAILURE;
}

gpgme_key_unref (key[0]);
gpgme_key_unref (key[1]);
gpgme_data_release (in);
gpgme_data_release (out);
gpgme_release (ctx);

return EXIT_SUCCESS;
}

Llamando a esta función con los parámetros adecuados cifraremos un fichero tal y como lo haríamos desde la shell con el comando gpg. Un ejemplo de llamada a la función sería el siguiente:

if(gpg_encrypt("F1234567", "secreto", "secreto.gpg") != 0)
{
... // error
}


Para compilar un programa que usa la librería GPGME disponemos de gpgme-config, que nos permite obtener los flags necesarios y las librerías. Un ejemplo de compilación es el siguiente:

$ gcc `gpgme-config --cflags --libs` -D_FILE_OFFSET_BITS=64  test.c


Finalmente, para descifrar un archivo con GPG podemos ejecutar el siguiente comando:
$ gpg --decrypt  file

GPG nos solicitará la contraseña y descifrará el archivo.

Pueden encontrarse más ejemplos de como usar esta librería en las fuentes de: GPGME.

No hay comentarios: