Skip to content

Conversation

@stktyagi
Copy link
Member

@stktyagi stktyagi commented Dec 27, 2025

Replaced PyOpenSSL with cryptography with retained functionalities.

Fixes #131

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #131.

Description of Changes

Migrated the cryptographic engine from PyOpenSSL to the cryptography library and resolved the renewal bug by fixing the serial number update order.

Summary by CodeRabbit

  • Refactor

    • Migrated all certificate operations from OpenSSL to the cryptography library, including parsing, generation, signing, validation, renewal, CRL building, extension handling, and improved human-readable certificate rendering.
  • New Features

    • Certificate text output improved and additional extension types supported; CA renewal now propagates to issued certificates with serial synchronization.
  • Chores

    • Removed pyOpenSSL dependency in favor of cryptography.
  • Tests

    • Updated tests for cryptography objects and OID-based assertions; added UTC datetime helper and expanded renewal, serial-sync, and extension coverage.

✏️ Tip: You can customize this high-level summary in your review settings.

Replaced PyOpenSSL with cryptography with retained functionalities.

Fixes #131
@stktyagi
Copy link
Member Author

Closes #131 #193

@coveralls
Copy link

coveralls commented Dec 27, 2025

Coverage Status

coverage: 95.411% (-3.9%) from 99.359%
when pulling ea11aed on issues/131-drop-pyopenssl
into 2066447 on master.

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick review

PEM validation now catches generic Exception, which can hide the real cause of failures and make debugging harder.

The use of default_backend() is deprecated in recent cryptography versions and may cause warnings or issues in the future.

The issuer check (cert.issuer == ca_cert.subject) may be too strict and could reject otherwise valid certificates due to encoding or ordering differences.

Extension parsing is somewhat permissive and may silently ignore unsupported or malformed values instead of failing fast.

What do you think?

@stktyagi
Copy link
Member Author

stktyagi commented Dec 27, 2025

@nemesifier
thanks for the review, before I add a commit in references to these changes, this is how I am thinkning about those concerns. Might save some time for you to check than checking the commit directly.

Instead of a generic except Exception, I can update the validation to catch only ValueError, TypeError, and UnsupportedAlgorithm. In that case unrelated logic or system errors would bubble up with a proper traceback. I also rechecked that i had a redundant except Exception block in the private key condition that was masking similar issues, so I'll fix that too.

Yeah, I have removed all references to default_backend() to not cause any issues.

This was a bit tricky, you are totally right here. I initially tried rfc4514_string(), but shell testing confirmed it is still sequence-dependent and doesn't solve the ordering issue. Comparing canonical sets of attributes regardless position would help here, like:

issuer_identity = set((attr.oid, str(attr.value).strip().lower()) for attr in cert.issuer)
ca_identity = set((attr.oid, str(attr.value).strip().lower()) for attr in ca_cert.subject)

We can make it failfast by checking immediately for required structure and supported values, like:

 for ext in self.extensions:
    if not all(k in ext for k in ("name", "critical", "value")):
        raise ValidationError("Each extension must have 'name', 'critical', and 'value'.")

    if ext["name"] == "extendedKeyUsage":
        allowed = ["serverAuth", "clientAuth"]
        provided = [v.strip() for v in ext["value"].split(",")]
        for p in provided:
            if p not in allowed:
                raise ValidationError(f"Unsupported EKU: {p}")

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exhaustive but I was able to scan at least 50% of the code.

@github-project-automation github-project-automation bot moved this from To do (general) to In progress in OpenWISP Contributor's Board Dec 30, 2025
@nemesifier
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Replaces pyOpenSSL usage with the cryptography library across models, tests, CRL generation, PEM validation, certificate building/signing, verification, extension handling, and renewal propagation; requirements drop pyOpenSSL.

Changes

Cohort / File(s) Summary
Core model migration
django_x509/base/models.py
Reimplemented certificate/key handling with cryptography: x509 returns cryptography.x509.Certificate; pkey loads serialization.load_pem_private_key; _generate() uses CertificateBuilder + rsa.generate_private_key; _get_subject() replaces _fill_subject() using NameOID; _add_extensions() now accepts/returns a CertificateBuilder and attaches BasicConstraints, KeyUsage, SubjectKeyIdentifier, AuthorityKeyIdentifier, ExtendedKeyUsage, nsComment, nsCertType; _validate_pem(), _import(), _verify_ca() and CRL building/signing migrated to cryptography; renewal propagation updated to prefer issued_certificates then cert_set.
Tests updated to cryptography
django_x509/tests/test_ca.py, django_x509/tests/test_cert.py, django_x509/tests/__init__.py
Tests converted from pyOpenSSL to cryptography APIs: use .subject/.issuer and NameOID attribute access, extension retrieval via get_extension_for_class/get_extension_for_oid, serial/validity via cryptography properties; added UTC_TIME/GENERALIZED_TIME constants and datetime_to_string helper; updated renewal and serial-sync expectations and PEM/key loading assertions.
Dependency removal
requirements.txt
Removed pyOpenSSL~=25.3.0 from requirements.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Model as Model method
  participant Builder as CertificateBuilder
  participant Crypto as Cryptography primitives
  participant CAKey as CA Private Key
  participant DB as Database

  rect rgba(0,128,96,0.08)
  Note over Model,Builder: Certificate generation / renewal (cryptography)
  Model->>Builder: call _get_subject(), set subject, issuer, serial, validity
  Model->>Crypto: generate or load private/public key
  Model->>Builder: add public_key
  Model->>Builder: call _add_extensions(builder, public_key)
  Builder->>CAKey: sign(builder, chosen hash algorithm)
  CAKey-->>Builder: cryptography.x509.Certificate (signed)
  Builder->>DB: persist PEM (certificate ± private key)
  DB-->>Model: persisted certificate record
  end

  alt Renewal with issued certs
    Note over Model: propagate renew to children
    Model->>Model: iterate issued_certificates / cert_set
    Model->>Builder: regenerate child certs signed by renewed CA
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I hopped from pyOpenSSL to cryptography's glen,
NameOIDs in my paw and builders in my den,
I signed little keys, chased expiries anew,
Renewed tiny certs — a bright carrot-peek view,
PEMs snug like clover, every fingerprint true.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.66% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: migrating from PyOpenSSL to cryptography as referenced in issue #131.
Description check ✅ Passed The description follows the template and provides key information: migration scope, referenced issue, completed checklist items, and acknowledgment of outstanding documentation updates.
Linked Issues check ✅ Passed The PR successfully addresses issue #131's coding requirements: migrates from pyOpenSSL to cryptography, preserves existing functionality, and fixes the renewal serial number bug.
Out of Scope Changes check ✅ Passed All changes are directly related to the migration objectives: replacement of OpenSSL operations with cryptography equivalents, test updates, dependency changes, and helper functions for the new implementation.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8ecfc3 and ea11aed.

📒 Files selected for processing (1)
  • django_x509/base/models.py
🔇 Additional comments (10)
django_x509/base/models.py (10)

6-10: LGTM! Cryptography imports are well-organized.

The migration to the cryptography library is properly reflected in the imports, with all necessary modules, exceptions, and OIDs included.


190-218: LGTM! Certificate property methods correctly use cryptography APIs.

The x509 and x509_text properties have been properly migrated to use cryptography's certificate loading and attribute access patterns.


220-230: LGTM! Private key loading correctly migrated to cryptography.

The pkey property properly uses serialization.load_pem_private_key with appropriate password handling.


232-261: LGTM! PEM validation uses specific exception handling.

The validation correctly catches specific exceptions (ValueError, TypeError, UnsupportedAlgorithm) and provides appropriate error messages. The error message parsing to distinguish passphrase from key errors is a reasonable approach.


275-347: LGTM! Certificate generation correctly uses cryptography's CertificateBuilder.

The _generate method properly:

  • Generates RSA keys with appropriate parameters
  • Constructs the certificate builder with all required fields
  • Maps digest algorithms correctly
  • Signs the certificate with the appropriate key and algorithm
  • Serializes both certificate and private key with proper encryption

The _get_subject method correctly maps model attributes to x509.NameAttribute objects.


349-445: LGTM! Certificate import and CA verification properly migrated.

The _import method correctly extracts certificate attributes using cryptography's API, including proper handling of email addresses from either SAN or subject.

The _verify_ca method has been properly fixed per previous review feedback:

  • Order-independent issuer/CA comparison using set-based attribute matching
  • Proper exception chaining with from None
  • Signature verification correctly handles RSA and EC keys using appropriate padding/algorithms

461-597: LGTM! Extension handling implements fail-fast validation as intended.

The _add_extensions method has been properly updated per previous review feedback:

  • extendedKeyUsage: Validates all values and raises ValidationError for unknown entries
  • nsComment: Validates length (max 255 bytes) and requires a value
  • nsCertType: Properly maps values to bit representations and validates them
  • Fallback: Raises ValidationError for any unsupported extension types

The fail-fast approach ensures that misconfigurations are caught early rather than silently ignored.


599-606: LGTM! Certificate renewal logic is correct.

The renew method properly:

  • Generates a new serial number
  • Updates validity end date based on whether it's a CA or certificate
  • Regenerates the certificate using the new cryptography-based flow

632-642: LGTM! CA renewal propagation handles different relationship names.

The CA renewal method correctly:

  • Calls the parent renew() to renew the CA itself
  • Uses getattr with fallback to handle both issued_certificates and cert_set relationships
  • Propagates renewal to all child certificates

644-679: LGTM! CRL generation correctly uses cryptography APIs.

The crl property properly:

  • Loads the CA certificate and private key with password handling
  • Constructs the CRL using CertificateRevocationListBuilder
  • Adds revoked certificates with appropriate extensions
  • Signs the CRL with the CA's private key
  • Returns the CRL in PEM format

Comment @coderabbitai help to get the list of available commands and usage tips.

@openwisp openwisp deleted a comment from coderabbitai bot Dec 31, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
django_x509/tests/test_cert.py (1)

16-24: DRY violation: Same duplication as in test_ca.py.

As noted in past review comments, these time constants and helper function should be moved to a shared module.

django_x509/base/models.py (1)

444-449: Replace broad except Exception with specific cryptography exceptions.

This duplicates the concern from past review comments. The broad exception catch can mask unrelated errors. Use specific exceptions like InvalidSignature from cryptography.exceptions.

🔎 Proposed fix
+from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm

# ... in _verify_ca method:
         try:
             if isinstance(ca_pubkey, rsa.RSAPublicKey):
                 ca_pubkey.verify(
                     cert.signature,
                     cert.tbs_certificate_bytes,
                     padding.PKCS1v15(),
                     cert.signature_hash_algorithm,
                 )
             elif isinstance(ca_pubkey, ec.EllipticCurvePublicKey):
                 ca_pubkey.verify(
                     cert.signature,
                     cert.tbs_certificate_bytes,
                     ec.ECDSA(cert.signature_hash_algorithm),
                 )
             else:
                 ca_pubkey.verify(cert.signature, cert.tbs_certificate_bytes)
-        except Exception:
+        except InvalidSignature:
             raise ValidationError(
                 _("Cryptographic signature verification failed: CA does not match.")
-            )
+            ) from None
django_x509/tests/test_ca.py (1)

20-33: DRY violation: datetime_to_string and time constants are duplicated.

Per the past review comment, these constants (generalized_time, utc_time) and the datetime_to_string function are duplicated between test_ca.py and test_cert.py. They should be moved to a shared location like __init__.py.

🔎 Proposed refactor - move to django_x509/tests/__init__.py

In django_x509/tests/__init__.py:

generalized_time = "%Y%m%d%H%M%SZ"
utc_time = "%y%m%d%H%M%SZ"

def datetime_to_string(datetime_):
    if datetime_.year < 2050:
        return datetime_.strftime(utc_time)
    return datetime_.strftime(generalized_time)

Then import in both test files:

from . import TestX509Mixin, datetime_to_string, generalized_time, utc_time
🧹 Nitpick comments (3)
django_x509/base/models.py (2)

618-650: Remove default_backend() usage in CRL generation for consistency.

Similar to the earlier comment, default_backend() is deprecated and should be removed for consistency with modern cryptography library usage.

🔎 Proposed fix
     def crl(self):
         """
         Returns up to date CRL of this CA
         """
-        backend = default_backend()
         ca_cert = x509.load_pem_x509_certificate(self.certificate.encode())
-        # import ipdb; ipdb.set_trace()
-        pkey_kwargs = {"backend": backend, "password": None}
+        pkey_kwargs = {"password": None}
         if self.passphrase:
             pkey_kwargs["password"] = self.passphrase.encode()
         private_key = serialization.load_pem_private_key(
             self.private_key.encode(), **pkey_kwargs
         )
         now = timezone.now()
         builder = (
             x509.CertificateRevocationListBuilder()
             .issuer_name(ca_cert.subject)
             .last_update(now)
             .next_update(now + timedelta(days=1))
         )
         for cert in self.get_revoked_certs():
             revoked = (
                 x509.RevokedCertificateBuilder()
                 .serial_number(int(cert.serial_number))
                 .revocation_date(now)
                 .add_extension(
                     x509.CRLReason(x509.ReasonFlags.unspecified),
                     critical=False,
                 )
-                .build(backend)
+                .build()
             )
             builder = builder.add_revoked_certificate(revoked)
         crl = builder.sign(
             private_key=private_key,
             algorithm=hashes.SHA256(),
-            backend=backend,
         )
         return crl.public_bytes(encoding=serialization.Encoding.PEM)

Also, there's a leftover debug comment # import ipdb; ipdb.set_trace() at line 620 that should be removed.


197-199: Remove default_backend() call to align with cryptography library best practices.

In cryptography ≥3.1, default_backend() is no longer required—the backend is automatically provided. The project uses cryptography ~46.0.3, which supports calling the API without the backend parameter. The import of default_backend can also be removed.

Proposed change
         if self.certificate:
             return x509.load_pem_x509_certificate(
-                self.certificate.encode("utf-8"), default_backend()
+                self.certificate.encode("utf-8")
             )

Also remove the import:

-from cryptography.hazmat.backends import default_backend
django_x509/tests/test_ca.py (1)

211-253: Consider using direct attribute assignment instead of setattr.

Per static analysis (B010), using setattr with a constant attribute is no safer than direct assignment. While this pattern is common in tests for temporarily modifying settings, direct assignment is more readable.

🔎 Example refactor
     def test_basic_constraints_not_critical(self):
-        setattr(app_settings, "CA_BASIC_CONSTRAINTS_CRITICAL", False)
+        app_settings.CA_BASIC_CONSTRAINTS_CRITICAL = False
         ca = self._create_ca()
         ext = ca.x509.extensions.get_extension_for_class(x509.BasicConstraints)
         self.assertFalse(ext.critical)
-        setattr(app_settings, "CA_BASIC_CONSTRAINTS_CRITICAL", True)
+        app_settings.CA_BASIC_CONSTRAINTS_CRITICAL = True
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5888b00 and 399e0cf.

📒 Files selected for processing (4)
  • django_x509/base/models.py
  • django_x509/tests/test_ca.py
  • django_x509/tests/test_cert.py
  • requirements.txt
💤 Files with no reviewable changes (1)
  • requirements.txt
🧰 Additional context used
🧬 Code graph analysis (1)
django_x509/tests/test_cert.py (4)
django_x509/base/models.py (8)
  • x509 (192-199)
  • _verify_ca (401-449)
  • pkey (224-233)
  • x509_text (202-221)
  • _get_subject (330-352)
  • save (183-189)
  • renew (568-575)
  • renew (601-611)
django_x509/tests/__init__.py (3)
  • TestX509Mixin (23-66)
  • _create_cert (43-66)
  • _create_ca (24-41)
django_x509/models.py (1)
  • Cert (16-23)
tests/openwisp2/sample_x509/models.py (1)
  • Cert (22-28)
🪛 Gitleaks (8.30.0)
django_x509/tests/test_ca.py

[high] 65-74: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 Ruff (0.14.10)
django_x509/tests/test_ca.py

215-215: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


218-218: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


222-222: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


225-225: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


229-229: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


239-239: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


243-243: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


246-246: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


253-253: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)

django_x509/tests/test_cert.py

236-236: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


240-240: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


243-243: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


248-250: Do not call setattr with a constant attribute value. It is not any safer than normal property access.

Replace setattr with assignment

(B010)


472-472: Possible hardcoded password assigned to argument: "passphrase"

(S106)

django_x509/base/models.py

244-244: Do not catch blind exception: Exception

(BLE001)


263-263: Do not catch blind exception: Exception

(BLE001)


427-429: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


446-446: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (13)
django_x509/base/models.py (4)

6-11: LGTM - Proper cryptography imports for the migration.

The imports correctly bring in the necessary cryptography components for certificate handling, including x509 module, hash algorithms, serialization, asymmetric key types, and OIDs.


280-328: LGTM - Clean migration to cryptography CertificateBuilder.

The _generate method properly:

  • Clears cached properties before regeneration
  • Uses rsa.generate_private_key with appropriate parameters
  • Builds certificates using x509.CertificateBuilder with correct subject/issuer handling
  • Maps digest algorithms correctly
  • Handles passphrase encryption with BestAvailableEncryption

330-352: LGTM - Subject construction using NameOID attributes.

The _get_subject method correctly maps model fields to X.509 NameOIDs and only includes non-empty values. The string coercion handles foreign key redefinition in subclasses.


607-611: LGTM - CA renewal properly propagates to child certificates.

The renewal logic correctly falls back between issued_certificates and cert_set relations and renews all child certificates with the updated CA.

django_x509/tests/test_ca.py (4)

65-74: Test private key is acceptable for testing purposes.

The Gitleaks static analysis flags this as a private key exposure. However, this is intentionally a test-only private key used for import testing and poses no security risk. This is a false positive that can be safely ignored.


76-109: LGTM - Test correctly validates cryptography-based CA creation.

The test properly verifies:

  • Serial number matches between model and x509 object
  • BasicConstraints path_length is set correctly
  • Subject attributes are extracted via OID-based lookups
  • Issuer equals subject for self-signed CA
  • Certificate version is v3
  • BasicConstraints extension is critical and marks CA as true

637-668: LGTM - Comprehensive CA renewal test with child certificate verification.

The test properly validates:

  • CA certificate, key, and validity_end are updated after renewal
  • All child certificates are renewed with new certificates, keys, and serial numbers
  • Validity end dates are extended

693-700: LGTM - Good addition to verify serial number synchronization.

This test ensures the serial number stored in the model matches the serial number embedded in the PEM certificate after renewal, catching potential bugs in the renewal order.

django_x509/tests/test_cert.py (5)

96-132: LGTM - Comprehensive certificate creation test with cryptography.

The test properly validates:

  • Certificate and private key are generated
  • Serial number and version are correct
  • Subject attributes match model fields via OID lookups
  • Issuer matches CA subject
  • CA signature verification works
  • BasicConstraints marks it as non-CA

268-285: LGTM - Extension test correctly validates nsCertType and extendedKeyUsage.

The test verifies:

  • nsCertType extension is added with correct OID and value
  • extendedKeyUsage extension has correct criticality
  • CLIENT_AUTH OID is present in extended key usage

Note: This test will need updating if the nsCertType fix (to respect the input value) is implemented in models.py - currently it checks for hardcoded b"\x03\x02\x07\x80" which matches the hardcoded implementation.


324-342: LGTM - Good test coverage for _get_subject with None attributes.

This test properly verifies that:

  • Only set attributes appear in the subject
  • Empty/unset attributes are excluded
  • Dynamically adding attributes works correctly

402-424: Good: Added proper failure assertion for serial clash test.

The else: self.fail(...) clause ensures the test fails if no ValidationError is raised, making the test more robust.


477-498: LGTM - Certificate renewal test properly verifies CA is unchanged.

The test correctly validates:

  • Certificate, key, validity_end, and serial_number are updated
  • The associated CA remains unchanged after cert renewal
  • Uses refresh_from_db() to ensure database state is verified

stktyagi and others added 3 commits December 31, 2025 17:02
…131

Centralized time format constants and datetime_to_string helper in tests/__init__.py, improved Exception Handling, deprecation cleanup.

Fixes #131
Replaced hardcoded client bitstring with dynamic bitmask mapping.

Fixes #131
@stktyagi
Copy link
Member Author

Extension parsing is somewhat permissive and may silently ignore unsupported or malformed values instead of failing fast.

Will address this in next commit.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
django_x509/base/models.py (1)

447-459: Consider adding value validation in extension format check.

The current implementation only validates the structure of extensions (dict with required keys) but doesn't validate the values. While _add_extensions performs some validation later (e.g., for nsCertType), catching invalid extension values early would provide better error messages to users and fail faster.

🔎 Suggested approach

Consider adding validation for extension values in this method:

  • Validate that name is a supported extension type
  • Validate that value is appropriate for the extension type
  • Validate that critical is a boolean

This would catch configuration errors earlier in the flow rather than during certificate generation.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 200a938 and a8ecfc3.

📒 Files selected for processing (1)
  • django_x509/base/models.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.0.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.0.0
🔇 Additional comments (2)
django_x509/base/models.py (2)

619-629: LGTM: CA renewal with child certificate propagation.

The renewal logic correctly handles both the CA and its issued certificates. The use of getattr to support both issued_certificates and cert_set provides backward compatibility while allowing for the relationship name to be customized.


631-667: LGTM: CRL generation migrated correctly to cryptography.

The CRL generation has been properly migrated to use the cryptography library's CertificateRevocationListBuilder. The implementation correctly handles password-protected private keys, builds revoked certificate entries, and signs the CRL.

Replaced permissive filtering with explicit validation for EKU and nsCertType.

Fixes #131
Comment on lines 21 to 23
KEY_LENGTH_CHOICES = (
("512", "512"),
("1024", "1024"),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
KEY_LENGTH_CHOICES = (
("512", "512"),
("1024", "1024"),
KEY_LENGTH_CHOICES = (
("224", "224"),
("256", "256"),
("384", "384"),
("512", "512"),
("1024", "1024"),

now that we are switching to cryptography and using cert.signature_hash_algorithm.name.lower() just by adding these key length values to the KEY_LENGTH_CHOICES should also directly solve #118

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @stktyagi, thanks also to @davidjmemmett for this support on this!

@nemesifier nemesifier merged commit 7f43fa8 into master Jan 10, 2026
20 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in OpenWISP Contributor's Board Jan 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

[change] Drop pyOpenSSL in favour of cryptography

4 participants