I need to send requests to a SOAP API, and as part of that process, I need to hash my XML request using SHA256 and then sign it with an RSA key (which I have in the form of a PCKS12 certificate). I've tried using the Crypto library like this:
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
with open("/my/cert/file.p12", "rb") as f:
cert_data = f.read()
private_key = RSA.import_key(cert_data, passphrase="some_passphrase")
signer = pkcs1_15.new(private_key)
hash = SHA256.new(xml_data)
signature = signer.sign(hashed)
However, when I try testing this code, I always get the following error from the Crypto library:
security/crypto.py:25: in __init__
private_key = RSA.import_key(cert.certificate, passphrase=cert.passphrase)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/PublicKey/RSA.py:851: in import_key
return _import_keyDER(extern_key, passphrase)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/PublicKey/RSA.py:746: in _import_keyDER
return decoding(extern_key, passphrase)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/PublicKey/RSA.py:697: in _import_pkcs1_private
der = DerSequence().decode(encoded, nr_elements=9, only_ints_expected=True)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/Util/asn1.py:610: in decode
result = DerObject.decode(self, der_encoded, strict=strict)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/Util/asn1.py:228: in decode
self._decodeFromStream(s, strict)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/Util/asn1.py:623: in _decodeFromStream
DerObject._decodeFromStream(self, s, strict)
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/Util/asn1.py:245: in _decodeFromStream
length = self._decodeLen(s)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <Crypto.Util.asn1.DerSequence object at 0x7f03288f97d0>
s = <Crypto.Util.asn1.BytesIO_EOF object at 0x7f0328890310>
def _decodeLen(self, s):
"""Decode DER length octets from a file."""
length = s.read_byte()
if length > 127:
encoded_length = s.read(length & 0x7F)
> if bord(encoded_length[0]) == 0:
E IndexError: index out of range
/Yv1Cv_rJ-py3.11/lib/python3.11/site-packages/Crypto/Util/asn1.py:205: IndexError
It's clear that I'm not importing this certificate correctly but I'm not sure how, because I can successfully import it with the cryptography library:
import cryptography.hazmat.primitives.serialization.pkcs12
with open("/my/cert/file.p12", "rb") as f:
private_key, certificate, additional = pkcs12.load_key_and_certificates(
f.read(), b"some_passphrase",
)
I notice that the private_key here also has a sign method, but calling it is more complicated so I'd rather avoid it if possible.
TL;DR: What's the "proper" way to use a PCKS12 key to produce a signature from a data hash in Python?