Создать самозаверяющий сертификат с конечной датой в прошлом

24

Я хотел бы создавать самозаверяющие сертификаты на лету с произвольными датами начала и окончания, включая даты окончания в прошлом . Я бы предпочел использовать стандартные инструменты, например, OpenSSL, но все, что выполняет работу, было бы замечательно.

Вопрос переполнения стека Как сгенерировать сертификат openssl со сроком действия менее одного дня? задает аналогичный вопрос, но я хочу, чтобы мой сертификат был самоподписанным.

Если вам интересно, сертификаты нужны для автоматизированного тестирования.

rlandster
источник

Ответы:

32

У вас есть два способа создания сертификатов в прошлом. Либо подделка времени (1) (2), либо определение временного интервала при подписании сертификата (3).

1) Во-первых, о фальсификации времени: чтобы одна программа думала, что она не совпадает с датой системы, взгляните libfaketimeиfaketime

Чтобы установить его в Debian:

sudo apt-get install faketime

Затем вы должны использовать faketimeперед opensslкомандой.

Для примеров использования:

$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

От man faketime:

Данная команда обманом заставит поверить, что текущее системное время - это время, указанное в метке времени. Настенные часы будут продолжать работать с этой даты и времени, если не указано иное (см. Дополнительные параметры). Фактически, faketime - это простая оболочка для libfaketime, которая использует механизм LD_PRELOAD для загрузки небольшой библиотеки, которая перехватывает системные вызовы таких функций, как time (2) и fstat (2).

Так, например, в вашем случае вы можете очень хорошо определить дату 2008 года, а затем создать сертификат со сроком действия от 2 лет до 2010 года.

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

В качестве примечания, эта утилита может использоваться в нескольких версиях Unix, включая MacOS, в качестве оболочки для любых программ (не только для командной строки).

В качестве пояснения, только двоичные файлы, загруженные с помощью этого метода (и их дочерние элементы), изменили свое время, и фиктивное время не влияет на текущее время остальной системы.

2) Как утверждает @Wyzard, у вас также есть datefudgeпакет, который очень похож на использование faketime.

Как отличия, datefudgeне влияет fstat(т.е. не меняет время создания файла). Он также имеет собственную библиотеку datefudge.so, которую он загружает с помощью LD_PRELOAD.

Он также имеет место, -s static timeгде всегда указывается указанное время, несмотря на то, сколько дополнительных секунд прошло.

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

3) Помимо подделки времени, а еще проще, вы также можете определить начальную и конечную точки действия сертификата при подписании сертификата в OpenSSL.

Неправильное представление о вопросе, на который вы ссылаетесь в своем вопросе, заключается в том, что срок действия сертификата определяется не во время запроса (по запросу CSR), а при его подписании.

При использовании openssl caдля создания самоподписанного сертификата добавьте параметры -startdateи -enddate.

Формат даты в этих двух опциях, согласно источникам openssl openssl/crypto/x509/x509_vfy.c, - ASN1_TIME, иначе ASN1UTCTime: формат должен быть либо ГГММДДЧЧММССЗ, либо ГГГГММДДЧЧММССЗ.

Цитирование 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."
     */

А из журнала CHANGE (ошибка 2038?) - этот журнал изменений является просто дополнительной сноской, поскольку он касается только тех, кто использует непосредственно API.

Изменения между 1.1.0e и 1.1.1 [xx XXX xxxx]

*) Добавьте в ASN.1 типы INT32, UINT32, INT64, UINT64 и варианты с префиксом Z. Они предназначены для замены LONG и ZLONG и для обеспечения безопасности по размеру. Использование LONG и ZLONG не рекомендуется и планируется для устаревания в OpenSSL 1.2.0.

Итак, создание сертификата с 1 января 2008 года по 1 января 2010 года может быть выполнено как:

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

или

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

-startdateи -enddateпоявляются в opensslисточниках и журнале ИЗМЕНЕНИЯ; как заметил @guntbert, хотя они не отображаются на главной man opensslстранице, они также появляются в 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).

Цитирование openssl/CHANGE:

Изменения между 0.9.3a и 0.9.4 [09 августа 1999]

*) Исправлены аргументы -startdate и -enddate (которые отсутствовали) в программе 'ca'.

PS Что касается выбранного ответа на вопрос вы ссылаетесь от StackExchange: это вообще плохая идея , чтобы изменить системное время, особенно в производственных системах; и с методами в этом ответе вам не нужны привилегии суперпользователя при их использовании.

Руи Ф Рибейро
источник
1
+1. Я знал, что кто-то придет с чем-то лучшим, чем то, что я написал :)
Celada
2
Есть также похожая программа под названием datefudge.
Вайзард
@Wyzard Спасибо, я действительно нашел это в Debian; Интересно, что в руководстве говорится, что, хотя оно также изменяет системные вызовы таких функций, как time (2), оно не влияет на fstat (2).
Руи Ф Рибейро
1
И то, faketimeи другое datefudgeпрекрасно работает в моей системе Debian jessie.
rlandster
1
OTOH: +5, чтобы узнать, где установить эти даты!
Гюнтберт,
8

Я почти удивлен, обнаружив, что очевидная вещь работает: тогда opensslкак в качестве аргумента принимает количество дней, в течение которых сертификат должен быть действительным, просто укажите отрицательное число!

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

Обратите внимание, что на самом деле это приводит к чему-то очень странному: сертификату, чья временная метка истечения предшествует его временной метке начала действия. На самом деле я не рекомендую использовать это для автоматического тестирования, так как это странно. Вы, вероятно, хотите также указать дату начала действия.

Celada
источник
Ну, если честно, я понятия не имел, что ты можешь использовать отрицательные дни.
Руи Ф Рибейро
Вы не можете указать дату начала?
FreeSoftwareServers
@FreeSoftwareServers В CSR вы не можете; см. последнюю часть моего ответа.
Руи Ф Рибейро
Еще интереснее то, что код не найдет такой сертификат? Кстати, я расширил свой ответ
Rui F Ribeiro
3

Или вы можете использовать что-то вроде этой короткой программы на Python ...

Он создает ключ (test.key) и сертификат (test.crt) со временем начала 10 лет в прошлом (-10 * 365 * 24 * 60 * 60 секунд - 10 лет) и сроком действия 5 лет в прошлом. (-5 * 365 * 24 * 60 * 60).

Обратите внимание, что это минимальная демонстрационная программа, поэтому она не требует установки каких-либо расширений (например, basicConstraints) и использует фиксированный серийный номер.

#!/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
источник
в коде отсутствуют основные поля стандарта X.509.
Руи Ф Рибейро
2
Это очень полезно. Это облегчает мне программный контроль над созданием сертификата.
rlandster