16

I cant seem to get the handshake working properly.

cert = 'path/to/cert_file.pem'
url = 'https://example.com/api'

requests.get(url, cert=cert, verify=True)

This is fine when I use it locally where I have the file physically. We host our application on heroku and use environvariables.

The requests module doesnt seem to accept certificates as strings. eg.

$ export CERTIFICATE="long-list-of-characters"

requests.get(url, cert=get_env('CERTIFICATE'), verify=True)

I have also tried something like this:

cert = tempfile.NamedTemporaryFile()
cert.write(CERTIFICATE)
cert.seek(0)
requests.get(url, cert=cert.name, verify=True)

First of all, it works locally but not on heroku. Anyways, it doesnt feel like a solid solution. I get a SSL handshake error.

Any suggestions?

gelbander
  • 1,176
  • 14
  • 20
  • @Gelbander, who signed the cert_file.pem? Is it self-signed by your custom/inhouse root Certificate Authority? Have you tried uploading your pem file to Heroku, just to be sure to be sure it works passing the pem full path in Heroku? – Andre Miras Nov 27 '16 at 16:32
  • Also I'm not sure using env variables is the preferred way. It's probably better using "heroku certs:add server.crt server.key" – Andre Miras Nov 27 '16 at 16:51

4 Answers4

6

Vasili's answer is technically correct, though per se it doesn't answer your question. The keyfile, truly, must be unencrypted to begin with.

I myself have just resolved a situation like yours. You were on the right path; all you had to do was

1. Pass delete=False to NamedTemporaryFile(), so the file wouldn't be deleted after calling close()

2. close() the tempfile before using it, so it would be saved

Note that this is a very unsafe thing to do. delete=False, as I understand, causes the file to stay on disk even after deleting the reference to it. So, to delete the file, you should manually call os.unlink(tmpfile.name).

Doing this with certificates is a huge security risk: you must ensure that the string with the certificate is secured and hidden and nobody has access to the server.

Nevertheless, it is quite a useful practice in case of, for example, managing your app both on a Heroku server as a test environment and in a Docker image built in the cloud, where COPY directives are not an option. It is also definitely better than storing the file in your git repository :D

Dmitry Orlov
  • 454
  • 6
  • 14
4

This is an old question, but since I ended up here and the question wasn't answered I figure I'll point to the solution I came up with for a similar question that can be used to solve the OP's problem.

This can be done by monkey patching requests using this technique.

greenbender
  • 838
  • 7
  • 11
-1

One simple hack is to use verify=False and not send the certificates at all. This works in most cases and when you are okay with not verifying the connection.

Roopak A Nelliat
  • 2,009
  • 3
  • 19
  • 26
  • `verify=False` impacts what you do with the certificates you receive from the remote part, and has nothing to do with certificate you may send from your side to the remote side. And in any case is always a bad idea, if you do that you should go back to HTTP as you remove all interests of using HTTPS. – Patrick Mevzek Feb 09 '23 at 20:28
-4

As per the requests documentation:

The private key to your local certificate must be unencrypted. Currently, Requests does not support using encrypted keys.

You can [also] specify a local cert to use as client side certificate, as a single file (containing the private key and the certificate) or as a tuple of both file's path:

requests.get('https://kennethreitz.com', cert=('/path/client.cert', '/path/client.key'))

You must include the path for both public and private key... or you can include the path to a single file that contains both.

Community
  • 1
  • 1
Vasili Syrakis
  • 9,321
  • 1
  • 39
  • 56