Skip to content

Commit 0fd81cc

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 2af8f59 commit 0fd81cc

File tree

2 files changed

+68
-40
lines changed

2 files changed

+68
-40
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: 36 additions & 24 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

@@ -15,12 +18,13 @@ def test_key_generation() -> None:
1518
taxpayer = factories.TaxPayerFactory.build(key=None)
1619
taxpayer.generate_key()
1720

18-
key = taxpayer.key.file.read().decode()
19-
assert key.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
20-
assert key.splitlines()[-1] == "-----END PRIVATE KEY-----"
21+
key = taxpayer.key.file.read()
22+
key_str = key.decode()
23+
assert key_str.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
24+
assert key_str.splitlines()[-1] == "-----END PRIVATE KEY-----"
2125

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

2529

2630
def test_dont_overwrite_keys() -> None:
@@ -39,14 +43,15 @@ def test_overwrite_keys_force() -> None:
3943
taxpayer = factories.TaxPayerFactory.build(key__data=text)
4044

4145
taxpayer.generate_key(force=True)
42-
key = taxpayer.key.file.read().decode()
46+
key = taxpayer.key.file.read()
47+
key_str = key.decode()
4348

4449
assert text != key
45-
assert key.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
46-
assert key.splitlines()[-1] == "-----END PRIVATE KEY-----"
50+
assert key_str.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
51+
assert key_str.splitlines()[-1] == "-----END PRIVATE KEY-----"
4752

48-
loaded_key = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
49-
assert isinstance(loaded_key, crypto.PKey)
53+
loaded_key = load_pem_private_key(key, password=None)
54+
assert loaded_key is not None
5055

5156

5257
@freeze_time(datetime.fromtimestamp(1489537017))
@@ -56,29 +61,36 @@ def test_csr_generation() -> None:
5661
taxpayer.generate_key()
5762

5863
csr_file = taxpayer.generate_csr()
59-
csr = csr_file.read().decode()
64+
csr = csr_file.read()
65+
csr_str = csr.decode()
6066

61-
assert csr.splitlines()[0] == "-----BEGIN CERTIFICATE REQUEST-----"
67+
assert csr_str.splitlines()[0] == "-----BEGIN CERTIFICATE REQUEST-----"
6268

63-
assert csr.splitlines()[-1] == "-----END CERTIFICATE REQUEST-----"
69+
assert csr_str.splitlines()[-1] == "-----END CERTIFICATE REQUEST-----"
6470

65-
loaded_csr = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr)
66-
assert isinstance(loaded_csr, crypto.X509Req)
71+
loaded_csr = x509.load_pem_x509_csr(csr)
72+
assert isinstance(loaded_csr, x509.CertificateSigningRequest)
6773

68-
expected_components = [
69-
(b"O", b"John Smith"),
70-
(b"CN", b"djangoafip1489537017"),
71-
(b"serialNumber", b"CUIT 20329642330"),
72-
]
73-
74-
assert expected_components == loaded_csr.get_subject().get_components()
74+
subject = loaded_csr.subject
75+
assert (
76+
subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)[0].value
77+
== "John Smith"
78+
)
79+
assert (
80+
subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
81+
== "djangoafip1489537017"
82+
)
83+
assert (
84+
subject.get_attributes_for_oid(NameOID.SERIAL_NUMBER)[0].value
85+
== "CUIT 20329642330"
86+
)
7587

7688

7789
def test_certificate_object() -> None:
7890
taxpayer = factories.TaxPayerFactory.build()
7991
cert = taxpayer.certificate_object
8092

81-
assert isinstance(cert, crypto.X509)
93+
assert isinstance(cert, X509)
8294

8395

8496
def test_null_certificate_object() -> None:

0 commit comments

Comments
 (0)