HOWTO: Migrating from M2Crypto to PyCA/cryptography
Introduction
PyCA/cryptography is a package which provides cryptographic recipes and primitives to Python developers.
This document has instructions on how to migrate from M2Crypto to PyCA/cryptography for features that are currently supported.
S/MIME
Signing
If your application does S/MIME signing, it can be migrated to
PyCA/cryptography by using
the PKCS7
API, particularly the PKCS7SignatureBuilder
class. Below
is an example migration showcasing the equivalent APIs and parameters in
PyCA/cryptography.
M2Crypto
from M2Crypto import BIO, SMIME
s = SMIME.SMIME()
s.load_key('../tests/signer_key.pem', '../tests/signer.pem')
data = b'data'
buf = BIO.MemoryBuffer(data)
p7 = s.sign(buf, SMIME.PKCS7_DETACHED)
out = BIO.MemoryBuffer()
buf = BIO.MemoryBuffer(data)
s.write(out, p7, buf)
print(out.read())
PyCA/cryptography
from cryptography.hazmat.primitives.serialization import load_pem_private_key, pkcs7, Encoding
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.x509 import load_pem_x509_certificate
with open('../tests/signer_key.pem', 'rb') as key_data:
key = load_pem_private_key(key_data.read(), password=None)
with open('../tests/signer.pem', 'rb') as cert_data:
cert = load_pem_x509_certificate(cert_data.read())
output = pkcs7.PKCS7SignatureBuilder().set_data(
b"data"
).add_signer(
cert, key, hashes.SHA512(), rsa_padding=padding.PKCS1v15()
).sign(
Encoding.SMIME, [pkcs7.PKCS7Options.DetachedSignature]
)
print(output)
RSA
Following are migration examples for common operations with RSA key pairs.
The documentation for the relevant PyCA/cryptography
APIs can be found
here.
Signing and verifying
M2Crypto
from M2Crypto import RSA
import hashlib
message = b"This is the message string"
digest = hashlib.sha1(message).digest()
key = RSA.load_key('../tests/rsa.priv.pem')
signature = key.sign(digest, algo='sha1')
assert key.verify(digest, signature, algo='sha1') == 1
print(signature.hex())
PyCA/cryptography
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
with open('../tests/rsa.priv.pem', 'rb') as key_data:
key = load_pem_private_key(key_data.read(), password=None)
message = b"This is the message string"
signature = key.sign(message, padding.PKCS1v15(), hashes.SHA1())
public_key = key.public_key()
public_key.verify(signature, message, padding.PKCS1v15(), hashes.SHA1())
print(signature.hex())
Encrypting and decrypting
M2Crypto
from M2Crypto import RSA
message = b"This is the message string"
key = RSA.load_key('../tests/rsa.priv.pem')
cipher_text = key.public_encrypt(message, RSA.pkcs1_padding)
plain_text = key.private_decrypt(cipher_text, RSA.pkcs1_padding)
assert plain_text == message
PyCA/cryptography
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
with open('../tests/rsa.priv.pem', 'rb') as key_data:
key = load_pem_private_key(key_data.read(), password=None)
message = b"This is the message string"
public_key = key.public_key()
cipher_text = public_key.encrypt(message, padding.PKCS1v15())
plain_text = key.decrypt(cipher_text, padding.PKCS1v15())
assert plain_text == message
X.509 certificates
Following are migration examples for common operations with X.509 certificates.
The documentation for the relevant PyCA/cryptography
APIs can be found
here.
Loading and examining certificates
M2Crypto
from M2Crypto import X509
cert = X509.load_cert('../tests/x509.pem')
print(cert.get_issuer())
print(cert.get_subject())
print(cert.get_not_before())
print(cert.get_not_after())
PyCA/cryptography
from cryptography import x509
with open('../tests/x509.pem', 'rb') as cert_data:
cert = x509.load_pem_x509_certificate(cert_data.read())
print(cert.issuer)
print(cert.subject)
print(cert.not_valid_before_utc)
print(cert.not_valid_after_utc)
Signature verification
Note that this example only checks that the signature of a certificate matches a public key, as described in the OpenSSL documentation. For complete X.509 path validation see PyCA/cryptography’s X.509 verification module.
M2Crypto
from M2Crypto import X509
cert = X509.load_cert('../tests/x509.pem')
cacert = X509.load_cert("../tests/ca.pem")
assert cert.verify(cacert.get_pubkey()) == 1
PyCA/cryptography
from cryptography.x509 import load_pem_x509_certificate
with open('../tests/ca.pem', 'rb') as cacert_data:
cacert = load_pem_x509_certificate(cacert_data.read())
with open('../tests/x509.pem', 'rb') as cert_data:
cert = load_pem_x509_certificate(cert_data.read())
cert.verify_directly_issued_by(cacert)