Skip to content

Commit abc59fb

Browse files
committed
Migrate from deprecated pyopenssl calls
Fixes: DeprecationWarning: CSR support in pyOpenSSL is deprecated. You should use the APIs in cryptography. Cryptography does not support the insecure md5 hash algorithm. It is unclear whether AFIP supports any secure algorithm; this needs to be tested by registering a new certificate on the web UI.
1 parent 2ecadc0 commit abc59fb

File tree

2 files changed

+56
-32
lines changed

2 files changed

+56
-32
lines changed

django_afip/crypto.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
from typing import IO
44

5+
from cryptography import x509
56
from cryptography.hazmat.primitives import hashes
7+
from cryptography.hazmat.primitives.asymmetric import rsa
68
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
79
from cryptography.hazmat.primitives.serialization import Encoding
10+
from cryptography.hazmat.primitives.serialization import NoEncryption
11+
from cryptography.hazmat.primitives.serialization import PrivateFormat
812
from cryptography.hazmat.primitives.serialization import load_pem_private_key
913
from cryptography.hazmat.primitives.serialization.pkcs7 import PKCS7Options
1014
from cryptography.hazmat.primitives.serialization.pkcs7 import PKCS7SignatureBuilder
1115
from cryptography.x509 import load_pem_x509_certificate
12-
from OpenSSL import crypto
16+
from cryptography.x509.oid import NameOID
1317

1418
from django_afip import exceptions
1519

@@ -41,10 +45,18 @@ def create_embeded_pkcs7_signature(data: bytes, cert: bytes, key: bytes) -> byte
4145

4246
def create_key(file_: IO[bytes]) -> None:
4347
"""Create a key and write it into ``file_``."""
44-
pkey = crypto.PKey()
45-
pkey.generate_key(crypto.TYPE_RSA, 2048)
48+
private_key = rsa.generate_private_key(
49+
public_exponent=65537,
50+
key_size=2048,
51+
)
4652

47-
file_.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
53+
file_.write(
54+
private_key.private_bytes(
55+
encoding=Encoding.PEM,
56+
format=PrivateFormat.PKCS8,
57+
encryption_algorithm=NoEncryption(),
58+
)
59+
)
4860
file_.flush()
4961

5062

@@ -56,16 +68,20 @@ def create_csr(
5668
file_: IO[bytes],
5769
) -> None:
5870
"""Create a certificate signing request and write it into ``file_``."""
59-
key = crypto.load_privatekey(crypto.FILETYPE_PEM, key_file.read())
60-
61-
req = crypto.X509Req()
62-
subj = req.get_subject()
63-
64-
subj.O = organization_name
65-
subj.CN = common_name
66-
subj.serialNumber = serial_number # type: ignore[attr-defined]
67-
68-
req.set_pubkey(key)
69-
req.sign(key, "md5")
71+
private_key = load_pem_private_key(key_file.read(), password=None)
72+
73+
csr = (
74+
x509.CertificateSigningRequestBuilder()
75+
.subject_name(
76+
x509.Name(
77+
[
78+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization_name),
79+
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
80+
x509.NameAttribute(NameOID.SERIAL_NUMBER, serial_number),
81+
]
82+
)
83+
)
84+
.sign(private_key, hashes.SHA256()) # type: ignore[arg-type]
85+
)
7086

71-
file_.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
87+
file_.write(csr.public_bytes(Encoding.PEM))

tests/test_taxpayer.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
from datetime import datetime
44

55
import pytest
6+
from cryptography import x509
7+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
8+
from cryptography.x509.oid import NameOID
69
from factory.django import FileField
710
from freezegun import freeze_time
8-
from OpenSSL import crypto
11+
from OpenSSL.crypto import X509
912

1013
from django_afip import factories
1114

@@ -20,8 +23,8 @@ def test_key_generation() -> None:
2023
assert key.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
2124
assert key.splitlines()[-1] == "-----END PRIVATE KEY-----"
2225

23-
loaded_key = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
24-
assert isinstance(loaded_key, crypto.PKey)
26+
loaded_key = load_pem_private_key(key.encode(), password=None)
27+
assert loaded_key is not None
2528

2629

2730
def test_dont_overwrite_keys() -> None:
@@ -48,8 +51,8 @@ def test_overwrite_keys_force() -> None:
4851
assert key.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
4952
assert key.splitlines()[-1] == "-----END PRIVATE KEY-----"
5053

51-
loaded_key = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
52-
assert isinstance(loaded_key, crypto.PKey)
54+
loaded_key = load_pem_private_key(key.encode(), password=None)
55+
assert loaded_key is not None
5356

5457

5558
@freeze_time(datetime.fromtimestamp(1489537017))
@@ -62,26 +65,31 @@ def test_csr_generation() -> None:
6265
csr = csr_file.read().decode()
6366

6467
assert csr.splitlines()[0] == "-----BEGIN CERTIFICATE REQUEST-----"
65-
6668
assert csr.splitlines()[-1] == "-----END CERTIFICATE REQUEST-----"
6769

68-
loaded_csr = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr.encode())
69-
assert isinstance(loaded_csr, crypto.X509Req)
70-
71-
expected_components = [
72-
(b"O", b"John Smith"),
73-
(b"CN", b"djangoafip1489537017"),
74-
(b"serialNumber", b"CUIT 20329642330"),
75-
]
70+
loaded_csr = x509.load_pem_x509_csr(csr.encode())
71+
assert isinstance(loaded_csr, x509.CertificateSigningRequest)
7672

77-
assert expected_components == loaded_csr.get_subject().get_components()
73+
subject = loaded_csr.subject
74+
assert (
75+
subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)[0].value
76+
== "John Smith"
77+
)
78+
assert (
79+
subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
80+
== "djangoafip1489537017"
81+
)
82+
assert (
83+
subject.get_attributes_for_oid(NameOID.SERIAL_NUMBER)[0].value
84+
== "CUIT 20329642330"
85+
)
7886

7987

8088
def test_certificate_object() -> None:
8189
taxpayer = factories.TaxPayerFactory.build()
8290
cert = taxpayer.certificate_object
8391

84-
assert isinstance(cert, crypto.X509)
92+
assert isinstance(cert, X509)
8593

8694

8795
def test_null_certificate_object() -> None:

0 commit comments

Comments
 (0)