Créer un certificat auto-signé avec une date de fin dans le passé

24

Je voudrais créer des certificats auto-signés à la volée avec des dates de début et de fin arbitraires, y compris des dates de fin dans le passé . Je préférerais utiliser des outils standard, par exemple OpenSSL, mais tout ce qui fait le travail serait formidable.

La question de débordement de pile Comment générer un certificat openssl avec une expiration inférieure à un jour? pose une question similaire, mais je veux que mon certificat soit auto-signé.

Au cas où vous vous poseriez la question, les certificats sont nécessaires pour les tests automatisés.

rlandster
la source

Réponses:

32

Vous avez deux façons de créer des certificats dans le passé. Soit truquer l'heure (1) (2), soit définir l'intervalle de temps lors de la signature du certificat (3).

1) Tout d'abord, sur le fait de simuler le temps: pour faire croire à un programme qu'il est dans une date différente du système, regardez libfaketimeetfaketime

Pour l'installer dans Debian:

sudo apt-get install faketime

Vous utiliseriez alors faketimeavant la opensslcommande.

Pour des exemples d'utilisation:

$faketime 'last friday 5 pm' /bin/date
Fri Apr 14 17:00:00 WEST 2017
$faketime '2008-12-24 08:15:42' /bin/date
Wed Dec 24 08:15:42 WET 2008

De man faketime:

La commande donnée sera amenée à croire que l'heure système actuelle est celle spécifiée dans l'horodatage. L'horloge murale continuera de fonctionner à partir de cette date et heure, sauf indication contraire (voir les options avancées). En fait, faketime est un simple wrapper pour libfaketime, qui utilise le mécanisme LD_PRELOAD pour charger une petite bibliothèque qui intercepte les appels système à des fonctions telles que time (2) et fstat (2).

Ainsi par exemple, dans votre cas, vous pouvez très bien définir une date de 2008, et créer ensuite un certificat d'une validité de 2 ans jusqu'en 2010.

faketime '2008-12-24 08:15:42' openssl ... 

En guise de remarque, cet utilitaire peut être utilisé dans plusieurs versions Unix, y compris MacOS, comme un wrapper pour tout type de programmes (non exclusif à la ligne de commande).

À titre de clarification, seuls les binaires chargés avec cette méthode (et leurs enfants) ont leur heure modifiée, et la fausse heure n'affecte pas l'heure actuelle du reste du système.

2) Comme l'indique @Wyzard, vous avez également le datefudgepackage qui est très similaire à l'utilisation faketime.

En tant que différences, datefudgen'influence pas fstat(c'est-à-dire ne change pas la création de l'heure du fichier). Il possède également sa propre bibliothèque, datefudge.so, qu'il charge à l'aide de LD_PRELOAD.

Il a également un -s static timeoù le temps référencé est toujours renvoyé malgré le nombre de secondes supplémentaires qui se sont écoulées.

$ datefudge --static "2007-04-01 10:23" sh -c "sleep 3; date -R"
Sun, 01 Apr 2007 10:23:00 +0100

3) En plus de simuler le temps, et encore plus simplement, vous pouvez également définir le point de départ et le point de fin de validité du certificat lors de la signature du certificat dans OpenSSL.

L'idée fausse de la question à laquelle vous liez dans votre question est que la validité du certificat n'est pas définie au moment de la demande (à la demande de RSE), mais lors de sa signature.

Lorsque vous utilisez openssl capour créer le certificat auto-signé, ajoutez les options -startdateet -enddate.

Le format de date dans ces deux options, selon opensl sources at openssl/crypto/x509/x509_vfy.c, est ASN1_TIME alias ASN1UTCTime: le format doit être YYMMDDHHMMSSZ ou YYYYMMDDHHMMSSZ.

Citant openssl/crypto/x509/x509_vfy.c:

int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time)
{
    static const size_t utctime_length = sizeof("YYMMDDHHMMSSZ") - 1;
    static const size_t generalizedtime_length = sizeof("YYYYMMDDHHMMSSZ") - 1;
    ASN1_TIME *asn1_cmp_time = NULL;
    int i, day, sec, ret = 0;

    /*
     * Note that ASN.1 allows much more slack in the time format than RFC5280.
     * In RFC5280, the representation is fixed:
     * UTCTime: YYMMDDHHMMSSZ
     * GeneralizedTime: YYYYMMDDHHMMSSZ
     *
     * We do NOT currently enforce the following RFC 5280 requirement:
     * "CAs conforming to this profile MUST always encode certificate
     *  validity dates through the year 2049 as UTCTime; certificate validity
     *  dates in 2050 or later MUST be encoded as GeneralizedTime."
     */

Et à partir du journal CHANGE (bug 2038?) - Ce journal des modifications est juste une note de bas de page supplémentaire, car il ne concerne que ceux qui utilisent directement l'API.

Changements entre 1.1.0e et 1.1.1 [xx XXX xxxx]

*) Ajoutez les types ASN.1 INT32, UINT32, INT64, UINT64 et les variantes préfixées par Z. Celles-ci sont destinées à remplacer LONG et ZLONG et à être de taille sûre. L'utilisation de LONG et ZLONG est déconseillée et programmée pour dépréciation dans OpenSSL 1.2.0.

Ainsi, la création d'un certificat du 1er janvier 2008 au 1er janvier 2010, peut se faire comme:

openssl ca -config /path/to/myca.conf -in req.csr -out ourdomain.pem \
-startdate 200801010000Z -enddate 201001010000Z

ou

openssl ca -config /path/to/myca.conf -in req.csr -out ourdomain.pem \
-startdate 0801010000Z -enddate 1001010000Z

-startdateet -enddateapparaissent dans les opensslsources et le journal des modifications; comme l'a noté @guntbert, bien qu'ils n'apparaissent pas dans la man opensslpage principale , ils apparaissent également dans man ca:

-startdate date
       this allows the start date to be explicitly set. The format of the date is
       YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure).

   -enddate date
       this allows the expiry date to be explicitly set. The format of the date is
       YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure).

Citant openssl/CHANGE:

Changements entre 0.9.3a et 0.9.4 [09 août 1999]

*) Correction des arguments -startdate et -enddate (qui manquaient) au programme 'ca'.

PS Quant à la réponse choisie à la question à laquelle vous faites référence de StackExchange: c'est généralement une mauvaise idée de changer l'heure du système, en particulier dans les systèmes de production; et avec les méthodes de cette réponse, vous n'avez pas besoin des privilèges root lorsque vous les utilisez.

Rui F Ribeiro
la source
1
+1. Je savais que quelqu'un viendrait avec quelque chose de mieux que ce que j'ai écrit :)
Celada
2
Il existe également un programme similaire appelé datefudge.
Wyzard
@Wyzard Merci, en effet je l'ai trouvé dans Debian; Fait intéressant, le manuel indique que même s'il modifie également les appels système à des fonctions telles que time (2), il n'influence pas fstat (2).
Rui F Ribeiro
1
Les deux faketimeet le datefudgetravail à merveille sur mon système Debian jessie.
rlandster
1
OTOH: +5 pour savoir fixer ces dates!
guntbert
8

Je suis presque surpris de constater que l'évidence fonctionne: alors que opensslprend comme argument le nombre de jours pour lesquels le certificat doit être valide, il suffit de fournir un nombre négatif!

openssl req -x509 -newkey rsa:4096 \
    -keyout key.pem -out cert.pem -days -365

Notez que cela se traduit en fait par quelque chose de très étrange: un certificat dont l'horodatage d'expiration précède son horodatage de début de validité. Je ne vous recommande pas réellement de l'utiliser pour vos tests automatisés, car c'est bizarre. Vous voulez probablement un moyen de dater également l'horodatage de début de validité.

Celada
la source
Eh bien, pour être juste, je ne savais pas que vous pouviez utiliser des jours négatifs.
Rui F Ribeiro
Pouvez-vous pas spécifier la date de début?
FreeSoftwareServers
@FreeSoftwareServers Dans la CSR, vous ne pouvez pas; voir la dernière partie de ma réponse.
Rui F Ribeiro
Plus intéressant encore, le barf de code ne trouvera-t-il pas un tel certificat? btw, j'ai développé ma réponse
Rui F Ribeiro
3

Ou vous pouvez utiliser quelque chose comme ce court programme python ... (des mises en garde s'appliquent)

Il crée une clé (test.key) et un certificat (test.crt) avec un temps de création de 10 ans dans le passé (-10 * 365 * 24 * 60 * 60 secondes est de -10 ans) et un délai d'expiration de 5 ans dans le passé (-5 * 365 * 24 * 60 * 60).

Veuillez noter qu'il s'agit d'un programme de démonstration minimal, il ne prend donc pas la peine de définir des extensions (par exemple, BasicConstraints) et utilise une série fixe.

#!/usr/bin/env python

from OpenSSL import crypto

key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, 2048)
cert = crypto.X509()
cert.get_subject().CN = "Test"
cert.set_serial_number(666)
cert.gmtime_adj_notBefore(-10*365*24*60*60)
cert.gmtime_adj_notAfter(-5*365*24*60*60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(key)
cert.sign(key, 'sha384')

open("test.crt", "wb").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
open("test.key", "wb").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))
Edheldil
la source
le code semble manquer les champs standard X.509 essentiels.
Rui F Ribeiro
2
C'est très utile. Cela me donne un contrôle programmatique plus facile sur la création du certificat.
rlandster