Skip to content

Commit 066a130

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 066a130

File tree

2 files changed

+56
-31
lines changed

2 files changed

+56
-31
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 & 15 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

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

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

2528

2629
def test_dont_overwrite_keys() -> None:
@@ -45,8 +48,8 @@ def test_overwrite_keys_force() -> None:
4548
assert key.splitlines()[0] == "-----BEGIN PRIVATE KEY-----"
4649
assert key.splitlines()[-1] == "-----END PRIVATE KEY-----"
4750

48-
loaded_key = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
49-
assert isinstance(loaded_key, crypto.PKey)
51+
loaded_key = load_pem_private_key(key, password=None)
52+
assert loaded_key is not None
5053

5154

5255
@freeze_time(datetime.fromtimestamp(1489537017))
@@ -62,23 +65,29 @@ def test_csr_generation() -> None:
6265

6366
assert csr.splitlines()[-1] == "-----END CERTIFICATE REQUEST-----"
6467

65-
loaded_csr = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr)
66-
assert isinstance(loaded_csr, crypto.X509Req)
68+
loaded_csr = x509.load_pem_x509_csr(csr)
69+
assert isinstance(loaded_csr, x509.CertificateSigningRequest)
6770

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()
71+
subject = loaded_csr.subject
72+
assert (
73+
subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)[0].value
74+
== "John Smith"
75+
)
76+
assert (
77+
subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
78+
== "djangoafip1489537017"
79+
)
80+
assert (
81+
subject.get_attributes_for_oid(NameOID.SERIAL_NUMBER)[0].value
82+
== "CUIT 20329642330"
83+
)
7584

7685

7786
def test_certificate_object() -> None:
7887
taxpayer = factories.TaxPayerFactory.build()
7988
cert = taxpayer.certificate_object
8089

81-
assert isinstance(cert, crypto.X509)
90+
assert isinstance(cert, X509)
8291

8392

8493
def test_null_certificate_object() -> None:

0 commit comments

Comments
 (0)