Criar certificado autoassinado com data de término no passado

24

Gostaria de criar certificados autoassinados em tempo real com datas de início e término arbitrárias, incluindo datas de término anteriores . Eu preferiria usar ferramentas padrão, por exemplo, OpenSSL, mas qualquer coisa que faça o trabalho seria ótimo.

A questão Stack Overflow Como gerar o certificado openssl com validade inferior a um dia? faz uma pergunta semelhante, mas quero que meu certificado seja autoassinado.

Caso você esteja se perguntando, os certificados são necessários para testes automatizados.

rlandster
fonte

Respostas:

32

Você tem duas maneiras de criar certificados no passado. Fingindo a hora (1) (2) ou definindo o intervalo de tempo ao assinar o certificado (3).

1) Em primeiro lugar, sobre fingir o tempo: para fazer um programa pensar que está em uma data diferente do sistema, dê uma olhada libfaketimeefaketime

Para instalá-lo no Debian:

sudo apt-get install faketime

Você usaria faketimeantes do opensslcomando.

Para exemplos de uso:

$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:

O comando fornecido será levado a crer que a hora atual do sistema é a especificada no registro de data e hora. O relógio de parede continuará funcionando a partir desta data e hora, a menos que especificado de outra forma (consulte as opções avançadas). Na verdade, faketime é um invólucro simples para libfaketime, que usa o mecanismo LD_PRELOAD para carregar uma pequena biblioteca que intercepta chamadas do sistema para funções como time (2) e fstat (2).

Por exemplo, no seu caso, é possível definir muito bem uma data de 2008 e criar um certificado com a validade de 2 anos até 2010.

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

Como uma observação lateral, esse utilitário pode ser usado em várias versões do Unix, incluindo o MacOS, como um invólucro para qualquer tipo de programa (não exclusivo da linha de comando).

Como esclarecimento, apenas os binários carregados com esse método (e seus filhos) têm seu horário alterado e o horário falso não afeta o horário atual do restante do sistema.

2) Como afirma o @Wyzard, você também tem o datefudgepacote que é muito semelhante em uso faketime.

Como diferenças, datefudgenão influencia fstat(ou seja, não altera a criação do tempo do arquivo). Também possui sua própria biblioteca, datefudge.so, que carrega usando LD_PRELOAD.

Ele também possui um local -s static timeonde o tempo referenciado é sempre retornado, apesar de quantos segundos extras se passaram.

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

3) Além de fingir a hora, e ainda mais simplesmente, você também pode definir o ponto inicial e final da validade do certificado ao assinar o certificado no OpenSSL.

O equívoco da pergunta à qual você vincula sua pergunta é que a validade do certificado não está definida no momento da solicitação (na solicitação de RSE), mas ao assiná-la.

Ao usar openssl capara criar o certificado autoassinado, adicione as opções -startdatee -enddate.

O formato da data nessas duas opções, de acordo com as fontes openssl em openssl/crypto/x509/x509_vfy.c, é ASN1_TIME, também conhecido como ASN1UTCTime: o formato deve ser YYMMDDHHMMSSZ ou YYYYMMDDHHMMSSZ.

Citação 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."
     */

E a partir do log CHANGE (bug 2038?) - Esse log de alterações é apenas uma nota de rodapé adicional, pois diz respeito apenas àqueles que usam diretamente a API.

Alterações entre 1.1.0e e 1.1.1 [xx XXX xxxx]

*) Adicione os tipos ASN.1 INT32, UINT32, INT64, UINT64 e variantes prefixadas com Z. Elas destinam-se a substituir LONG e ZLONG e a garantir o tamanho. O uso de LONG e ZLONG é desencorajado e agendado para descontinuação no OpenSSL 1.2.0.

Portanto, a criação de um certificado de 1º de janeiro de 2008 a 1º de janeiro de 2010 pode ser feita da seguinte forma:

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

-startdatee -enddateaparecem nas opensslfontes e no log de CHANGE; como observou @guntbert, embora não apareçam na man opensslpágina principal , também aparecem em 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).

Citação openssl/CHANGE:

Alterações entre 0.9.3a e 0.9.4 [09 de agosto de 1999]

*) Corrija os argumentos -startdate e -enddate (que estavam faltando) no programa 'ca'.

PS Quanto à resposta escolhida da pergunta que você faz referência no StackExchange: geralmente é uma má idéia alterar o horário do sistema, especialmente nos sistemas de produção; e com os métodos nesta resposta, você não precisa de privilégios de root ao usá-los.

Rui F Ribeiro
fonte
1
+1. Eu sabia que alguém viria junto com algo melhor do que o que eu escrevi :)
Celada
2
Há também um programa semelhante chamado datefudge.
wyzard --stop Prejudicar Monica--
@ Wyzard Obrigado, na verdade eu o encontrei no Debian; Curiosamente, o manual afirma que, embora ele também altere as chamadas do sistema para funções como time (2), ele não influencia o fstat (2).
Rui F Ribeiro
1
Ambos faketimee datefudgefuncionam lindamente no meu sistema Debian jessie.
precisa saber é o seguinte
1
OTOH: +5 para descobrir onde definir essas datas!
guntbert
8

Estou quase surpreso ao descobrir que o óbvio funciona: considerando opensslcomo argumento o número de dias pelos quais o certificado deve ser válido, basta fornecer um número negativo!

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

Observe que isso realmente resulta em algo muito estranho: um certificado cujo carimbo de data / hora de expiração precede seu carimbo de data / hora de início de validade. Na verdade, não recomendo que você use isso para seus testes automatizados, pois é estranho. Você provavelmente deseja uma maneira de atualizar também o registro de data e hora do início da validade.

Celada
fonte
Bem, para ser justo, eu não tinha ideia de que você poderia usar dias negativos.
Rui F Ribeiro
Você não pode especificar a data de início?
FreeSoftwareServers
@FreeSoftwareServers No CSR, você não pode; veja a última parte da minha resposta.
Rui F Ribeiro
Mais interessante ainda, o código não encontra esse certificado? btw, eu ampliei minha resposta
Rui F Ribeiro
3

Ou você pode usar algo como este pequeno programa python ...

Ele cria uma chave (test.key) e um certificado (test.crt) com tempo de criação 10 anos no passado (-10 * 365 * 24 * 60 * 60 segundos é -10 anos) e tempo de expiração 5 anos no passado (-5 * 365 * 24 * 60 * 60).

Observe que é um programa de demonstração mínimo, portanto não se preocupa em definir extensões (por exemplo, basicConstraints) e usa serial fixo.

#!/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
fonte
o código parece estar faltando campos padrão X.509 essenciais.
Rui F Ribeiro
2
Isso é muito útil. Isso me dá um controle programático mais fácil sobre a criação do certificado.
rlandster