domingo 24 de mayo de 2009

Pkcs7 en C con OpenSSL.

En esta entrada muestro código de ejemplo para poder firmar un mensaje según la especificación Pkcs 7. Para hacerlo aprovecho el api que brinda el proyecto OpenSSL. Por razones obvias el lenguaje de programación es C.

Una vez generado el binario ejecutable del siguiente código, tendremos una aplicación de terminal, que recibe tres parámetros:
- ruta del archivo del certificado X509. Dicho archivo puede estar en formato PEM o DER.
- ruta del archivo de la clave privada. También el formato puede ser PEM o DER.
- mensaje para firmar.
Para finalizar se imprime por stdout el resultado.

Para compilar:
jaa@dino-thunder_ng ~/taller $ gcc pkcs7_sign.c -o pkcs7_sign -lssl


Evidentemente deben estar instalados OpenSSL y sus headers de desarrollo para poder compilar.

A continuación el ejemplo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/x509.h>

extern FILE *stderr;

char * pkcs7_sign
    (char * certfile,
    char * keyfile,
    char * message,
    char * cert_passwd,
    char * key_passwd);

int main (int argc, char ** argv)
{
    char * pk7sm= NULL;
    if (argc != 4)
    {
        printf ("MODO DE USO: %s cert_file key_file, message\\n",argv[0]);
        exit (1);
    }
    pk7sm=pkcs7_sign (argv[1],argv[2],argv[3],NULL,NULL);
    if (pk7sm != NULL)
        printf ("%s",pk7sm);
    else
    {
        fprintf (stderr, "Error inesperado.\\n");
        exit (1);
    }
    exit (0);
}

X509 * load_file (char * fname)
{
    FILE * fp   = NULL;
    X509 * cert = NULL;

    fp = fopen ((const char *)fname, "r");
    if (fp == NULL)
    {
        perror ("fopen()");
        return NULL;
    }
    cert = d2i_X509_fp(fp, NULL);
    if (cert != NULL)
        goto success;
    if (cert == NULL)
    {
        rewind (fp);
        cert = (X509 *)PEM_read_X509 (fp, NULL, NULL, NULL);
    }
    success:
        fclose (fp);
        return cert;
}

EVP_PKEY * load_key_file (char * fname)
{
    FILE * fp   = NULL;
    EVP_PKEY * pkey = NULL;

    fp = fopen ((const char *)fname, "r");
    if (fp == NULL)
    {
        perror ("fopen()");
        return NULL;
    }
    pkey = d2i_PrivateKey_fp (fp, NULL);
    if (pkey != NULL)
        goto success;
    if (pkey == NULL)
    {
        rewind (fp);
        pkey = (EVP_PKEY *) PEM_read_PrivateKey (fp, NULL, NULL, NULL);
    }
    success:
        fclose (fp);
        return pkey;
}

char * pkcs7_sign
    (char * certfile,
    char * keyfile,
    char * message,
    char * cert_passwd,
    char * key_passwd)
{
    PKCS7       * scms          = NULL;
    X509        * cert          = NULL;
    EVP_PKEY    * pkey          = NULL;
    BIO         * message_bio   = NULL;
    BIO         * signed_message= NULL;

    char * bio_data_ptr         = NULL;
    char * smessage             = NULL;
    long   bio_data_len         = 0;

    cert = load_file (certfile);
    if (cert == NULL)
    {
        fprintf (stderr,"Formato de certificado no valido: DER o PEM\\n");
        goto finally;
    }
    pkey = load_key_file (keyfile);
    if (pkey == NULL)
    {
        fprintf (stderr,"Formato de clave no valido: DER o PEM\\n");
        goto finally;
    }
    message_bio = BIO_new_mem_buf (message, strlen(message));
    if (message_bio == NULL)
    {
        fprintf (stderr,"Error creando buffer.\\n");
        goto finally;
    }
    SSL_library_init();
    scms = PKCS7_sign (cert, pkey, NULL, message_bio, PKCS7_DETACHED);
    if (scms == NULL)
    {
        long err= ERR_get_error ();
        char * err_txt = (char *)ERR_error_string (err);
        fprintf (stderr,"Error firmando pkcs7 (%i): %s\\n",\\
                (int)err,((err_txt==NULL)?" ":err_txt));
        goto finally;
    }

    signed_message = BIO_new (BIO_s_mem());
    if (signed_message == NULL)
    {
        fprintf (stderr,"Error creando buffer.\\n");
        goto finally;
    }
    if (PEM_write_bio_PKCS7 (signed_message, scms) <= 0)
    {
        fprintf (stderr,"Error transportando a PEM.\\n");
        goto finally;
    }
    bio_data_len =BIO_get_mem_data (signed_message, &bio_data_ptr);
    smessage = (char *)malloc (bio_data_len + 1);
    if (smessage == NULL)
    {
        perror ("malloc ()");
        goto finally;
    }
    memcpy (smessage, bio_data_ptr, bio_data_len);
    *(smessage+bio_data_len)=\'\\0\';
    finally:
        if (message_bio != NULL)
            BIO_free(message_bio);
        if (signed_message != NULL)
            BIO_free(signed_message);
        if (cert != NULL)
            X509_free(cert);
        if (pkey != NULL)
            EVP_PKEY_free(pkey);
        return smessage;
}

Etiquetas: , , , ,

0 comentarios:

Publicar un comentario en la entrada

Suscribirse a Enviar comentarios [Atom]

<< Página principal