Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to use a .pfx file with Python requests – also works with .p12 files
import contextlib
import OpenSSL.crypto
import os
import requests
import ssl
import tempfile
@contextlib.contextmanager
def pfx_to_pem(pfx_path, pfx_password):
''' Decrypts the .pfx file to be used with requests. '''
with tempfile.NamedTemporaryFile(suffix='.pem') as t_pem:
f_pem = open(t_pem.name, 'wb')
pfx = open(pfx_path, 'rb').read()
p12 = OpenSSL.crypto.load_pkcs12(pfx, pfx_password)
f_pem.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, p12.get_privatekey()))
f_pem.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, p12.get_certificate()))
ca = p12.get_ca_certificates()
if ca is not None:
for cert in ca:
f_pem.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
f_pem.close()
yield t_pem.name
# HOW TO USE:
# with pfx_to_pem('foo.pem', 'bar') as cert:
# requests.post(url, cert=cert, data=payload)
@mkane848
Copy link

mkane848 commented Sep 26, 2017

So I got through the [Errno 13] issue (thanks @d1t69 for the heads up about delete=False), but it seems my credentials are being rejected. They work fine in Postman (Make the request, it asks me to pick my cert, then returns the info I need), so I assume something's going wrong with how it's translating the cert. I checked the contents of the generated .pem file, and the Certificate matches the .pem I used to make the .pfx file but the Key doesn't match the .key file (and trying to substitute that out for the original key gives an SSL Error about asn1 encoding issues).

I also tried this method to no avail. Any insight would be greatly appreciated!

@daultonoryan
Copy link

daultonoryan commented Oct 4, 2017

@d1t69 would you mind posting your actual code adjustment I seem to be messing it up somewhere along the road

@mkane848
Copy link

mkane848 commented Oct 5, 2017

@daultonoryan On Windows, all I had to do was change Line 11 to with tempfile.NamedTemporaryFile(suffix='.pem', delete=False) as t_pem:

The file remains in the folder after the script runs if you leave it like this, so I guess either manually delete it or add a line to delete it after the loop wraps up to clean it out.

@grubbsb
Copy link

grubbsb commented Nov 13, 2017

@erikbern works great and nice simple example of the implementation. Thank you for sharing 👍

@omedirk
Copy link

omedirk commented Mar 27, 2018

I combined this with a hider based on: (https://benkurtovic.com/2014/06/01/obfuscating-hello-world.html)

that way at least the password is not there when you read the code

def pfx_helper(nIn):
    return (lambda f, n: f(f, n))(
        lambda f, n: chr(n % 256) + f(f, n // 256) if n else "",
        nIn)
def pfx_helperInverse(strInverse):
    codes = [ord(c) for c in strInverse]
    return sum(codes[i] * 256 ** i for i in range(len(codes)))

inverse = pfx_helperInverse("yourpasswordhere")
print ("pfx_helper(",inverse, ")")
print (pfx_helper(inverse))

@tigrus
Copy link

tigrus commented Apr 25, 2018

# HOW TO USE:
# with pfx_to_pem('foo.pem', 'bar') as cert:
#     requests.post(url, cert=cert, data=payload)

Is it correct usage? What will happen, if you will send 100k signed requests?

@AleksandarSavic95
Copy link

AleksandarSavic95 commented May 23, 2018

This is a really nice workaround, but there is a library called requests_pkcs12 which does this job 🙂

@slicendicen2
Copy link

slicendicen2 commented Jul 13, 2018

This was a huge help! Thank You. Worked perfectly for me.

@LakshmanRamavath
Copy link

LakshmanRamavath commented Aug 7, 2018

I am getting permission denier error upon executing this piece of code
but when i keep delete=False , pem is getting created and not getting deleted, upon executing with this i get SSL Error

@matheussales
Copy link

matheussales commented Oct 25, 2018

I am getting error something like this - "OpenSSL.SSL.Error: [('memory buffer routines', 'BUF_MEM_grow_clean', 'malloc failure'), ('SSL routines', 'read_state_machine', 'BUF lib')]", can you please help? This error occurs every 1 week, more or less.
Thanks.

@szhjia
Copy link

szhjia commented Mar 5, 2019

Thanks a lot

@thedreamerl
Copy link

thedreamerl commented Mar 14, 2019

Worked fine just about 5 months ago and now when I try to run my program I am getting an error 'certificate verify failed'. Can you please help?? Any suggestions?
Certificates are up to date((

@arthuralvim
Copy link

arthuralvim commented Apr 17, 2019

VOCÊ É UM ARROMBADO!!! Valeuuuuuuuu!

@shacker
Copy link

shacker commented May 8, 2020

With this technique, for me anyway, I'm able to decrypt a .pfx file without error, but then it fails authentication when submitting to the server per the example, even though the same .pfx file with same password submits fine via Postman. Don't know why it won't work, but I used these instructions to "decompose" the .pfx into a pair of .pem files, and those can be submitted directly from python requests. And with that technique, you only have to do it once, not on every request.

@kadnan
Copy link

kadnan commented Sep 2, 2020

It works on *nix machine but on Windows, it is giving error

[WinError 32] The process cannot access the file because it is being used by another process: 'C:\\path\to\\\tmpbnb1dy2v.pem'

@dsv-brazil-user-0
Copy link

dsv-brazil-user-0 commented Oct 26, 2020

For anyone having "Permission Denied" error, here is the fix (delete=False):

with tempfile.NamedTemporaryFile(suffix='.pem', dir=mainDir, delete=False) as t_pem:

@jgm-ktg
Copy link

jgm-ktg commented Dec 4, 2020

with tempfile.NamedTemporaryFile(suffix='.pem') as t_pem:
        t_pem.close() # better solution on Windows than delete=False
        f_pem = open(t_pem.name, 'wb')

@claudep
Copy link

claudep commented May 15, 2021

A variant using cryptography (to avoid DeprecationWarning: PKCS#12 support in pyOpenSSL is deprecated):

from contextlib import contextmanager
from pathlib import Path
from tempfile import NamedTemporaryFile

import requests
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates


@contextmanager
def pfx_to_pem(pfx_path, pfx_password):
    ''' Decrypts the .pfx file to be used with requests. '''
    pfx = Path(pfx_path).read_bytes()
    private_key, main_cert, add_certs = load_key_and_certificates(pfx, pfx_password.encode('utf-8'), None)

    with NamedTemporaryFile(suffix='.pem') as t_pem:
        with open(t_pem.name, 'wb') as f_pem:
            pem_file.write(private_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()))
            pem_file.write(main_cert.public_bytes(Encoding.PEM))
            for ca in add_certs:
                pem_file.write(ca.public_bytes(Encoding.PEM))
        yield t_pem.name

# HOW TO USE:
# with pfx_to_pem('foo.pem', 'bar') as cert:
#     requests.post(url, cert=cert, data=payload)

@ildarbinanas
Copy link

ildarbinanas commented Jul 15, 2021

I am find mistake:

wrong:

with open(t_pem.name, 'wb') as f_pem:

right:

with open(t_pem.name, 'wb') as pem_file:

from contextlib import contextmanager
from pathlib import Path
from tempfile import NamedTemporaryFile

import requests
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates


@contextmanager
def pfx_to_pem(pfx_path, pfx_password):
    ''' Decrypts the .pfx file to be used with requests. '''
    pfx = Path(pfx_path).read_bytes()
    private_key, main_cert, add_certs = load_key_and_certificates(pfx, pfx_password.encode('utf-8'), None)

    with NamedTemporaryFile(suffix='.pem') as t_pem:
        with open(t_pem.name, 'wb') as pem_file:
            pem_file.write(private_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()))
            pem_file.write(main_cert.public_bytes(Encoding.PEM))
            for ca in add_certs:
                pem_file.write(ca.public_bytes(Encoding.PEM))
        yield t_pem.name

# HOW TO USE:
# with pfx_to_pem('foo.pem', 'bar') as cert:
#     requests.post(url, cert=cert, data=payload)

@shanmugara
Copy link

shanmugara commented Jul 18, 2021

Thank you. This helped me.

@sivanagaraju
Copy link

sivanagaraju commented Aug 2, 2021

For me still getting the 401 Error.
I tried to connect to API using browser its working fine. I am not sure what i am missing. I am using cryptography

@OnescuAlex-Vlad
Copy link

OnescuAlex-Vlad commented Dec 9, 2021

Thank you, saved a lot of time!

@trevorboydsmith
Copy link

trevorboydsmith commented Dec 15, 2021

i recommend putting a license statement at the top. if you choose not to put a license statement then read: https://choosealicense.com/no-permission/ . thanks. cheers.

@JoajEnergiDanmark
Copy link

JoajEnergiDanmark commented Aug 10, 2022

Thanks a lot! Precisely what I needed!

@mahesh3359
Copy link

mahesh3359 commented Sep 6, 2022

could someone help me with a full example, in my case, I am reading the pfx file from Azure key vault, so I dont have a way to specify the path of the pfx file, unless, I create a temp file,

so I am looking for an example which is something like, converting pfx file which is in memory(read from upstream) and then convert in to certificate(X-509) and a private key to pass along with a request,

thanks, for the help

@claudep
Copy link

claudep commented Sep 6, 2022

If you have an in-memory bytestring, then you just don't need to read them from a file (what Path(pfx_path).read_bytes() is doing), as load_key_and_certificates expects bytes in its first argument.

@mahesh3359
Copy link

mahesh3359 commented Sep 6, 2022

with open(t_pem.name, 'wb') as f_pem:

Hi I was talking about with open(t_pem.name, 'wb') as f_pem: this line

@claudep
Copy link

claudep commented Sep 6, 2022

And I was referring to my version (see comment of May 21) which doesn't use deprecated code.

@mahesh3359
Copy link

mahesh3359 commented Sep 6, 2022

And I was referring to my version (see comment of May 21) which doesn't use deprecated code.

@claudep thanks for responding, this is how I am retrieving pfx cert, could you help me with the flow for the next steps, once I have the certificate. how to use this certificate for making API request,

certificate_client = CertificateClient(vault_url=KVUri, credential=credential)
certificate = certificate_client.get_certificate("CERT_KEYVAULT_IN_PFX")

@claudep
Copy link

claudep commented Sep 6, 2022

Sorry, I don't know this CertificateClient stuff. Once you have the in-memory bytes, you should be able to feed them to load_key_and_certificates. I'm afraid at this stage you will have to test/debug things yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment