Requests: SSLError "bad handshake: Error([('SSL rotinas', 'tls_process_server_certificate', 'certificate check failed')],) com certificado autoassinado

Criado em 10 nov. 2017  ·  12Comentários  ·  Fonte: psf/requests

Olá. Estou tentando me conectar a um servidor com https. Eu tenho o certificado que é um certificado autoassinado que está sendo incluído no parâmetro de verificação, mas o resultado é um erro 'falha na verificação do certificado'. Eu estava suspeitando que tinha a ver com o certificado sendo autoassinado (pelo Microsoft IIS), mas com curl isso funciona.
Desde já, obrigado!

Esta é a saída do openssl:

openssl s_client -showcerts -connect server:44300
CONNECTED(00000003)
depth=0 CN = server
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = server
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=server
   i:/CN=server
-----BEGIN CERTIFICATE-----
<certificate data here>
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=server
issuer=/CN=server
---
No client certificate CA names sent
Peer signing digest: SHA1
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1477 bytes and written 431 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
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-AES256-GCM-SHA384
    Session-ID: CC4A000083B1E03B446416C9C0B16CBEAB79949E3CF5C936A309A6F92FA01364
    Session-ID-ctx:
    Master-Key: 798A570B0EC2A0CBB7C4C4DE6167E7579A92239942D869CD794B8BEBEA6EB5E492394634AD32665A8BB829DE1F3858D2
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510329948
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

resultado esperado

Estou esperando, como o certificado está no parâmetro de verificação, que a conexão não falhe. Se eu tentar o mesmo com curl fora do python, funciona:

curl https://server:44300 --cacert /usr/share/ca-certificates/server.crt
 HTTP/1.1 403 Forbidden
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 1158
Content-Type: text/html
Server: Microsoft-IIS/10.0
X-Frame-Options: SAMEORIGIN
P3P: CP=None
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Requested-With,Content-Type
Access-Control-Allow-Credentials: true
Date: Fri, 10 Nov 2017 16:02:26 GMT

(Falha, mas não devido a problemas de certificado)

Resultado atual

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 72, in get
    return request('get', url, params=params, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/adapters.py", line 506, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='nlybstqvp4nb75n.code1.emi.philips.com', port=44300): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

Etapas de reprodução

import requests
requests.get('https://server:44300', verify='/usr/share/ca-certificates/server.crt')

Informação do sistema

$ python -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": "2.1.3"
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "2.7.12"
  },
  "platform": {
    "release": "4.10.0-38-generic",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "1010007f",
    "version": "17.3.0"
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "1000207f"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": true
}

Este comando está disponível apenas em Requests v2.16.4 e superior. Por outro lado,
forneça algumas informações básicas sobre seu sistema (versão Python,
sistema operacional, etc).

Comentários muito úteis

Se você é iniciante no módulo python requests e deseja fazer algumas coisas que exigem acesso a sites seguros, há uma grande chance de você ser condenado por este erro - Certificate verify failed e como todos os programadores iniciantes você será tentado a usar auth = session.post( mysecureurl, verify=false)

Mas isso é uma prática muito ruim e foi desencorajada em muitos posts SO, mas ainda assim os iniciantes usam mal isso porque o erro é muito chato de corrigir.
Deixe-me tentar lançar alguma luz sobre esta questão.
Python (pip) e Conda e qualquer software baseado em python usa armazenamento de certificados separado, assim como todos os navegadores. A biblioteca Python Requests usa seu próprio arquivo CA por padrão ou usará o pacote de certificados do pacote certifi, se instalado. Além disso, o pip não usa os certificados do sistema ao contrário do curl .
Portanto, para o requests você precisa especificar manualmente o armazenamento de certificados por meio do conda ou pip.

Tldr;

  1. Exporte toda a cadeia de certificados codificados .cer usando o navegador de acordo com este blog incrível mostrado aqui. Observe que esse blog não é sobre conda certstore, mas git certstore e diz apenas para exportar a raiz, no entanto, exportei todas as cadeias de certificados em arquivos separados .
  2. Em seguida, instale certifi usando o comando pip install certifi
  3. Verifique o caminho padrão do armazenamento de certificados de conda ou python:

import ssl
ssl.get_default_verify_paths() OU
import certifi
certifi.where()

  1. Depois de localizar o arquivo cacert.pem padrão, abra-o (de preferência no Notepad++) e anexe todo o certificado no final do arquivo. (Cuidar da demarcação do certificado -----BEGIN CERTIFICATE----- e -----END CERTIFICATE----- ). Salve o arquivo e pronto.
    Ou se você estiver usando o conda, use os comandos do conda:
    conda config --set ssl_verify <pathToYourFile>.crt
    (Notei que este comando atualiza coisas em C:\Users\johndoe\.condarc )

  2. Use o código abaixo para verificar:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

Além disso, se você estiver no linux, poderá exportar cacert personalizado para todo o sistema ou perfil de usuário ( .bashrc ou .bash_profile ) usando este link .

Todos 12 comentários

BTW, este é o Ubuntu 16.04

Esse erro é quase certo porque o próprio certificado é inválido de alguma forma. Você pode fornecer o próprio certificado codificado em PEM, por favor?

Oi. Obrigado pela resposta rápida. Este é o certificado:
-----INICIAR CERTIFICADO-----
MIID3TCCAsWgAwIBAGIIU/nMdlbWojMwDQYJKoZIhvcNAQELBQAwMDEuMCwGA1UE
AwwlTkxZQlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbTAeFw0xNzEx
MTAwMDAwMDBaFw0yNzExMTAwMDAwMDBaMDAxLjAsBgNVBAMMJU5MWUJTVFFWUDRO
Qjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC3mzWep6k1/FbkzzoyZ8QBy/tE8adfwKvw80zaLL+car1bBZ9U
VIXs4es3babtjD3QJWP5/mwoBdIp8gvQkjA1X7RBNJZXbPz6hGR4eqaeRQLrFV9Y
TtB92MA9CDpCXfalCvzzO1jw3zvP1BHUdnTQEwSnnwtf/Ryaud+e7TDxGq8LThmc
glZgO8d2zaYpIjwWx92bXDF/qlqWBkH5mtKIkWOw6Y71xz0Di62cFrMAPEGBjK3c
szpBa5Ttb9+SFtl16t2xDyCbiPFkoMW/4u3Husy/i18hLhEuQwZMHnWsocm+etZ4
8fDt5Bqhab8zC+LKS+Ll7qZdqMHzobeB6j5JAgMBAAGjgfowgfcwXwYDVR0jBFgw
VoAUGdJ3os9nPtTubuwcy1ugtDMdSMCHNKQyMDAxLjAsBgNVBAMMJU5MWUJTVFFW
UDROQjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb22CCFP5zHZW1qIzMB0GA1UdDgQW
BBQZ0neiz2c+1O5u7BzLW6C0Mx1IwDAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIE
sDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwOwYDVR0RBDQwMoIlTkxZ
QlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbYIJbG9jYWxob3N0MA0G
CSqGSIb3DQEBCwUAA4IBAQC0MtflowNB4LTLKD1MW3w0QIY5ale3/sEhNCQgHGN5
iNZJptFuFt5jgPGmFjy0Pb5vLMS/Ag1RF3UgTeZzFnaSgyB4mTnwj1gLdwQidVcr
2SlL7TffCj0m/bYjtNbwExRqXE4pQKb5RKwYwpruZaX/G3oHWOG9+2X9Pw5C42zB
OFE4KvYUwOV+noabXvil8LERIdKYxR/2B6qBiwm47IcioqM07zTYLHJ+WDTEMO2k
Qy51yXwFmeOEr5MIBELYCQ0j2AfI4RCXr+2cyUym7tjEr3/I8EsZ5Crvdf++Gwaz
2A05ScPMr+5yfVXygZCenMTwNAyUY1yN9zVj8/n94Psa
-----FIM DO CERTIFICADO-----
Eu sei que não é totalmente válido. Mas de alguma forma o curl é capaz de aceitá-lo. Eu queria saber por que os pedidos não fazem o mesmo. Eu assumi que ambos usam openssl para validar o certificado.

Alguma atualização sobre isso? Também estou enfrentando problemas com sites certificados por auto-assinatura na biblioteca de solicitações python.

Obtendo abaixo do erro.

request.get('https://10.10.24.20', verifique='/etc/ssl/certs/certSIGN_ROOT_CA.pem')
Traceback (última chamada mais recente):
Arquivo "", linha 1, em
Arquivo "/usr/local/lib/python2.7/dist-packages/requests/api.py", linha 72, em get
return request('get', url, params=params, *kwargs)Arquivo "/usr/local/lib/python2.7/dist-packages/requests/api.py", linha 58, na solicitaçãoreturn session.request(método=método, url=url, * kwargs)
Arquivo "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", linha 508, na solicitação
resp = self.send(prep, *send_kwargs)Arquivo "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", linha 618, no envior = adapter.send(solicitação, * kwargs)
Arquivo "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", linha 506, em send
aumentar SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='10.10.24.20', port=443): Máximo de tentativas excedidas com url: / (causado por SSLError(SSLError("bad handshake: Error([('SSL rotinas', 'tls_process_server_certificate ', 'falha na verificação do certificado')],)",),))

Se eu continuar verificando False, funciona, mas eu quero com Verify=True

Eu não tinha visto isso antes, então abri um novo problema, desculpe por isso. Mas estou enfrentando o mesmo problema. Para mim, 'request' falha mesmo com Verify=False.

$ python
Python 2.7.13 (padrão, 19 de janeiro de 2017, 14:48:08)
[GCC 6.3.0 20170118] no linux2
Digite "ajuda", "direitos autorais", "créditos" ou "licença" para obter mais informações.

solicitações de importação
request.get("https://localhost:9000/getcpuinfo", verifique=False)
Traceback (última chamada mais recente):
Arquivo "", linha 1, em
Arquivo "/usr/lib/python2.7/dist-packages/requests/api.py", linha 70, em get
return request('get', url, params=params, *kwargs)Arquivo "/usr/lib/python2.7/dist-packages/requests/api.py", linha 56, na solicitaçãoreturn session.request(método=método, url=url, * kwargs)
Arquivo "/usr/lib/python2.7/dist-packages/requests/sessions.py", linha 488, na solicitação
resp = self.send(prep, *send_kwargs)Arquivo "/usr/lib/python2.7/dist-packages/requests/sessions.py", linha 609, no envior = adapter.send(solicitação, * kwargs)
Arquivo "/usr/lib/python2.7/dist-packages/requests/adapters.py", linha 497, no envio
aumentar SSLError(e, request=request)
request.exceptions.SSLError: ("handshake incorreto: SysCallError(-1, 'EOF inesperado')",)

Minhas informações de certificado local são,

$ openssl s_client -showcerts -connect localhost:9000
CONNECTED(00000003)
depth=0 CN = localhost
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = localhost
verify return:1
write:errno=0
---
Certificate chain
 0 s:/CN=localhost
   i:/CN=localhost
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIJAKATu2AY/QT4MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xNzExMDkyMTQ1NTBaFw0xNzEyMDkyMTQ1NTBaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAJTPk5nao0wG/EDFGnq7BvXMkEZ5oUVq7PAUxWi+E/byJk924l7Z5kACgWBa
zQL0lLXLpdMk97EFGWMblz5Ehtqh7U8HaE9OZ6x/pesDTka+REnpXecklRrdZHX7
lfFnNIU58grPpB2GyUXrRdOtcPlaKXUo+VTd7PgwMtYVtt8pyTWxSB2MMYrqJGT8
78KX6trRzQLm7tas3U0jD59+R8j7gxU6FyFaNJBrkJ5T9kHGKOsAzSqZdCgRBjl5
i7xcXJfOAAnZ3jhGlY5DQht+HZDHhjkLG9kcZZhFDYteFk8drzbd3lBw96nLq+8A
Sy92FtQL4GiYSwZ0WVAmwmTCGjUCAwEAAaNTMFEwHQYDVR0OBBYEFLYjwGKbcV9h
sYHxe8l9UvXVivByMB8GA1UdIwQYMBaAFLYjwGKbcV9hsYHxe8l9UvXVivByMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHl7okBCJlms+cwfzLhs
sbyyuX2wgngxyvjy497zBmeh1TiueGPhOx9u/sfJSZmoUaeRd/zPGkp2DcPQ+Lo2
EHYrXMPE1Ecgpu/15JZ8jNuE+FwZb9lllULLwzq8pDkdbdsSRltdV/rFlZ2YkscB
c+xvVaCltw5KpKnY6AWHoqwoDcd8TZKzyKXLSuluKbHNC1lvg8cMzs6hFA9P92Ae
9P08AKLAIOGJ7QzRrXQIsAO4p9rHheeZeYQZyNiRrXPQUoWos4+OjynaNs+FabhN
XBtSl/GGPRRRfU/D9v4iKfQx15CEvs1AKn1Z6mIPF05pSqbgIoz2mJBV6UM7e+hz
TRs=
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=localhost
issuer=/CN=localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1198 bytes and written 302 bytes
Verification error: self signed 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: 5311B8500C8AF327083E1465FE1E1A6A98E0996B4791150A01D6B130C7F0549909A4BDCDED388E9EDE124BB6C50E150A
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510599077
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: no
---

Por favor, não sequestre este problema. Em geral, você deve usar o Stack Overflow para fazer essas perguntas: se você sobrecarregar o problema com solicitações, você me inclinará a fechá-lo.

@sg77 Seu certificado está marcado CA=FALSE , o que o torna inelegível para ser um certificado de emissão. Suspeito que o curl esteja personalizando seu código para usar este certificado como um pino, em vez de uma CA raiz. Requests não faz isso: este certificado não pode ser uma CA raiz, portanto não é validado.

Eu recomendo cunhar um novo certificado com CA=TRUE ou omitir completamente as BasicConstraints.

@ashwini-kaklij Não tenho ideia de por que sua verificação está falhando, porque não consigo ver o certificado. Por favor , não poste aqui : direcione sua pergunta para o StackOverflow.

@uttampawar Seu erro é causado pelo servidor não gostar do nosso handshake TLS por algum motivo. Na ausência de mais detalhes, não posso determinar o motivo. Por favor, mais uma vez, leve sua pergunta para o Stack Overflow.

@Lukasa Eu não queria sequestrar o problema, apenas elevar. Pode ser que este seja um lugar errado. Acrescentei meus comentários e observações, pois vi que é de natureza semelhante. Eu aprecio seu feedback. Vou perguntar no stackoverflow. Obrigado.

Por favor, não sequestre este problema. Em geral, você deve usar o Stack Overflow para fazer essas perguntas: se você sobrecarregar o problema com solicitações, você me inclinará a fechá-lo.

@sg77 Seu certificado está marcado CA=FALSE , o que o torna inelegível para ser um certificado de emissão. Suspeito que o curl esteja personalizando seu código para usar este certificado como um pino, em vez de uma CA raiz. Requests não faz isso: este certificado não pode ser uma CA raiz, portanto não é validado.

Eu recomendo cunhar um novo certificado com CA=TRUE ou omitir completamente as BasicConstraints.

@ashwini-kaklij Não tenho ideia de por que sua verificação está falhando, porque não consigo ver o certificado. Por favor, _não poste aqui_: em vez disso, direcione sua pergunta para o StackOverflow.

@uttampawar Seu erro é causado pelo servidor não gostar do nosso handshake TLS por algum motivo. Na ausência de mais detalhes, não posso determinar o motivo. Por favor, mais uma vez, leve sua pergunta para o Stack Overflow.

Oi Lukasa - Acabei de ver sua resposta. Você respondeu a @sg77 dizendo "Seu certificado está marcado CA=FALSE ".

Como você sabe que isso é falso e onde/como posso definir isso de volta para TRUE?

Obrigado.

postar a chave do certificado online é como postar sua senha

@Synida

postar a chave do certificado online é como postar sua senha

Umm não. A postagem da chave privada do certificado é equivalente à sua senha.

Cada CA nos negócios tem seu certificado em todos os principais sistemas operacionais e navegadores. Eles estão dando sua senha para todos?

https://en.wikipedia.org/wiki/Public-key_cryptography

Esse erro é quase certo porque o próprio certificado é inválido de alguma forma. Você pode fornecer o próprio certificado codificado em PEM, por favor?

como posso ter acesso a este certificado?

Se você é iniciante no módulo python requests e deseja fazer algumas coisas que exigem acesso a sites seguros, há uma grande chance de você ser condenado por este erro - Certificate verify failed e como todos os programadores iniciantes você será tentado a usar auth = session.post( mysecureurl, verify=false)

Mas isso é uma prática muito ruim e foi desencorajada em muitos posts SO, mas ainda assim os iniciantes usam mal isso porque o erro é muito chato de corrigir.
Deixe-me tentar lançar alguma luz sobre esta questão.
Python (pip) e Conda e qualquer software baseado em python usa armazenamento de certificados separado, assim como todos os navegadores. A biblioteca Python Requests usa seu próprio arquivo CA por padrão ou usará o pacote de certificados do pacote certifi, se instalado. Além disso, o pip não usa os certificados do sistema ao contrário do curl .
Portanto, para o requests você precisa especificar manualmente o armazenamento de certificados por meio do conda ou pip.

Tldr;

  1. Exporte toda a cadeia de certificados codificados .cer usando o navegador de acordo com este blog incrível mostrado aqui. Observe que esse blog não é sobre conda certstore, mas git certstore e diz apenas para exportar a raiz, no entanto, exportei todas as cadeias de certificados em arquivos separados .
  2. Em seguida, instale certifi usando o comando pip install certifi
  3. Verifique o caminho padrão do armazenamento de certificados de conda ou python:

import ssl
ssl.get_default_verify_paths() OU
import certifi
certifi.where()

  1. Depois de localizar o arquivo cacert.pem padrão, abra-o (de preferência no Notepad++) e anexe todo o certificado no final do arquivo. (Cuidar da demarcação do certificado -----BEGIN CERTIFICATE----- e -----END CERTIFICATE----- ). Salve o arquivo e pronto.
    Ou se você estiver usando o conda, use os comandos do conda:
    conda config --set ssl_verify <pathToYourFile>.crt
    (Notei que este comando atualiza coisas em C:\Users\johndoe\.condarc )

  2. Use o código abaixo para verificar:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

Além disso, se você estiver no linux, poderá exportar cacert personalizado para todo o sistema ou perfil de usuário ( .bashrc ou .bash_profile ) usando este link .

Esta página foi útil?
0 / 5 - 0 avaliações