Je me rends compte que cette réponse est très tardive (et longue). Mais compte tenu de l'importance de cette question dans les résultats des moteurs de recherche, je me suis dit qu'elle méritait une réponse décente.
Une grande partie de ce que vous lirez ci-dessous est empruntée à cette démonstration et la documentation d'OpenSSL. Le code ci-dessous s'applique à la fois au C et au C++.
Avant de pouvoir créer un certificat, nous devons créer une clé privée. OpenSSL fournit la clé EVP_PKEY
structure permettant de stocker en mémoire une clé privée indépendante de l'algorithme. Cette structure est déclarée dans openssl/evp.h
mais elle est incluse par openssl/x509.h
(dont nous aurons besoin plus tard), il n'est donc pas nécessaire d'inclure explicitement l'en-tête.
Afin d'attribuer un EVP_PKEY
nous utilisons EVP_PKEY_new
:
EVP_PKEY * pkey;
pkey = EVP_PKEY_new();
Il existe également une fonction correspondante pour libérer la structure - EVP_PKEY_free
- qui accepte un seul argument : le EVP_PKEY
initialisée ci-dessus.
Nous devons maintenant générer une clé. Pour notre exemple, nous allons générer une clé RSA. Cela se fait à l'aide de la commande RSA_generate_key
qui est déclarée dans openssl/rsa.h
. Cette fonction renvoie un pointeur sur un RSA
structure.
Une invocation simple de la fonction peut ressembler à ceci :
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 la valeur de retour de RSA_generate_key
es NULL
mais quelque chose n'a pas fonctionné. Si ce n'est pas le cas, nous avons maintenant une clé RSA, et nous pouvons l'assigner à notre EVP_PKEY
de la structure précédente :
EVP_PKEY_assign_RSA(pkey, rsa);
En RSA
sera automatiquement libérée lorsque la structure EVP_PKEY
est libérée.
Passons maintenant au certificat proprement dit.
OpenSSL utilise l'option X509
pour représenter un certificat x509 en mémoire. La définition de cette structure se trouve dans openssl/x509.h
. La première fonction dont nous aurons besoin est X509_new
. Son utilisation est relativement simple :
X509 * x509;
x509 = X509_new();
Comme ce fut le cas pour EVP_PKEY
il existe une fonction correspondante pour libérer la structure - X509_free
.
Nous devons maintenant définir quelques propriétés du certificat à l'aide d'un certain nombre d'éléments X509_*
fonctions :
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
Le numéro de série de notre certificat est ainsi fixé à "1". Certains serveurs HTTP à code source ouvert refusent d'accepter un certificat dont le numéro de série est "0", ce qui est la valeur par défaut. L'étape suivante consiste à spécifier la durée de validité du certificat. Pour ce faire, nous faisons appel aux deux fonctions suivantes :
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
La première ligne définit la valeur notBefore
à l'heure actuelle. (Le X509_gmtime_adj
ajoute le nombre de secondes spécifié à l'heure actuelle - dans ce cas, aucune). La deuxième ligne définit la valeur notAfter
à 365 jours à partir de maintenant (60 secondes * 60 minutes * 24 heures * 365 jours).
Nous devons maintenant définir la clé publique de notre certificat en utilisant la clé que nous avons générée précédemment :
X509_set_pubkey(x509, pkey);
Comme il s'agit d'un certificat auto-signé, le nom de l'émetteur correspond au nom du sujet. La première étape de ce processus consiste à obtenir le nom du sujet :
X509_NAME * name;
name = X509_get_subject_name(x509);
Si vous avez déjà créé un certificat auto-signé en ligne de commande, vous vous souvenez probablement qu'on vous a demandé un code de pays. C'est ici que nous le fournissons, ainsi que l'organisation ("O") et le nom commun ("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);
(J'utilise ici la valeur "CA" parce que je suis Canadien et que c'est le code de notre pays. Notez également que le paramètre #4 doit être explicitement transformé en un objet de type unsigned char *
.)
Nous pouvons maintenant définir le nom de l'émetteur :
X509_set_issuer_name(x509, name);
Enfin, nous sommes prêts à procéder à la signature. Nous appelons X509_sign
avec la clé que nous avons générée précédemment. Le code pour cela est d'une simplicité déconcertante :
X509_sign(x509, pkey, EVP_sha1());
Notez que nous utilisons l'option SHA-1 algorithme de hachage pour signer la clé. Cela diffère de l'algorithme mkcert.c
que j'ai mentionnée au début de cette réponse, qui utilise MD5.
Nous avons maintenant un certificat auto-signé ! Mais nous n'avons pas encore terminé - nous devons écrire ces fichiers sur le disque. Heureusement, OpenSSL nous couvre là aussi avec la fonction PEM_*
qui sont déclarées dans openssl/pem.h
. Le premier dont nous aurons besoin est PEM_write_PrivateKey
pour enregistrer notre clé privée.
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 vous ne souhaitez pas crypter la clé privée, passez simplement NULL
pour les troisième et quatrième paramètres ci-dessus. Dans tous les cas, vous devrez vous assurer que le fichier n'est pas lisible par le monde entier. (Pour les utilisateurs d'Unix, cela signifie chmod 600 key.pem
.)
La vie en noir et blanc Il ne nous reste plus qu'une seule fonction : écrire le certificat sur le disque. La fonction dont nous avons besoin pour cela est 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 */
);
Et c'est fini ! Nous espérons que les informations contenues dans cette réponse sont suffisantes pour vous donner une idée approximative de la façon dont tout fonctionne, bien que nous ayons à peine effleuré la surface d'OpenSSL.
Pour ceux qui souhaitent voir à quoi ressemble le code ci-dessus dans une application réelle, j'ai créé une Gist (écrite en C++) que vous pouvez consulter. aquí .