cert-utils 0.2.0__tar.gz → 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cert_utils-0.2.0 → cert_utils-1.0.1}/CHANGES.txt +25 -0
- cert_utils-1.0.1/PKG-INFO +208 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/setup.py +4 -3
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/__init__.py +2 -1
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/compat/cryptography.py +11 -5
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/compat/openssl.py +179 -23
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/compat/pyopenssl.py +3 -2
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/conditionals.py +21 -9
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/core.py +441 -206
- cert_utils-1.0.1/src/cert_utils/model.py +139 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/scripts/cert_info.py +2 -1
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/utils.py +13 -136
- cert_utils-1.0.1/src/cert_utils.egg-info/PKG-INFO +208 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils.egg-info/SOURCES.txt +2 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils.egg-info/requires.txt +1 -1
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/_utils.py +18 -10
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/draft-acme-ari/edge_case_generate.py +2 -1
- cert_utils-1.0.1/tests/test_data/unit_tests/account_002/private_key.pem +51 -0
- cert_utils-1.0.1/tests/test_data/unit_tests/account_003/private_key.pem +6 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_unit.py +116 -32
- cert_utils-0.2.0/PKG-INFO +0 -211
- cert_utils-0.2.0/src/cert_utils/model.py +0 -103
- cert_utils-0.2.0/src/cert_utils.egg-info/PKG-INFO +0 -211
- {cert_utils-0.2.0 → cert_utils-1.0.1}/LICENSE.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/MANIFEST.in +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/README.md +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/pyproject.toml +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/setup.cfg +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/compat/deprecated.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/convert.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/errors.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e5-cross.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e5.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e6-cross.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e6.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e7-cross.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e7.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e8-cross.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e8.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e9-cross.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/e9.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/fakeleintermediatex1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/fakelerootx1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/isrg-root-ocsp-x1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/isrg-root-x1-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/isrg-root-x2-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/isrg-root-x2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/isrgrootx1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/isrgrootx1.pkix +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-e1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-e2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-r3-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-r3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-r4-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-r4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-x1-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-x2-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-x3-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/lets-encrypt-x4-cross-signed.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/letsencryptauthorityx1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/letsencryptauthorityx2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/letsencryptauthorityx3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/letsencryptauthorityx4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/r10.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/r11.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/r12.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/r13.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/r14.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/trustid-x3-root.p7c +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt-certs/trustid-x3-root.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/letsencrypt_info.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils/py.typed +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils.egg-info/dependency_links.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils.egg-info/entry_points.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils.egg-info/not-zip-safe +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/src/cert_utils.egg-info/top_level.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/__init__.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/__README__.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/__README__.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/alternate_chains/1/chain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/alternate_chains/1/fullchain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/alternate_chains/2/chain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/alternate_chains/2/fullchain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/cert.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/chain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/fullchain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/alternate_chains/1/privkey.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/draft-acme-ari/appendix_a-cert.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/draft-acme-ari/cert--all.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/draft-acme-ari/cert--issuer+serial.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/draft-acme-ari/cert--key_id.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-ec/ec384-1-key.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-ec/ec384-1.csr +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-ec/ec384-2-key.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-5.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-cycle-account_daily.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-cycle-account_weekly.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-cycle-global_daily.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-cycle-global_weekly.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/AcmeAccountKey-cycle-single_certificate.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/acme_account_1.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/acme_account_2.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/acme_account_3.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/acme_account_4.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/acme_account_5.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/private_1.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/private_2.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/private_3.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/private_4.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/private_5.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_1-server.crt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_1-server.csr +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_1-server.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_2-server.crt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_2-server.csr +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_2-server.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_3-server.crt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_3-server.csr +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_3-server.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_4-server.crt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_4-server.csr +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_4-server.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_5-server.crt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_5-server.csr +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/key_technology-rsa/selfsigned_5-server.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/__README__.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/_data.json +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/_demo.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/cert.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/chain_0.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/chain_0_bad.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/chain_1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/chain_2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/root_0.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/root_1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/TestA/root_2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/long_chains/__README__.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/cert1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/cert2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/cert3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/cert4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/cert5.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/cert6.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/chain1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/chain2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/chain3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/chain4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/chain5.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/chain6.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/fullchain1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/fullchain2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/fullchain3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/fullchain4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/fullchain5.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/fullchain6.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/privkey1.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/privkey2.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/privkey3.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/privkey4.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/privkey5.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/pebble-certs/privkey6.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/_support/account.key +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/_support/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/account_001/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/account_001/meta.json +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/account_001/private_key.json +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/account_001/private_key.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/account_001/regr.json +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/cert.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/chain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/csr.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/fullchain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/privkey.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_001/test.py +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_002/csr.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_002/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_002/privkey.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_003/cert.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_003/csr.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_003/fullchain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_003/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_003/privkey.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_004/cert.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_004/csr.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_004/fullchain.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_004/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_004/privkey.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_005/csr.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_005/info.txt +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tests/test_data/unit_tests/cert_005/privkey.pem +0 -0
- {cert_utils-0.2.0 → cert_utils-1.0.1}/tox.ini +0 -0
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
1.0.1
|
|
2
|
+
* logging cleanup
|
|
3
|
+
* pinned "josepy<2" in advance of breaking changes;
|
|
4
|
+
no planned changes should affect this library
|
|
5
|
+
* updated pre-commit configuration
|
|
6
|
+
- upgraged black to psf distribution
|
|
7
|
+
- added flake8
|
|
8
|
+
|
|
9
|
+
1.0.0
|
|
10
|
+
* new_account_key supports LetsEncrypt updates to EC
|
|
11
|
+
* ALLOWED_CURVES_ECDSA is now a list of strings
|
|
12
|
+
* new_ec_key named argument is now curve(str)
|
|
13
|
+
* functions now require KeyTechnologyEnum, not KeyTechnology for key_technology_id
|
|
14
|
+
* added account_key__verify
|
|
15
|
+
* extended account_key__sign to accept `standardize_signature`; default True
|
|
16
|
+
for converting the EC signature to the encoding ACME requires
|
|
17
|
+
* corrected use of `secp256k1` to `secp256r1`
|
|
18
|
+
* `key_technology` concept of "RSA" and "EC" has been renamed to `key_technology_basic`
|
|
19
|
+
* `key_technology` concept is now a tuple of `(key_technology_basic, (data, ))`
|
|
20
|
+
For a RSA, the payload is:
|
|
21
|
+
("rsa", (rsa_bits,))
|
|
22
|
+
For an EC, the payload is:
|
|
23
|
+
("ec", (nist_curve_name,))
|
|
24
|
+
* validate_domains now accepts a wildcard
|
|
25
|
+
|
|
1
26
|
0.2.0
|
|
2
27
|
* replaced pyopenssl with cryptography.
|
|
3
28
|
pyopenssl was originally used because certbot required it and this package
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cert_utils
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: TLS/SSL Certiicate Utilities
|
|
5
|
+
Home-page: https://github.com/aptise/cert_utils
|
|
6
|
+
Author: Jonathan Vanasco
|
|
7
|
+
Author-email: jonathan@findmeon.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Description: 
|
|
10
|
+
|
|
11
|
+
cert_utils
|
|
12
|
+
==========
|
|
13
|
+
|
|
14
|
+
**cert_utils** offers support for common operations when dealing with SSL
|
|
15
|
+
Certificates, specifically within the LetsEncrypt ecosystem.
|
|
16
|
+
|
|
17
|
+
This library was originally developed as a toolkit for bugfixing and
|
|
18
|
+
troubleshooting large ACME installations.
|
|
19
|
+
|
|
20
|
+
**cert_utils** will attempt to process operations with Python via the modern
|
|
21
|
+
[Cryptography](https://cryptography.io/en/latest/) package when possible.
|
|
22
|
+
If the required Python libraries are not installed, it will fallback to using
|
|
23
|
+
OpenSSL commandline via subprocesses. **cert_utils** does a bit of work to
|
|
24
|
+
standardize certificate operations across versions of Python and OpenSSL that
|
|
25
|
+
do not share the same inputs, outputs or invocations.
|
|
26
|
+
|
|
27
|
+
**cert_utils** was formerly part of the
|
|
28
|
+
**[peter_sslers](https://github.com/aptise/peter_sslers)** ACME Client and
|
|
29
|
+
Certificate Management System, and has been descoped into it's own library.
|
|
30
|
+
|
|
31
|
+
This library *does not* process Certificates and Certificate Data itself.
|
|
32
|
+
Instead, it offers a simplified API to invoke other libraries and extract data
|
|
33
|
+
from Certificates. It was designed for developers and system administrators to
|
|
34
|
+
more easily use the various libraries to accomplish specific tasks on the
|
|
35
|
+
commandline or as part of other projects.
|
|
36
|
+
|
|
37
|
+
This library now includes a utility, `cert_info`:
|
|
38
|
+
|
|
39
|
+
Examples CLI:
|
|
40
|
+
-------------
|
|
41
|
+
|
|
42
|
+
The CLI tool `cert_info` can parse a cert and pull the active ARI info.
|
|
43
|
+
|
|
44
|
+
The tool accepts a filepath to a local file OR a https URL to be inspected live:
|
|
45
|
+
|
|
46
|
+
> cert_info https://letsencrypt.org
|
|
47
|
+
|
|
48
|
+
This will gnerate the output:
|
|
49
|
+
|
|
50
|
+
********************************************************************************
|
|
51
|
+
********************************************************************************
|
|
52
|
+
Process URL:
|
|
53
|
+
url: https://letsencrypt.org
|
|
54
|
+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
55
|
+
Connection Data:
|
|
56
|
+
{'cert_dict': {'OCSP': ('http://e6.o.lencr.org',),
|
|
57
|
+
'caIssuers': ('http://e6.i.lencr.org/',),
|
|
58
|
+
'issuer': ((('countryName', 'US'),),
|
|
59
|
+
(('organizationName', "Let's Encrypt"),),
|
|
60
|
+
(('commonName', 'E6'),)),
|
|
61
|
+
'notAfter': 'Mar 7 14:44:04 2025 GMT',
|
|
62
|
+
'notBefore': 'Dec 7 14:44:05 2024 GMT',
|
|
63
|
+
'serialNumber': '04C7A73A361AC02B67BDBD6AF210285B1CC7',
|
|
64
|
+
'subject': ((('commonName', 'letsencrypt.org'),),),
|
|
65
|
+
'subjectAltName': (('DNS', 'lencr.org'),
|
|
66
|
+
('DNS', 'letsencrypt.com'),
|
|
67
|
+
('DNS', 'letsencrypt.org'),
|
|
68
|
+
('DNS', 'www.lencr.org'),
|
|
69
|
+
('DNS', 'www.letsencrypt.com'),
|
|
70
|
+
('DNS', 'www.letsencrypt.org')),
|
|
71
|
+
'version': 3},
|
|
72
|
+
'cert_pem': '-----BEGIN CERTIFICATE-----\n'
|
|
73
|
+
'MIID1DCCA1qgAwIBAgISBMenOjYawCtnvb1q8hAoWxzHMAoGCCqGSM49BAMDMDIx\n'
|
|
74
|
+
'CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF\n'
|
|
75
|
+
'NjAeFw0yNDEyMDcxNDQ0MDVaFw0yNTAzMDcxNDQ0MDRaMBoxGDAWBgNVBAMTD2xl\n'
|
|
76
|
+
'dHNlbmNyeXB0Lm9yZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDgHHaIeMwYz\n'
|
|
77
|
+
'APO9ZgfeGansUeFRN0sW4K23nrVppRXfjHvN+83vtH170hshCrIAZRJCL+CPcA2N\n'
|
|
78
|
+
'UqmLiSdM/M+jggJmMIICYjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYB\n'
|
|
79
|
+
'BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFD2MLtcnwBWM\n'
|
|
80
|
+
'FukcX4wMfYCQUCMAMB8GA1UdIwQYMBaAFJMnRpgDqVFojpjWxEJI2yO/WJTSMFUG\n'
|
|
81
|
+
'CCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL2U2Lm8ubGVuY3Iub3Jn\n'
|
|
82
|
+
'MCIGCCsGAQUFBzAChhZodHRwOi8vZTYuaS5sZW5jci5vcmcvMG8GA1UdEQRoMGaC\n'
|
|
83
|
+
'CWxlbmNyLm9yZ4IPbGV0c2VuY3J5cHQuY29tgg9sZXRzZW5jcnlwdC5vcmeCDXd3\n'
|
|
84
|
+
'dy5sZW5jci5vcmeCE3d3dy5sZXRzZW5jcnlwdC5jb22CE3d3dy5sZXRzZW5jcnlw\n'
|
|
85
|
+
'dC5vcmcwEwYDVR0gBAwwCjAIBgZngQwBAgEwggEEBgorBgEEAdZ5AgQCBIH1BIHy\n'
|
|
86
|
+
'APAAdgB9WR4S4XgqexxhZ3xe/fjQh1wUoE6VnrkDL9kOjC55uAAAAZOhyXh9AAAE\n'
|
|
87
|
+
'AwBHMEUCIAXoVc0BRK6ELL0Q1lHv+QbrobnBiPthcr2E7g0IulkHAiEAlVkiVkF9\n'
|
|
88
|
+
'7KeR/At3XdJNRYbJda0iKaKtqKy0KH7igZgAdgATSt8atZhCCXgMb+9MepGkFrcj\n'
|
|
89
|
+
'Sc5YV2rfrtqnwqvgIgAAAZOhyXk0AAAEAwBHMEUCIQD+yhm/6lwMMWyxBaB15Mkh\n'
|
|
90
|
+
'g3tsQCtr0DOD4uvAzc63FAIgHgjXduGCbB/3VTtRCGWfmkWSS9z+CjfO1O6hkzGW\n'
|
|
91
|
+
'XBowCgYIKoZIzj0EAwMDaAAwZQIxAKVLzxW03Kk8qJMBtWQCw5dgSHNWOGG9Oq03\n'
|
|
92
|
+
'XOGr8092sx9Ezz3XsXWeq8JvokFjhAIwCZOX/UjxdHfgV/O6gkiNARQ1FokDyfS0\n'
|
|
93
|
+
'ClynPk9psMTPVl5QWgYJzjVrJagAjtJm\n'
|
|
94
|
+
'-----END CERTIFICATE-----\n',
|
|
95
|
+
'peername': ('100.28.201.155', 443)}
|
|
96
|
+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
97
|
+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
98
|
+
Cert Data:
|
|
99
|
+
ARI ID: kydGmAOpUWiOmNbEQkjbI79YlNI.BMenOjYawCtnvb1q8hAoWxzH
|
|
100
|
+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
101
|
+
Requesting ARI
|
|
102
|
+
ari_url: https://acme-v02.api.letsencrypt.org/draft-ietf-acme-ari-03/renewalInfo/kydGmAOpUWiOmNbEQkjbI79YlNI.BMenOjYawCtnvb1q8hAoWxzH
|
|
103
|
+
{'Server': 'nginx', 'Date': 'Tue, 10 Dec 2024 18:29:04 GMT', 'Content-Type': 'application/json', 'Content-Length': '101', 'Connection': 'keep-alive', 'Cache-Control': 'public, max-age=0, no-cache', 'Link': '<https://acme-v02.api.letsencrypt.org/directory>;rel="index"', 'Retry-After': '21600', 'X-Frame-Options': 'DENY', 'Strict-Transport-Security': 'max-age=604800'}
|
|
104
|
+
- - - - - - - - - -
|
|
105
|
+
{'suggestedWindow': {'end': '2025-02-06T15:03:34Z',
|
|
106
|
+
'start': '2025-02-04T15:03:34Z'}}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
Examples API:
|
|
110
|
+
-------------
|
|
111
|
+
|
|
112
|
+
For example, `cert_utils.parse_cert` returns a Python dict of key fields in a
|
|
113
|
+
certificate. This can make writing a script to analyze large directories of
|
|
114
|
+
certificates fairly simple.
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### Parse a Leaf/End-Entity
|
|
118
|
+
|
|
119
|
+
Example Script:
|
|
120
|
+
|
|
121
|
+
```!python
|
|
122
|
+
import cert_utils
|
|
123
|
+
import pprint
|
|
124
|
+
|
|
125
|
+
cert_path = "./tests/test_data/unit_tests/cert_001/cert.pem"
|
|
126
|
+
cert_pem = open(cert_path, 'r').read()
|
|
127
|
+
data = cert_utils.parse_cert(cert_pem)
|
|
128
|
+
pprint.pprint(data)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Result:
|
|
132
|
+
|
|
133
|
+
{'SubjectAlternativeName': ['a.example.com',
|
|
134
|
+
'b.example.com',
|
|
135
|
+
'c.example.com',
|
|
136
|
+
'd.example.com'],
|
|
137
|
+
'authority_key_identifier': 'D159010094B0A62ADBABE54B2321CA1B6EBA93E7',
|
|
138
|
+
'enddate': datetime.datetime(2025, 6, 16, 20, 19, 30),
|
|
139
|
+
'fingerprint_sha1': 'F63C5C66B52551EEDADF7CE44301D646680B8F5D',
|
|
140
|
+
'issuer': 'CN=Pebble Intermediate CA 601ea1',
|
|
141
|
+
'issuer_uri': None,
|
|
142
|
+
'key_technology': 'RSA',
|
|
143
|
+
'spki_sha256': '34E67CC615761CBADAF430B2E02E0EC39C99EEFC73CCE469B18AE54A37EF6942',
|
|
144
|
+
'startdate': datetime.datetime(2020, 6, 16, 20, 19, 30),
|
|
145
|
+
'subject': 'CN=a.example.com'}
|
|
146
|
+
|
|
147
|
+
The payload contains `SubjectAlternativeName` listing all the domains, along
|
|
148
|
+
with `enddate` and `startdate` in Python datetime objects for easy comparison.
|
|
149
|
+
|
|
150
|
+
### Parse a Trusted Root
|
|
151
|
+
|
|
152
|
+
Example Script:
|
|
153
|
+
|
|
154
|
+
```!python
|
|
155
|
+
import cert_utils
|
|
156
|
+
import pprint
|
|
157
|
+
|
|
158
|
+
cert_path = "./src/cert_utils/letsencrypt-certs/isrgrootx1.pem"
|
|
159
|
+
cert_pem = open(cert_path, 'r').read()
|
|
160
|
+
data = cert_utils.parse_cert(cert_pem)
|
|
161
|
+
pprint.pprint(data)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Result:
|
|
165
|
+
|
|
166
|
+
{'SubjectAlternativeName': None,
|
|
167
|
+
'authority_key_identifier': None,
|
|
168
|
+
'enddate': datetime.datetime(2035, 6, 4, 11, 4, 38),
|
|
169
|
+
'fingerprint_sha1': 'CABD2A79A1076A31F21D253635CB039D4329A5E8',
|
|
170
|
+
'issuer': 'C=US\nO=Internet Security Research Group\nCN=ISRG Root X1',
|
|
171
|
+
'issuer_uri': None,
|
|
172
|
+
'key_technology': 'RSA',
|
|
173
|
+
'spki_sha256': '0B9FA5A59EED715C26C1020C711B4F6EC42D58B0015E14337A39DAD301C5AFC3',
|
|
174
|
+
'startdate': datetime.datetime(2015, 6, 4, 11, 4, 38),
|
|
175
|
+
'subject': 'C=US\nO=Internet Security Research Group\nCN=ISRG Root X1'}
|
|
176
|
+
|
|
177
|
+
The payload on Trusted Roots is identical.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
Why does this exist?
|
|
181
|
+
--------------------
|
|
182
|
+
|
|
183
|
+
The [peter_sslers](https://github.com/aptise/peter_sslers) project was designed
|
|
184
|
+
to deploy on a wide variety of production servers that did not share common
|
|
185
|
+
Python and OpenSSL installations. Earlier versions of the library
|
|
186
|
+
(within peter_sslers) supported both Python2.7 and Python3, as it was common to
|
|
187
|
+
encounter a machine that did not have Python3 installed. Although it is still
|
|
188
|
+
common to find these machines, Python2.7 was dropped to take advantage of
|
|
189
|
+
typing. Depending on the version of OpenSSL installed on a system,
|
|
190
|
+
**cert_utils** will invoke the binary or regex the output to bridge support
|
|
191
|
+
through a unified interface.
|
|
192
|
+
|
|
193
|
+
Keywords: web
|
|
194
|
+
Platform: UNKNOWN
|
|
195
|
+
Classifier: Intended Audience :: Developers
|
|
196
|
+
Classifier: Programming Language :: Python
|
|
197
|
+
Classifier: Programming Language :: Python :: 3
|
|
198
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
199
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
200
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
201
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
202
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
203
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
204
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
205
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
206
|
+
Requires-Python: >=3.7
|
|
207
|
+
Description-Content-Type: text/markdown
|
|
208
|
+
Provides-Extra: testing
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""metadata_utils installation script.
|
|
2
2
|
"""
|
|
3
|
+
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
5
6
|
|
|
@@ -21,12 +22,12 @@ requires = [
|
|
|
21
22
|
"python-dateutil",
|
|
22
23
|
"psutil>=4.4.0", # for Python2/3 compat
|
|
23
24
|
"requests",
|
|
24
|
-
"typing_extensions",
|
|
25
|
+
"typing_extensions", # Literal, Required, TypeDict
|
|
25
26
|
]
|
|
26
27
|
tests_require = [
|
|
27
28
|
"certbot",
|
|
28
29
|
"cryptography>=42.0.0",
|
|
29
|
-
"josepy",
|
|
30
|
+
"josepy<2",
|
|
30
31
|
"mypy",
|
|
31
32
|
"pytest",
|
|
32
33
|
"types-psutil",
|
|
@@ -46,7 +47,7 @@ setup(
|
|
|
46
47
|
author="Jonathan Vanasco",
|
|
47
48
|
author_email="jonathan@findmeon.com",
|
|
48
49
|
zip_safe=False,
|
|
49
|
-
python_requires=">=3.
|
|
50
|
+
python_requires=">=3.7",
|
|
50
51
|
keywords="web",
|
|
51
52
|
install_requires=requires,
|
|
52
53
|
tests_require=tests_require,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from .convert import * # noqa: F401, F403
|
|
2
2
|
from .core import * # noqa: F401, F403
|
|
3
|
+
from .model import * # noqa: F401, F403
|
|
3
4
|
from .utils import * # noqa: F401, F403
|
|
4
5
|
|
|
5
6
|
# ==============================================================================
|
|
6
7
|
|
|
7
|
-
__VERSION__ = "0.
|
|
8
|
+
__VERSION__ = "1.0.1"
|
|
@@ -11,6 +11,8 @@ from typing import Union
|
|
|
11
11
|
# locals
|
|
12
12
|
from .. import conditionals
|
|
13
13
|
from ..utils import CERT_PEM_REGEX
|
|
14
|
+
from ..utils import curve_to_nist
|
|
15
|
+
from ..utils import TECHNOLOGY_RETURN_VALUES
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
16
18
|
from cryptography.x509 import Certificate
|
|
@@ -52,20 +54,24 @@ def _format_cryptography_components(
|
|
|
52
54
|
|
|
53
55
|
def _cryptography__public_key_technology(
|
|
54
56
|
publickey: "_TYPES_CRYPTOGRAPHY_PUBLICKEY",
|
|
55
|
-
) -> Optional[
|
|
57
|
+
) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
56
58
|
"""
|
|
57
59
|
:param key: key object
|
|
58
60
|
:type key: instance of
|
|
59
|
-
:returns:
|
|
60
|
-
|
|
61
|
+
:returns: If the key is valid, it will return a Tuple wherein
|
|
62
|
+
- The first element is the Key's technology (EC, RSA)
|
|
63
|
+
- The second element is a Tuple with the key's type.
|
|
64
|
+
(RSA, (bits, ))
|
|
65
|
+
(EC, (curve_name, ))
|
|
61
66
|
"""
|
|
62
67
|
if TYPE_CHECKING:
|
|
63
68
|
assert conditionals.crypto_rsa is not None
|
|
64
69
|
assert conditionals.crypto_ec is not None
|
|
65
70
|
if isinstance(publickey, conditionals.crypto_rsa.RSAPublicKey):
|
|
66
|
-
return "RSA"
|
|
71
|
+
return ("RSA", (publickey.key_size,))
|
|
67
72
|
elif isinstance(publickey, conditionals.crypto_ec.EllipticCurvePublicKey):
|
|
68
|
-
|
|
73
|
+
curve_name = curve_to_nist(publickey.curve.name)
|
|
74
|
+
return ("EC", (curve_name,))
|
|
69
75
|
return None
|
|
70
76
|
|
|
71
77
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# stdlib
|
|
2
2
|
import binascii
|
|
3
|
+
import re
|
|
3
4
|
import subprocess
|
|
4
5
|
from typing import List
|
|
5
6
|
from typing import Optional
|
|
@@ -18,10 +19,59 @@ from ..errors import OpenSslError_InvalidCSR
|
|
|
18
19
|
from ..errors import OpenSslError_InvalidKey
|
|
19
20
|
from ..errors import OpenSslError_VersionTooLow
|
|
20
21
|
from ..utils import new_pem_tempfile
|
|
22
|
+
from ..utils import TECHNOLOGY_RETURN_VALUES
|
|
21
23
|
|
|
22
24
|
# ==============================================================================
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
# note the conditional whitespace before/after `CN`
|
|
28
|
+
# this is because of differing openssl versions
|
|
29
|
+
RE_openssl_x509_subject = re.compile(r"Subject:.*? CN ?= ?([^\s,;/]+)")
|
|
30
|
+
RE_openssl_x509_san = re.compile(
|
|
31
|
+
r"X509v3 Subject Alternative Name: ?\n +([^\n]+)\n?", re.MULTILINE | re.DOTALL
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# openssl 3 does not have "keyid:" as a prefix
|
|
36
|
+
# a keyid prefix is okay!
|
|
37
|
+
# we do not want the alternates, which are uri+serial; but take that out in results
|
|
38
|
+
RE_openssl_x509_authority_key_identifier = re.compile(
|
|
39
|
+
r"X509v3 Authority Key Identifier: ?\n +(?:keyid:)?([^\n]+)\n?",
|
|
40
|
+
re.MULTILINE | re.DOTALL,
|
|
41
|
+
)
|
|
42
|
+
# we have a potential line in there for the OSCP or something else.
|
|
43
|
+
RE_openssl_x509_issuer_uri = re.compile(
|
|
44
|
+
r"Authority Information Access: ?\n(?:[^\n]*^\n)? +CA Issuers - URI:([^\n]+)\n?",
|
|
45
|
+
re.MULTILINE | re.DOTALL,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
RE_openssl_x509_serial = re.compile(r"Serial Number: ?(\d+)")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# depending on openssl version, the "Public key: " text might list the bits
|
|
52
|
+
# it may or may not also have a dash in the phrase "Public Key"
|
|
53
|
+
# it may or may not be prefaced with the PublicKey type
|
|
54
|
+
RE_openssl_x509_keytype_rsa = re.compile(
|
|
55
|
+
r"Subject Public Key Info:\n"
|
|
56
|
+
r"\s+Public Key Algorithm: rsaEncryption\n"
|
|
57
|
+
r"\s+(?:RSA )?Public[\-\ ]?Key: \((\d+) bits?\)",
|
|
58
|
+
re.MULTILINE,
|
|
59
|
+
)
|
|
60
|
+
RE_openssl_x509_keytype_ec = re.compile(
|
|
61
|
+
r"Subject Public Key Info:\n"
|
|
62
|
+
r"\s+Public Key Algorithm: id-ecPublicKey\n"
|
|
63
|
+
r"\s+(?:EC )?Public[\-\ ]?Key: \((\d+) bits?\)\n"
|
|
64
|
+
r"\s+pub:\n"
|
|
65
|
+
r"[\s\d\w\:]+"
|
|
66
|
+
r"(?:"
|
|
67
|
+
r"(?:\s+ASN1 OID: ([\w]+)\n)"
|
|
68
|
+
r"|"
|
|
69
|
+
r"(?:\s+NIST CURVE: ([\-\w]+)\n)"
|
|
70
|
+
r")",
|
|
71
|
+
re.MULTILINE,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
25
75
|
def _cleanup_openssl_modulus(data: str) -> str:
|
|
26
76
|
data = data.strip()
|
|
27
77
|
if data[:8] == "Modulus=":
|
|
@@ -144,13 +194,13 @@ def _openssl_cert__normalize_pem(cert_pem: str) -> str:
|
|
|
144
194
|
|
|
145
195
|
|
|
146
196
|
def _openssl_spki_hash_cert(
|
|
147
|
-
|
|
197
|
+
key_technology_basic: str = "",
|
|
148
198
|
cert_pem_filepath: str = "",
|
|
149
199
|
as_b64: Optional[bool] = None,
|
|
150
200
|
) -> str:
|
|
151
201
|
"""
|
|
152
|
-
:param
|
|
153
|
-
:type
|
|
202
|
+
:param key_technology_basic: Is the key an "EC" or "RSA" key?
|
|
203
|
+
:type key_technology_basic: str
|
|
154
204
|
:param cert_pem_filepath: REQUIRED filepath to PEM Certificate.
|
|
155
205
|
Used for commandline OpenSSL fallback operations.
|
|
156
206
|
:type cert_pem_filepath: str
|
|
@@ -162,13 +212,13 @@ def _openssl_spki_hash_cert(
|
|
|
162
212
|
The OpenSSL Equivalent / Fallback is::
|
|
163
213
|
|
|
164
214
|
openssl x509 -pubkey -noout -in {CERT_FILEPATH} | \
|
|
165
|
-
openssl {
|
|
215
|
+
openssl {key_technology_basic} -pubout -outform DER -pubin | \
|
|
166
216
|
openssl dgst -sha256 -binary | \
|
|
167
217
|
openssl enc -base64
|
|
168
218
|
"""
|
|
169
|
-
if
|
|
170
|
-
raise ValueError("must submit `
|
|
171
|
-
|
|
219
|
+
if key_technology_basic not in ("EC", "RSA"):
|
|
220
|
+
raise ValueError("must submit `key_technology_basic`")
|
|
221
|
+
key_technology_basic = key_technology_basic.lower()
|
|
172
222
|
if not cert_pem_filepath:
|
|
173
223
|
raise FallbackError_FilepathRequired("Must submit `cert_pem_filepath`.")
|
|
174
224
|
if core.openssl_version is None:
|
|
@@ -195,7 +245,7 @@ def _openssl_spki_hash_cert(
|
|
|
195
245
|
p2 = psutil.Popen(
|
|
196
246
|
[
|
|
197
247
|
core.openssl_path,
|
|
198
|
-
|
|
248
|
+
key_technology_basic,
|
|
199
249
|
"-pubin",
|
|
200
250
|
"-pubout",
|
|
201
251
|
"-outform",
|
|
@@ -261,13 +311,13 @@ def _openssl_spki_hash_cert(
|
|
|
261
311
|
|
|
262
312
|
|
|
263
313
|
def _openssl_spki_hash_csr(
|
|
264
|
-
|
|
314
|
+
key_technology_basic: str = "",
|
|
265
315
|
csr_pem_filepath: str = "",
|
|
266
316
|
as_b64: Optional[bool] = None,
|
|
267
317
|
) -> str:
|
|
268
318
|
"""
|
|
269
|
-
:param
|
|
270
|
-
:type
|
|
319
|
+
:param key_technology_basic: Is the key an "EC" or "RSA" key?
|
|
320
|
+
:type key_technology_basic: str
|
|
271
321
|
:param csr_pem_filepath: REQUIRED filepath to PEM CSR.
|
|
272
322
|
Used for commandline OpenSSL fallback operations.
|
|
273
323
|
:type csr_pem_filepath: str
|
|
@@ -279,13 +329,13 @@ def _openssl_spki_hash_csr(
|
|
|
279
329
|
The OpenSSL Equivalent / Fallback is::
|
|
280
330
|
|
|
281
331
|
openssl REQ -pubkey -noout -in {CSR_FILEPATH} | \
|
|
282
|
-
openssl {
|
|
332
|
+
openssl {key_technology_basic} -pubout -outform DER -pubin | \
|
|
283
333
|
openssl dgst -sha256 -binary | \
|
|
284
334
|
openssl enc -base64
|
|
285
335
|
"""
|
|
286
|
-
if
|
|
287
|
-
raise ValueError("must submit `
|
|
288
|
-
|
|
336
|
+
if key_technology_basic not in ("EC", "RSA"):
|
|
337
|
+
raise ValueError("must submit `key_technology_basic`")
|
|
338
|
+
key_technology_basic = key_technology_basic.lower()
|
|
289
339
|
if not csr_pem_filepath:
|
|
290
340
|
raise FallbackError_FilepathRequired("Must submit `csr_pem_filepath`.")
|
|
291
341
|
if core.openssl_version is None:
|
|
@@ -312,7 +362,7 @@ def _openssl_spki_hash_csr(
|
|
|
312
362
|
p2 = psutil.Popen(
|
|
313
363
|
[
|
|
314
364
|
core.openssl_path,
|
|
315
|
-
|
|
365
|
+
key_technology_basic,
|
|
316
366
|
"-pubin",
|
|
317
367
|
"-pubout",
|
|
318
368
|
"-outform",
|
|
@@ -377,13 +427,13 @@ def _openssl_spki_hash_csr(
|
|
|
377
427
|
|
|
378
428
|
|
|
379
429
|
def _openssl_spki_hash_pkey(
|
|
380
|
-
|
|
430
|
+
key_technology_basic: str = "",
|
|
381
431
|
key_pem_filepath: str = "",
|
|
382
432
|
as_b64: Optional[bool] = None,
|
|
383
433
|
) -> str:
|
|
384
434
|
"""
|
|
385
|
-
:param
|
|
386
|
-
:type
|
|
435
|
+
:param key_technology_basic: Is the key an "EC" or "RSA" key?
|
|
436
|
+
:type key_technology_basic: str
|
|
387
437
|
:param key_pem_filepath: REQUIRED filepath to PEM encoded PrivateKey.
|
|
388
438
|
Used for commandline OpenSSL fallback operations.
|
|
389
439
|
:type key_pem_filepath: str
|
|
@@ -398,9 +448,9 @@ def _openssl_spki_hash_pkey(
|
|
|
398
448
|
openssl dgst -sha256 -binary | \
|
|
399
449
|
openssl enc -base64
|
|
400
450
|
"""
|
|
401
|
-
if
|
|
402
|
-
raise ValueError("must submit `
|
|
403
|
-
|
|
451
|
+
if key_technology_basic not in ("EC", "RSA"):
|
|
452
|
+
raise ValueError("must submit `key_technology_basic`")
|
|
453
|
+
key_technology_basic = key_technology_basic.lower()
|
|
404
454
|
if not key_pem_filepath:
|
|
405
455
|
raise FallbackError_FilepathRequired("Must submit `key_pem_filepath`.")
|
|
406
456
|
if core.openssl_version is None:
|
|
@@ -414,7 +464,7 @@ def _openssl_spki_hash_pkey(
|
|
|
414
464
|
p1 = psutil.Popen(
|
|
415
465
|
[
|
|
416
466
|
core.openssl_path,
|
|
417
|
-
|
|
467
|
+
key_technology_basic,
|
|
418
468
|
"-pubout",
|
|
419
469
|
"-outform",
|
|
420
470
|
"DER",
|
|
@@ -699,3 +749,109 @@ def key_single_op__pem_filepath(
|
|
|
699
749
|
data_str = data_bytes.decode("utf8")
|
|
700
750
|
data_str = data_str.strip()
|
|
701
751
|
return data_str
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def san_domains_from_text(text: str) -> List[str]:
|
|
755
|
+
"""
|
|
756
|
+
Helper function to extract SAN domains from a chunk of text in a x509 object
|
|
757
|
+
|
|
758
|
+
:param text: string extracted from a x509 document
|
|
759
|
+
:type text: str
|
|
760
|
+
:returns: list of domains
|
|
761
|
+
:rtype: list
|
|
762
|
+
"""
|
|
763
|
+
san_domains = set([])
|
|
764
|
+
_subject_alt_names = RE_openssl_x509_san.search(text)
|
|
765
|
+
if _subject_alt_names is not None:
|
|
766
|
+
for _san in _subject_alt_names.group(1).split(", "):
|
|
767
|
+
if _san.startswith("DNS:"):
|
|
768
|
+
san_domains.add(_san[4:].lower())
|
|
769
|
+
return sorted(list(san_domains))
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def authority_key_identifier_from_text(text: str) -> Optional[str]:
|
|
773
|
+
"""
|
|
774
|
+
:param text: string extracted from a x509 document
|
|
775
|
+
:type text: str
|
|
776
|
+
:returns: authority_key_identifier
|
|
777
|
+
:rtype: str
|
|
778
|
+
|
|
779
|
+
openssl will print a uppercase hex pairs, separated by a colon
|
|
780
|
+
we should remove the colons
|
|
781
|
+
"""
|
|
782
|
+
results = RE_openssl_x509_authority_key_identifier.findall(text)
|
|
783
|
+
if results:
|
|
784
|
+
authority_key_identifier = results[0]
|
|
785
|
+
# ensure we have a key_id and not "URI:" or other convention
|
|
786
|
+
if authority_key_identifier[2] == ":":
|
|
787
|
+
return authority_key_identifier.replace(":", "")
|
|
788
|
+
return None
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def serial_from_text(text: str) -> Optional[int]:
|
|
792
|
+
"""
|
|
793
|
+
:param text: string extracted from a x509 document
|
|
794
|
+
:type text: str
|
|
795
|
+
:returns: serial
|
|
796
|
+
:rtype: int
|
|
797
|
+
"""
|
|
798
|
+
results = RE_openssl_x509_serial.findall(text)
|
|
799
|
+
if results:
|
|
800
|
+
serial = results[0]
|
|
801
|
+
return int(serial)
|
|
802
|
+
return None
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
def issuer_uri_from_text(text: str) -> Optional[str]:
|
|
806
|
+
"""
|
|
807
|
+
:param text: string extracted from a x509 document
|
|
808
|
+
:type text: str
|
|
809
|
+
:returns: issuer_uri
|
|
810
|
+
:rtype: str
|
|
811
|
+
"""
|
|
812
|
+
results = RE_openssl_x509_issuer_uri.findall(text)
|
|
813
|
+
if results:
|
|
814
|
+
return results[0]
|
|
815
|
+
return None
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _x509_pubkey_technology__text(cert_text: str) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
819
|
+
m = RE_openssl_x509_keytype_rsa.search(cert_text)
|
|
820
|
+
if m:
|
|
821
|
+
v = m.groups()
|
|
822
|
+
return ("RSA", (int(v[0]),))
|
|
823
|
+
m = RE_openssl_x509_keytype_ec.search(cert_text)
|
|
824
|
+
if m:
|
|
825
|
+
v = m.groups()
|
|
826
|
+
c = None
|
|
827
|
+
if v[2]:
|
|
828
|
+
c = v[2]
|
|
829
|
+
elif v[0]:
|
|
830
|
+
c = "P-%s" % v[0]
|
|
831
|
+
else:
|
|
832
|
+
# TODO? better parsing
|
|
833
|
+
return None
|
|
834
|
+
return ("EC", (c,))
|
|
835
|
+
return None
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def _cert_pubkey_technology__text(cert_text: str) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
839
|
+
"""
|
|
840
|
+
:param cert_text: string extracted from a x509 document
|
|
841
|
+
:type cert_text: str
|
|
842
|
+
:returns: Pubkey type: "RSA" or "EC"
|
|
843
|
+
:rtype: strfRE_openssl_x509_keytype_rsa
|
|
844
|
+
"""
|
|
845
|
+
# `cert_text` is the output of of `openssl x509 -noout -text -in MYCERT `
|
|
846
|
+
return _x509_pubkey_technology__text(cert_text)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def _csr_pubkey_technology__text(csr_text: str) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
850
|
+
"""
|
|
851
|
+
:param csr_text: string extracted from a CSR document
|
|
852
|
+
:type csr_text: str
|
|
853
|
+
:returns: Pubkey type: "RSA" or "EC"
|
|
854
|
+
:rtype: str
|
|
855
|
+
"""
|
|
856
|
+
# `csr_text` is the output of of `openssl req -noout -text -in MYCERT`
|
|
857
|
+
return _x509_pubkey_technology__text(csr_text)
|
|
@@ -8,6 +8,7 @@ more esoteric and not well documented elsewhere.
|
|
|
8
8
|
|
|
9
9
|
Please use the other interfaces.
|
|
10
10
|
"""
|
|
11
|
+
|
|
11
12
|
import logging
|
|
12
13
|
from types import ModuleType
|
|
13
14
|
from typing import List
|
|
@@ -89,7 +90,7 @@ def ensure_chain(
|
|
|
89
90
|
openssl does not care about the order of intermediates, so this should
|
|
90
91
|
be iteratively built up like the pure-python example
|
|
91
92
|
"""
|
|
92
|
-
log.debug("
|
|
93
|
+
log.debug("ensure_chain >")
|
|
93
94
|
if fullchain_pem:
|
|
94
95
|
if chain_pem or cert_pem:
|
|
95
96
|
raise ValueError(
|
|
@@ -157,7 +158,7 @@ def ensure_chain_order(
|
|
|
157
158
|
|
|
158
159
|
/usr/local/bin/openssl verify -purpose sslserver -partial_chain -trusted {ROOT.pem} {CHAINREVERSED.pem}
|
|
159
160
|
"""
|
|
160
|
-
log.debug("
|
|
161
|
+
log.debug("ensure_chain_order >")
|
|
161
162
|
if cert_pem:
|
|
162
163
|
chain_certs.append(cert_pem)
|
|
163
164
|
if len(chain_certs) < 2:
|