Criei um certificado autoassinado para foo.localhost usando uma recomendação do Let's Encrypt usando este Makefile:
include ../.env
configuration = csr.cnf
certificate = self-signed.crt
key = self-signed.key
.PHONY: all
all: $(certificate)
$(certificate): $(configuration)
openssl req -x509 -out $@ -keyout $(key) -newkey rsa:2048 -nodes -sha256 -subj '/CN=$(HOSTNAME)' -extensions EXT -config $(configuration)
$(configuration):
printf "[dn]\nCN=$(HOSTNAME)\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:$(HOSTNAME)\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth" > $@
.PHONY: clean
clean:
$(RM) $(configuration)
Eu então atribuí isso a um servidor web. Verifiquei que o servidor retorna o certificado relevante:
$ openssl s_client -showcerts -connect foo.localhost:8443 < /dev/null
CONNECTED(00000003)
depth=0 CN = foo.localhost
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = foo.localhost
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/CN=foo.localhost
i:/CN=foo.localhost
-----BEGIN CERTIFICATE-----
[…]
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=foo.localhost
issuer=/CN=foo.localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1330 bytes and written 269 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: […]
Session-ID-ctx:
Master-Key: […]
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket:
[…]
Start Time: 1529622990
Timeout : 7200 (sec)
Verify return code: 21 (unable to verify the first certificate)
Extended master secret: no
---
DONE
Como faço para que o cURL confie nele sem modificar nada em /etc? --cacert
não funciona, presumivelmente porque não há CA:
$ curl --cacert tls/foo.localhost.crt 'https://foo.localhost:8443/'
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
O objetivo é habilitar HTTPS durante o desenvolvimento:
- Não posso ter um certificado completamente semelhante à produção sem muito trabalho para habilitar a verificação de DNS em todos os ambientes de desenvolvimento. Portanto, tenho que usar um certificado autoassinado.
- Obviamente, ainda quero tornar meu ambiente de desenvolvimento o mais semelhante possível à produção, portanto, não posso simplesmente ignorar todos e quaisquer problemas de certificado.
curl -k
é comocatch (Exception e) {}
neste caso - nada como um navegador conversando com um servidor web.
Em outras palavras, ao correr curl [something] https://project.local/api/foo
, quero ter certeza de que
- se o TLS estiver configurado corretamente, exceto por ter um certificado autoassinado, o comando será bem-sucedido e
- se eu tiver algum problema com minha configuração de TLS, exceto por ter um certificado autoassinado, o comando falhará.
Usando HTTP ou --insecure
falha no segundo critério.
Experimente
-k
:Deve "aceitar" certificados autoassinados
Seguir estas etapas deve resolver seu problema:
echo quit | openssl s_client -showcerts -servername "${API_HOST}" -connect "${API_HOST}":443 > cacert.pem
curl
cliente sobre isso:curl --cacert cacert.pem --location --silent https://${API_HOST}
Além disso, pode-se usar wget e ignorar certificados com:
wget --no-check-certificate https://${API_HOST}
Eu tive esse problema, exatamente o mesmo problema e mensagens de erro, mas usei o certtool do GNUTLS para gerar meu certificado em vez de openssl.
Meu problema era que eu não tinha feito meu certificado auto-assinado uma CA. Ele foi configurado apenas para atuar como um certificado de servidor web. Que é tudo o que eu queria fazer com ele e não ia usá-lo como CA para assinar outros certificados.
Mas quando você deseja adicionar um certificado à cadeia de confiança como emissor de outros certificados, esse certificado deve ser uma CA ou será rejeitado pelo openssl!
Com
certtool -i < mycert.crt
, é preciso ver isso:Tente adicionar
-addext basicConstraints=critical,CA:TRUE,pathlen:1
ao seu comando openssl ou modificar seu arquivo cnf para o mesmo efeito.Ou use o certtool, é muito mais fácil para a geração única de certificados:
E, em seguida, responda aos prompts para fornecer o CN do certificado e assim por diante. E diga sim quando perguntado se é para uma autoridade de certificação!
trustme
, usado por urllib3 , parece uma boa opção. Por seu leia-me:Não é válido ter uma cadeia de confiança que inclua um certificado autoassinado. Se fosse esse o caso, qualquer pessoa poderia fornecer uma cadeia de confiança válida (inventada). Se um certificado autoassinado aparecer em uma cadeia de confiança, ele deverá ser ignorado. Um certificado autoassinado só pode ser válido em um diretório local (controlado pelo proprietário do computador). O certificado fornecido a qualquer servidor deve ser encadeado ao certificado autoassinado.
Um guia geral sem a maioria dos pequenos detalhes.
Sua saída do
openssl s_client
comando está mostrando dois erros:Isso significa que o armazenamento de certificados padrão em sua máquina não possui um certificado que valida a cadeia fornecida no site que você usou. Você precisa de um diretório com um certificado autoassinado e um certificado encadeado para o servidor web.
Passos:
Você pode construir um novo diretório (em qualquer lugar), processá-lo com o
c_rehash
script e dizer ao openssl para usá-lo para verificar os certificados com a opção-CApath Directory
. Faça as alterações até se livrar de ambos os erros ao usar a-CApath
opção.Gere um certificado encadeado para o servidor web.
Em seguida, informe ao curl sobre o diretório de certificados com:
e todas as outras opções necessárias.
Isso limpará os dois erros.
A solução fornecida usando
trustme
funciona perfeitamente, mas se alguém estiver interessado em criar os certificados para fins de aprendizado, estes são os passos que funcionaram para mim:Crie a CA raiz:
Crie a chave privada do servidor:
Crie a solicitação de assinatura de certificado:
Crie o
localhost.ext
arquivo com o seguinte conteúdo:Este arquivo tem a configuração de DNS para o certificado. Isso é importante, pois a verificação de TLS não será aprovada se o nome na URL não corresponder ao DNS no certificado.
Assine o certificado do servidor com a CA raiz que foi criada antes:
Converta os
crt
arquivos para opem
formato:Agora você deve ser capaz de usar o
localhost.pem
e olocalhost.key
no servidor. O cliente deve usar orootCA.pem
para se conectar ao servidor.No meu caso, tive que usar um
jks
arquivo. Opem
arquivo pode ser convertido dajks
seguinte forma:As entradas no keystore podem ser vistas usando este comando:
O
localhost.jks
deve ser usado no servidor e a conexão pode ser testada usando:Referências:
Se você salvar o self-signed.crt do seu servidor, poderá passá-lo para o curl via "--cacert self-signed.crt" e o curl validará o certificado do seu servidor usando o certificado CA fornecido.