2008-11-02 48 views
57

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.Crear mediante programación el certificado X509 utilizando OpenSSL

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

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

Respuesta

36

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, necesitará invocar más de 20 funciones diferentes de la API de OpenSSL para crear una clave y un certificado autofirmado. Un ejemplo se encuentra en el código fuente de OpenSSL, en demos/x509/mkcert.c

Para obtener una respuesta más detallada, consulte Nathan Osman's explanation a continuación.

+0

Sí, necesito familiarizarme más con los conceptos de SSL. Voy a ver el ejemplo, gracias por el enlace (el enlace tiene un problema, pero lo resolveré.) También he usado Crypto ++ para algunas cosas, podría ser más fácil de usar que OpenSSL en este caso. –

+0

¡Gracias! Seleccionó esta respuesta debido al enlace provisto. –

0

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

  • de Licencias: Llamada de la openssl posiblemente ejecutable 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 la documentación de línea de comandos fenomenal que simplifica en gran medida una herramienta potencialmente complicado.

  • Capacidad de prueba: puede ejercer OpenSSL desde la línea de comandos hasta que comprenda exactamente cómo crear sus certs. Hay un lote de 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 decide utilizar la API, consulte la lista de los desarrolladores openssl-dev en www.openssl.org.

¡Buena suerte!

+0

OpenSSL no está bajo la GPL http://www.openssl.org/source/license.html – Zoredache

+5

OpenSSL es una licencia bajo una licencia de estilo apache, se puede usar en aplicaciones comerciales como cualquier otra licencia no copyleft .Las personas aún pueden querer consultar a un abogado para asegurarse de que todo lo que hacen esté bien, pero no tiene problemas relacionados con la GPL. –

+0

Observado y actualizado: gracias. La separación de código abierto de código cerrado es generalmente una buena idea, y a menos que la eficiencia sea de importancia crítica, las otras razones justifican el uso de la utilidad independiente openssl. –

139

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 es prestado de this demo 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.

A fin de asignar una estructura EVP_PKEY, utilizamos 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 solo argumento: la estructura EVP_PKEY inicializado anteriormente.

Ahora tenemos que 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.

Un simple invocación de la función podría tener este aspecto:

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 asignarlo a nuestra estructura EVP_PKEY de antes:

EVP_PKEY_assign_RSA(pkey, rsa); 

La estructura RSA será liberado automáticamente cuando la estructura EVP_PKEY se libera.


Ahora para el certificado en sí.

OpenSSL utiliza 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 de EVP_PKEY, hay una función correspondiente para liberar la estructura - X509_free.

Ahora tenemos que configurar algunas propiedades del certificado utilizando algunas funciones X509_*:

ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); 

Esto establece el número de serie de nuestro certificado a '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. Lo hacemos con los dos siguientes llamadas de función:

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

La primera línea establece notBefore la propiedad del certificado a 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 tenemos que establecer la clave pública de nuestro certificado utilizando la tecla generamos anteriormente:

X509_set_pubkey(x509, pkey); 

Dado que este es un certificado autofirmado, fijamos el nombre del emisor para el nombre de la tema. El primer paso en este proceso es obtener el nombre del asunto:

X509_NAME * name; 
name = X509_get_subject_name(x509); 

Si alguna vez has creado un certificado autofirmado en la línea de comandos antes, es probable que recuerde que se le solicita un código de país. Aquí es donde 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 eso es nuestro país . código también en cuenta que el parámetro # 4 debe ser emitidos de forma explícita a una unsigned char *)

Ahora podemos realmente establecer el nombre del emisor:.

X509_set_issuer_name(x509, name); 

Y finalmente estamos listos para llevar a cabo el proceso de firma. Llamamos al 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 usando el algoritmo de hash SHA-1 para firmar la clave. Esto difiere de la demostración mkcert.c que mencioné al comienzo 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 nos tiene cubiertos allí también con las funciones PEM_* que están declaradas 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 cifrar la clave privada, entonces simplemente pasan 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 usuarios de Unix, esto significa chmod 600 key.pem).

¡Vaya! 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 ya está! 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 puede ver here.

+0

¡Gracias por la excelente respuesta y explicación! Solo una pequeña duda: es esta frase 'Ahora necesitamos establecer la clave pública para nuestro certificado usando la clave que generamos antes:' ¿un error tipográfico? ¿No debería la 'clave pública' ser' clave privada'? –

+2

Tuve que agregar fclose (f) al final. De lo contrario, el archivo que se estaba escribiendo era 0B –

+0

@QamarSuleiman: Good catch. –

Cuestiones relacionadas