Specify password for SSL client side certificate

As far as I know currently it's not possible to specify the password for the client side certificate you're using for authentication.
This is a bit of a problem because you typically always want to password protect your .pem file which contains the private key. `openssl` won't even let you create one without a password.


Something like:

`requests.get('https://kennethreitz.com', cert='server.pem', cert_pw='my_password')`


Pretty sure you're supposed to use the `cert` param for that: `cert=('server.pem', 'my_password')`


@sigmavirus24 
The tuple is for `(certificate, key)`. Currently there is no support for encrypted keyfiles.
The [stdlib](file:///usr/share/doc/python/html/library/ssl.html#ssl.SSLContext.load_cert_chain) only got support for those in version 3.3.


Heh, @t-8ch, you accidentally linked to a file on your local FS. ;) [Correct link](http://docs.python.org/3.3/library/ssl.html#ssl.SSLContext.load_cert_chain).


Quite right @t-8ch. This is why I should never answer issues from the bus. :/


So the current consensus is we don't support this. How much work is it likely to be to add support in non-3.3 versions of Python?


How hard would it be to throw an error on this condition? I just ran into this silly problem and it took two hours to figure out, it would be nice if it would throw an error, it currently just sits there looping. Thanks for the awesome library!


Wait, it sits where looping? Where in execution do we fail? Can you print the traceback from where we loop?


It seems to hang right here:

r = requests.get(url,
                         auth=headeroauth,
                         cert=self.cert_tuple,
                         headers=headers,
                         timeout=10,
                         verify=True)

I tried turning the timeout out up or down to no avail, but I imagine it knows well before the timeout it can't use the cert. Thanks!


Ah, sorry, I wasn't clear. I meant to let it hang and then kill it with Ctrl + C so that python throws a KeyboardInterrupt exception, then to see where we are in the traceback. I want to know where in Requests the execution halts.


What's happening (or at least what I've seen in many cases) is that OpenSSL, upon being given a password-protected certificate, will prompt the user for a password. It shows up in no logs (because the prompt is directly printed), and it doesn't time out because it's waiting for a user to press enter.

Needless to say, it's cubmersome, dangerous behavior when the code is running on a server (because it'll hang your worker with no option for recovery other than killing the process).

Is there a way to make requests raise an exception in that case instead of prompting for a password, or is that completely out of your control and in OpenSSL's hands?


@maxnoel I'm pretty sure this is in OpenSSL's hands but if you can answer @Lukasa's question (the last comment on this issue) it would be very helpful in giving a definite answer regarding if there was anything we can do to help.


You can confirm OpenSSL is blocking on stdin for the passphrase from the interactive python prompt:

```
>>> r = requests.get("https://foo.example.com/api/user/bill", cert=("client.crt", "client.key"))
Enter PEM pass phrase:
>>>
```

If you're running from a backgrounded process, I assume OpenSSL will block waiting on that input.


That's correct. Is there anything requests can do to prevent that from happening? Raising an exception when no password is given would be far more useful than prompting for stuff on stdin (especially in a non-interactive program).


I'm afraid that I don't know of any way. @reaperhulk?


There are ways to stop OpenSSL from doing this, but I'm not sure if they're exposed by pyOpenSSL. Where does requests call pyopenssl to load the client cert? I can dig a bit.


@reaperhulk It's done from in urllib3, [here](https://github.com/shazow/urllib3/blob/master/urllib3/contrib/pyopenssl.py#L252-L260).


We also do something very similar for the stdlib, which will be a whole separate problem.


So we can do this with PyOpenSSL using a patch like [this](https://github.com/reaperhulk/urllib3/commit/c6d1e7f5aa76c0522d9d84e9b9ad757b7f4d8a22). In the stdlib version, we need to use `load_cert_chain` with a password.


Has this problem been solved? I'm currently running into this while trying to connect to an Apache server.


It has not.


What about PKCS#12 formatted (and encrypted) containers which could contain a client cert/key? Would this fall under the same feature request?


@mikelupo Yup.


@telam @mikelupo 
I have the same problem and Googled a lot, finally, I solved it by using pycurl.
In my situation, I use openssl to convert my .pfx file to .pem file which contains both cert & key(encrypted with pass phrase), then invoke the following code.

``` python
import pycurl
import StringIO

b = StringIO.StringIO()
c = pycurl.Curl()
url = "https://example.com"
c.setopt(pycurl.URL, url)
c.setopt(pycurl.WRITEFUNCTION, b.write)
c.setopt(pycurl.CAINFO, "/path/cacert.pem")
c.setopt(pycurl.SSLKEY, "/path/key_file.pem")
c.setopt(pycurl.SSLCERT, "/path/cert_file.pem")
c.setopt(pycurl.SSLKEYPASSWD, "your pass phrase")
c.perform()
c.close()
response_body = b.getvalue()
```

BTW, for security, it's better to not do hardcode for `pass phrase`


Of course. That said, the problem isn't really that a pass phrase is required -- it's that OpenSSL makes your program hang while waiting for someone to type a passphrase in stdin, even in the case of a non-interactive, GUI or remote program.

When a passphrase is required and none is provided, an exception should be raised instead.


if you use a default passphrase of '' for the key, openssl won't hang.
it'll return a bad password text. you can immediately alter your py flow
to then notify the user without that apparant stall


any plan to add this feature 


We want to add it, but we have no schedule to add it at this time.


@botondus I think I found a simpler way to achieve this with request library. I am documenting this for other people who are facing the issue.

I assume that you have a .p12 certificate and a passphrase for the key.

### Generate certificate and private key.

``` sh
// Generate the certificate file.
openssl pkcs12 -in /path/to/p12cert -nokeys -out certificate.pem
// Generate private key with passpharse, First enter the password provided with the key and then an arbitrary PEM password //(say: 1234) 
openssl pkcs12 -in /path/to/p12cert -nocerts -out privkey.pem
```

Well, we are not done yet and we need to generate the key that doesn't require the PEM password every time it needs to talk to the server.

### Generate key without passphrase.

``` sh
// Running this command will prompt for the pem password(1234), on providing which we will obtain the plainkey.pem
openssl rsa -in privkey.pem -out plainkey.pem
```

Now, you will have `certificate.pem` and `plainkey.pem`, both of the files required to talk to the API using requests.

Here is an example request using these cert and keys.

``` python
import requests
url = 'https://exampleurl.com'
headers = {
            'header1': '1214141414',
            'header2': 'adad-1223-122'
          }
response = requests.get(url, headers=headers, cert=('~/certificate.pem', '~/plainkey.pem'), verify=True)
print response.json()
```

Hope this helps:

cc @kennethreitz @Lukasa @sigmavirus24 


I have heard through the grapevine that Amazon does exactly this, internally.