Crear programáticamente el certificado X509 mediante OpenSSL

Tengo una aplicación C / C ++ y necesito crear un certificado XEM9 para pem que contenga tanto una clave pública como una privada. El certificado puede ser autofirmado, o sin firmar, no importa.

Quiero hacer esto dentro de una aplicación, no desde la línea de comando.

¿Qué funciones de OpenSSL harán esto por mí? ¡Cualquier código de muestra es una bonificación!

Primero tendrá que familiarizarse con la terminología y los mecanismos.

Un certificado X.509, por definición, no incluye una clave privada. En cambio, es una versión firmada por CA de la clave pública (junto con los atributos que la CA pone en la firma). El formato PEM solo admite el almacenamiento por separado de la clave y el certificado, aunque puede concatenar los dos.

En cualquier caso, deberá invocar más de 20 funciones diferentes de la API de OpenSSL para crear una clave y un certificado autofirmado. Un ejemplo es en la fuente OpenSSL misma, en demos / x509 / mkcert.c

Para obtener una respuesta más detallada, consulte la explicación de Nathan Osman a continuación.

Me doy cuenta de que esta es una respuesta muy tardía (y larga). Pero teniendo en cuenta lo bien que esta pregunta parece estar en los resultados de los motores de búsqueda, pensé que podría valer la pena escribir una respuesta decente para.

Mucho de lo que leerá a continuación se toma prestado de esta demostración y los documentos de OpenSSL. El código a continuación se aplica tanto a C como a C ++.


Antes de que podamos crear un certificado, necesitamos crear una clave privada. OpenSSL proporciona la estructura EVP_PKEY para almacenar una clave privada independiente del algoritmo en la memoria. Esta estructura se declara en openssl/evp.h pero está incluida en openssl/x509.h (que necesitaremos más adelante) por lo que no es necesario que incluya explícitamente el encabezado.

Para asignar una estructura EVP_PKEY , usamos EVP_PKEY_new :

 EVP_PKEY * pkey; pkey = EVP_PKEY_new(); 

También hay una función correspondiente para liberar la estructura – EVP_PKEY_free – que acepta un único argumento: la estructura EVP_PKEY inicializada anteriormente.

Ahora necesitamos generar una clave. Para nuestro ejemplo, generaremos una clave RSA. Esto se hace con la función RSA_generate_key que se declara en openssl/rsa.h Esta función devuelve un puntero a una estructura RSA .

Una invocación simple de la función podría verse así:

 RSA * rsa; rsa = RSA_generate_key( 2048, /* number of bits for the key - 2048 is a sensible value */ RSA_F4, /* exponent - RSA_F4 is defined as 0x10001L */ NULL, /* callback - can be NULL if we aren't displaying progress */ NULL /* callback argument - not needed in this case */ ); 

Si el valor de retorno de RSA_generate_key es NULL , entonces algo salió mal. Si no es así, ahora tenemos una clave RSA, y podemos asignarla a nuestra estructura EVP_PKEY desde el principio:

 EVP_PKEY_assign_RSA(pkey, rsa); 

La estructura RSA se liberará automáticamente cuando se EVP_PKEY estructura EVP_PKEY .


Ahora para el certificado en sí.

OpenSSL usa la estructura X509 para representar un certificado x509 en la memoria. La definición para esta estructura está en openssl/x509.h La primera función que vamos a necesitar es X509_new . Su uso es relativamente sencillo:

 X509 * x509; x509 = X509_new(); 

Como fue el caso con EVP_PKEY , hay una función correspondiente para liberar la estructura – X509_free .

Ahora necesitamos establecer algunas propiedades del certificado usando algunas funciones X509_* :

 ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); 

Esto establece el número de serie de nuestro certificado en ‘1’. Algunos servidores HTTP de código abierto se niegan a aceptar un certificado con un número de serie de ‘0’, que es el predeterminado. El siguiente paso es especificar el lapso de tiempo durante el cual el certificado es realmente válido. Hacemos eso con las siguientes dos llamadas a funciones:

 X509_gmtime_adj(X509_get_notBefore(x509), 0); X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); 

La primera línea establece la propiedad notBefore del certificado en la hora actual. (La función X509_gmtime_adj agrega el número especificado de segundos a la hora actual, en este caso ninguno.) La segunda línea establece la propiedad notAfter del certificado en 365 días a partir de ahora (60 segundos * 60 minutos * 24 horas * 365 días).

Ahora necesitamos establecer la clave pública para nuestro certificado usando la clave que generamos anteriormente:

 X509_set_pubkey(x509, pkey); 

Dado que este es un certificado autofirmado, establecemos el nombre del emisor al nombre del sujeto. El primer paso en ese proceso es obtener el nombre del sujeto:

 X509_NAME * name; name = X509_get_subject_name(x509); 

Si alguna vez ha creado un certificado autofirmado en la línea de comando, probablemente recuerde que se le solicitó un código de país. Aquí es donde lo proporcionamos junto con la organización (‘O’) y el nombre común (‘CN’):

 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0); X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"MyCompany Inc.", -1, -1, 0); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0); 

(Estoy usando el valor ‘CA’ aquí porque soy canadiense y ese es nuestro código de país. También tenga en cuenta que el parámetro # 4 debe convertirse explícitamente en un unsigned char * ).

Ahora podemos establecer el nombre del emisor:

 X509_set_issuer_name(x509, name); 

Y finalmente estamos listos para realizar el proceso de firma. Llamamos a X509_sign con la clave que generamos anteriormente. El código para esto es dolorosamente simple:

 X509_sign(x509, pkey, EVP_sha1()); 

Tenga en cuenta que estamos utilizando el algoritmo hash SHA-1 para firmar la clave. Esto difiere de la demo de mkcert.c que mencioné al principio de esta respuesta, que usa MD5.


¡Ahora tenemos un certificado autofirmado! Pero aún no hemos terminado, tenemos que escribir estos archivos en el disco. Afortunadamente, OpenSSL también nos cubre con las funciones PEM_* que se declaran en openssl/pem.h El primero que necesitaremos es PEM_write_PrivateKey para guardar nuestra clave privada.

 FILE * f; f = fopen("key.pem", "wb"); PEM_write_PrivateKey( f, /* write the key to the file we've opened */ pkey, /* our key from earlier */ EVP_des_ede3_cbc(), /* default cipher for encrypting the key on disk */ "replace_me", /* passphrase required for decrypting the key on disk */ 10, /* length of the passphrase string */ NULL, /* callback for requesting a password */ NULL /* data to pass to the callback */ ); 

Si no desea encriptar la clave privada, simplemente pase NULL para el tercer y cuarto parámetro anterior. De cualquier manera, definitivamente querrás asegurarte de que el archivo no sea legible por todo el mundo. (Para los usuarios de Unix, esto significa chmod 600 key.pem )

¡Uf! Ahora tenemos una función: necesitamos escribir el certificado en el disco. La función que necesitamos para esto es PEM_write_X509 :

 FILE * f; f = fopen("cert.pem", "wb"); PEM_write_X509( f, /* write the certificate to the file we've opened */ x509 /* our certificate */ ); 

¡Y terminamos! Esperamos que la información en esta respuesta sea suficiente para darle una idea aproximada de cómo funciona todo, aunque apenas hemos arañado la superficie de OpenSSL.

Para aquellos interesados ​​en ver cómo se ve todo el código anterior en una aplicación real, he reunido un Gist (escrito en C ++) que puedes ver aquí .

¿Alguna posibilidad de hacer esto a través de una llamada al system desde tu aplicación? Varias buenas razones para hacer esto:

  • Licencias: Llamar al ejecutable de openssl podría decirse que lo separa de su aplicación y puede proporcionar ciertas ventajas. Descargo de responsabilidad: consulte a un abogado sobre esto.

  • Documentación: OpenSSL viene con una fenomenal documentación de línea de comandos que simplifica enormemente una herramienta potencialmente complicada.

  • Capacidad de prueba: puede ejercer OpenSSL desde la línea de comandos hasta que comprenda exactamente cómo crear sus certs. Hay muchas opciones; espere gastar alrededor de un día en esto hasta que obtenga todos los detalles correctos. Después de eso, es trivial incorporar el comando en tu aplicación.

Si elige usar la API, consulte la lista de desarrolladores de openssl-dev en http://www.openssl.org.

¡Buena suerte!

Tutorial muy simple para crear certificados digitales http://publib.boulder.ibm.com/infocenter/rsthelp/v8r0m0/index.jsp?topic=/com.ibm.rational.test.lt.doc/topics/tcreatecertopenssl.html

Acerca de ejecutar estos comandos desde su código, no estoy seguro.