Utwórz certyfikat z podpisem własnym z datą końcową w przeszłości

24

Chciałbym tworzyć certyfikaty z podpisem własnym na bieżąco z dowolnymi datami początkowymi i końcowymi, w tym datami końcowymi w przeszłości . Wolałbym używać standardowych narzędzi, np. OpenSSL, ale wszystko, co wystarczy do wykonania zadania, byłoby świetne.

Pytanie przepełnienia stosu Jak wygenerować certyfikat openssl z terminem ważności krótszym niż jeden dzień? zadaje podobne pytanie, ale chcę, aby mój certyfikat był samopodpisany.

Jeśli się zastanawiasz, certyfikaty są potrzebne do automatycznego testowania.

Rlandster
źródło

Odpowiedzi:

32

W przeszłości istnieją dwa sposoby tworzenia certyfikatów. Albo sfałszowanie czasu (1) (2), albo określenie odstępu czasu podczas podpisywania certyfikatu (3).

1) Po pierwsze, o sfałszowaniu czasu: aby jeden program pomyślał, że jest w innym terminie niż system, spójrz libfaketimeifaketime

Aby zainstalować go w Debianie:

sudo apt-get install faketime

Następnie użyjesz faketimeprzed opensslpoleceniem.

Przykłady użycia:

$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

Od man faketime:

Podane polecenie zostanie oszukane, aby uwierzyć, że bieżący czas systemowy jest zgodny z czasem określonym w znaczniku czasu. Zegar ścienny będzie działał od tej daty i godziny, chyba że określono inaczej (patrz opcje zaawansowane). W rzeczywistości faketime to proste opakowanie dla libfaketime, które wykorzystuje mechanizm LD_PRELOAD do ładowania małej biblioteki, która przechwytuje wywołania systemowe funkcji takich jak time (2) i fstat (2).

Na przykład, w twoim przypadku, możesz bardzo dobrze zdefiniować datę 2008 roku, a następnie stworzyć certyfikat z ważnością od 2 lat do 2010 roku.

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

Na marginesie, narzędzie to może być używane w kilku wersjach Uniksa, w tym MacOS, jako opakowanie dowolnego rodzaju programów (nie tylko w linii poleceń).

Dla wyjaśnienia, tylko pliki binarne załadowane tą metodą (i ich dzieci) mają zmieniony czas, a fałszywy czas nie wpływa na bieżący czas reszty systemu.

2) Jak stwierdza @Wyzard, masz również datefudgepakiet, który jest bardzo podobny w użyciu faketime.

Różnice datefudgenie wpływają fstat(tzn. Nie zmieniają czasu tworzenia pliku). Ma także własną bibliotekę datefudge.so, którą ładuje przy użyciu LD_PRELOAD.

Ma również miejsce, w -s static timektórym wskazany czas jest zawsze zwracany, pomimo upływu dodatkowych sekund.

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

3) Oprócz fałszywego czasu, a nawet prościej, możesz także określić punkt początkowy i końcowy ważności certyfikatu podczas podpisywania certyfikatu w OpenSSL.

Błędne zrozumienie pytania, do którego prowadzi link, polega na tym, że ważność certyfikatu nie jest definiowana w czasie żądania (na żądanie CSR), ale podczas jego podpisywania.

Podczas openssl catworzenia samopodpisanego certyfikatu dodaj opcje -startdatei-enddate .

Format daty w tych dwóch opcjach, zgodnie ze źródłami openssl na openssl/crypto/x509/x509_vfy.c , to ASN1_TIME aka ASN1UTCTime: format musi być RRRMDDGGMMSSZ lub RRRRMMDDGGMMSSZ.

Cytowanie 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."
     */

I z dziennika ZMIANA (błąd 2038?) - Ten dziennik zmian jest tylko dodatkowym przypisem, ponieważ dotyczy tylko tych, którzy używają bezpośrednio interfejsu API.

Zmiany między 1.1.0e a 1.1.1 [xx XXX xxxx]

*) Dodaj typy ASN.1 INT32, UINT32, INT64, UINT64 i warianty z literą Z. Mają one zastąpić LONG i ZLONG i być bezpieczne pod względem rozmiaru. Używanie LONG i ZLONG jest odradzane i planowane do wycofania w OpenSSL 1.2.0.

Tak więc tworzenie certyfikatu od 1 stycznia 2008 r. Do 1 stycznia 2010 r. Można wykonać jako:

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

lub

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

-startdatei -enddatepojawiają się w opensslźródłach i dzienniku ZMIANY; jak zauważył @guntbert, chociaż nie pojawiają się na man opensslstronie głównej , pojawiają się również w 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).

Cytowanie openssl/CHANGE:

Zmiany między 0.9.3a a 0.9.4 [09 sierpnia 1999]

*) Napraw argumenty -startdate i -enddate (których brakowało) w programie „ca”.

PS Jeśli chodzi o wybraną odpowiedź na pytanie, na które powołujesz się ze StackExchange: zmiana czasu systemowego jest na ogół złym pomysłem , szczególnie w systemach produkcyjnych; a dzięki metodom opisanym w tej odpowiedzi nie potrzebujesz uprawnień roota podczas ich używania.

Rui F. Ribeiro
źródło
1
+1. Wiedziałem, że ktoś przyjdzie z czymś lepszym niż to, co napisałem :)
Celada,
2
Istnieje również podobny program o nazwie datefudge.
Wyzard
@Wyzard Dzięki, rzeczywiście znalazłem to w Debianie; co ciekawe, instrukcja mówi, że chociaż zmienia również wywołania systemowe funkcji takich jak time (2), nie ma wpływu na fstat (2).
Rui F Ribeiro
1
Oba faketimei datefudgedziałają pięknie na moim systemie Debiana jessie.
rlandster
1
OTOH: +5 za ustalenie, gdzie ustawić te daty!
guntbert 16.04.17
8

Jestem prawie zaskoczony, gdy stwierdziłem, że oczywista rzecz działa: chociaż openssljako argument przyjmuje liczbę dni, przez które certyfikat powinien być ważny, wystarczy podać liczbę ujemną!

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

Zauważ, że w rzeczywistości powoduje to coś bardzo dziwnego: certyfikat, którego znacznik czasu ważności poprzedza znacznik czasu rozpoczęcia ważności. W rzeczywistości nie zalecam używania tego do automatycznego testowania, ponieważ jest to dziwne. Prawdopodobnie potrzebujesz również sposobu na datowanie datownika rozpoczęcia ważności.

Celada
źródło
Cóż, szczerze mówiąc, nie miałem pojęcia, że ​​możesz wykorzystać dni negatywne.
Rui F Ribeiro
Nie możesz podać daty rozpoczęcia?
FreeSoftwareServers
@FreeSoftwareServers W CSR nie możesz; zobacz ostatnią część mojej odpowiedzi.
Rui F Ribeiro
Co ciekawe, czy kod kreskowy nie znajdzie takiego certyfikatu? przy okazji rozszerzyłem swoją odpowiedź
Rui F Ribeiro
3

Lub możesz użyć czegoś takiego jak ten krótki program python ... (mają zastosowanie zastrzeżenia)

Tworzy klucz (test.key) i certyfikat (test.crt) z czasem początkowym 10 lat w przeszłości (-10 * 365 * 24 * 60 * 60 sekund to -10 lat) i czasem wygaśnięcia 5 lat w przeszłości (-5 * 365 * 24 * 60 * 60).

Należy pamiętać, że jest to minimalny program demonstracyjny, więc nie zawraca sobie głowy ustawieniem żadnych rozszerzeń (np. BasicConstraints) i używa stałego numeru seryjnego.

#!/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
źródło
wydaje się, że w kodzie brakuje podstawowych pól standardowych X.509.
Rui F Ribeiro
2
To jest bardzo pomocne. Daje mi to łatwiejszą programową kontrolę nad tworzeniem certyfikatów.
rlandster 16.04.17