Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 32 additions & 16 deletions django_afip/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

from typing import IO

from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization import NoEncryption
from cryptography.hazmat.primitives.serialization import PrivateFormat
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.serialization.pkcs7 import PKCS7Options
from cryptography.hazmat.primitives.serialization.pkcs7 import PKCS7SignatureBuilder
from cryptography.x509 import load_pem_x509_certificate
from OpenSSL import crypto
from cryptography.x509.oid import NameOID

from django_afip import exceptions

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

def create_key(file_: IO[bytes]) -> None:
"""Create a key and write it into ``file_``."""
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 2048)
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)

file_.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
file_.write(
private_key.private_bytes(
encoding=Encoding.PEM,
format=PrivateFormat.PKCS8,
encryption_algorithm=NoEncryption(),
)
)
file_.flush()


Expand All @@ -56,16 +68,20 @@ def create_csr(
file_: IO[bytes],
) -> None:
"""Create a certificate signing request and write it into ``file_``."""
key = crypto.load_privatekey(crypto.FILETYPE_PEM, key_file.read())

req = crypto.X509Req()
subj = req.get_subject()

subj.O = organization_name
subj.CN = common_name
subj.serialNumber = serial_number # type: ignore[attr-defined]

req.set_pubkey(key)
req.sign(key, "md5")
private_key = load_pem_private_key(key_file.read(), password=None)

csr = (
x509.CertificateSigningRequestBuilder()
.subject_name(
x509.Name(
[
x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization_name),
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
x509.NameAttribute(NameOID.SERIAL_NUMBER, serial_number),
]
)
)
.sign(private_key, hashes.SHA256()) # type: ignore[arg-type]
)

file_.write(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
file_.write(csr.public_bytes(Encoding.PEM))
39 changes: 24 additions & 15 deletions tests/test_taxpayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
from datetime import datetime

import pytest
from cryptography import x509
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509.oid import NameOID
from factory.django import FileField
from freezegun import freeze_time
from OpenSSL import crypto
from OpenSSL.crypto import X509

from django_afip import factories

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

loaded_key = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
assert isinstance(loaded_key, crypto.PKey)
loaded_key = load_pem_private_key(key, password=None)
assert loaded_key is not None


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

loaded_key = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
assert isinstance(loaded_key, crypto.PKey)
loaded_key = load_pem_private_key(key, password=None)
assert loaded_key is not None


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

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

loaded_csr = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr)
assert isinstance(loaded_csr, crypto.X509Req)
loaded_csr = x509.load_pem_x509_csr(csr)
assert isinstance(loaded_csr, x509.CertificateSigningRequest)

expected_components = [
(b"O", b"John Smith"),
(b"CN", b"djangoafip1489537017"),
(b"serialNumber", b"CUIT 20329642330"),
]

assert expected_components == loaded_csr.get_subject().get_components()
subject = loaded_csr.subject
assert (
subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)[0].value
== "John Smith"
)
assert (
subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
== "djangoafip1489537017"
)
assert (
subject.get_attributes_for_oid(NameOID.SERIAL_NUMBER)[0].value
== "CUIT 20329642330"
)


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

assert isinstance(cert, crypto.X509)
assert isinstance(cert, X509)


def test_null_certificate_object() -> None:
Expand Down