New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specify password for SSL client side certificate #1573
Comments
Something like:
|
Pretty sure you're supposed to use the |
@sigmavirus24 |
Heh, @t-8ch, you accidentally linked to a file on your local FS. ;) Correct link. |
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, 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? |
You can confirm OpenSSL is blocking on stdin for the passphrase from the interactive python prompt:
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. |
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. In the stdlib version, we need to use |
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 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 |
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. |
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.// 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.// 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 Here is an example request using these cert and keys. 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: |
I have heard through the grapevine that Amazon does exactly this, internally. |
@ideasean Getting invalid credentials still. I should be pointing the load_cert_chain at a .pem file generated by the pfx_to_pem function written for the Temp File method, correct? It has the private key and the cert in it. Since the .pfx works with Postman but it won't authenticate here, could that mean that something's going wrong in the conversion process? |
I did not use the temp file method. I used the DESAdapter approach pretty much as written in AnoopPillai's post on Sep1 above starting with -
I can't speak to the conversion process, but perhaps a good test is to try using the converted pem file with Postman? Also note that I used the approach above because my pem file was encrypted / password protected, and Python requests currently does not support that. If your pem ends up being not password protected, then you should be able to use native requests per link (but then you will have an unprotected cert on your file system). |
@ideasean I broke down the .pfx as per this method and got a .pem file with Bag Attributes and Certificate as well as a .pem file with Bag Attributes and an Encrypted Private Key. Still getting invalid credentials, I guess I'll try putting the certs through on Postman and seeing if they work but I can't figure out why I'm apparently unable to unpack this .pfx properly I also tried the openssl command |
I installed the above-mentioned .cer and Postman doesn't even ask to use it when I make the API call (unlike the popup when it asks to use the .pfx), not sure how else I can make it use that specific cert since there's no "Certificates" panel in the settings like the docs say there is. |
You may be using the browser version of Postman, which doesn't include the cert panel, ssl validation disable etc. Try the full client to change certificate settings. You may want to continue this discussion on a different thread then, as we are a bit off topic. |
@mkane848 saw your original comment where you were getting a I use my private pem with a password using this:
|
For your information, I just implemented PKCS#12 support for The code is a clean implementation: it uses neither monkey patching nor temporary files. Instead, a custom TransportAdapter is used, which provides a custom SSLContext. Any feedback and improvements are welcome! Of course, I wish |
It would be very nice if we could simply do this:
...even if it only worked on python 3.3+. This would only be a minor addition to the API surface. AFAICS, this would mean a small change to urllib3 so that HTTPSConnection accepts an optional
Then it would be backwards-compatible, raising an exception only if you try to use a private key passphrase on an older platform that doesn't support it. Note that the Aside: I am using AWS KMS to manage "secret" data, so I would load the key password at runtime from KMS, not hard-code it into the application. |
I personally wouldn’t be against this change, as I think it would greatly improve our user interface for many users across the board. @sigmavirus24 any thoughts? |
@candlerb @kennethreitz Would it be acceptable to include the PKCS#12 case into that API as well?
The distinction could be either by file extension ( |
I don't have a problem with allowing requests to take a pkcs#12, as long as it can be done safely - and in my opinion that precludes writing the extracted private key to a temporary file. Googling for Python pkcs#12, I find:
So doing this, I think it would be necessary to hook things up in such a way that the key/cert themselves are passed to OpenSSL, not the filenames containing those things. That sounds like a much bigger change. If that's too hard, then it just means that the user has to convert pkcs#12 to PEM off-line, which is pretty straightforward (and can be documented). |
@candlerb As I wrote in my previous comment (#1573 (comment)), I already created a clean implementation that integrates well with So the problems you are describing are already solved. Right now my implementation adds new But I think it should be integrated into the
(Moreover, I'd prefer to see that into |
So, a few things:
I think there's still other work that needs doing before we can handle this in the more general case no matter what and that includes determining the right API for this for Requests 3.0. |
@sigmavirus24 Thanks for the feedback.
How would the PKCS#12 |
My organization has a need to use PKCS12 certificates and is willing to make the necessary enhancements to your library in order to do so. Decrypting the .p12 files to .pem files is considered too much of a risk and it adds an extra step to deal with. We'd like to add functionality to generate and provide an appropriate |
Just a quick reminder: A clean implementation has already been provided by our company, but as a separate adapter: https://github.com/m-click/requests_pkcs12 Feel free to reformat it into a pull request for requests itself. Along the way, you might want to fix a minor issue: The ssl_context should not be held in memory for a whole session, but as shortly as possible, just for a single given connection. See also: In case you fix it along the way, it would be nice if you could provide it as a small pull request to https://github.com/m-click/requests_pkcs12 in addition to requests itself. That way, all people who are using the |
Yeah, https://github.com/m-click/requests_pkcs12 worked for me and did exactly what I wanted it to do. Thanks so much @vog ! I hope requests is able to support that eventually. |
I am also going to thank @vog for his implementation, works just as expected, and solves the problem of keeping cert/key in the non-secure storages like S3 in my case. Hopefully, this can make its way to |
Closing as duplicate of #2519 |
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.The text was updated successfully, but these errors were encountered: