cert-utils 1.2.0__py3-none-any.whl
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/__init__.py +8 -0
- cert_utils/compat/__init__.py +0 -0
- cert_utils/compat/cryptography.py +172 -0
- cert_utils/compat/openssl.py +860 -0
- cert_utils/compat/pyopenssl.py +207 -0
- cert_utils/compat/signatures.py +114 -0
- cert_utils/conditionals.py +121 -0
- cert_utils/convert.py +307 -0
- cert_utils/core.py +3075 -0
- cert_utils/errors.py +54 -0
- cert_utils/letsencrypt-certs/deprecated/e5-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e5.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e6-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e6.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e7-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e7.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e8-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e8.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/e9-cross.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/e9.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/fakeleintermediatex1.pem +28 -0
- cert_utils/letsencrypt-certs/deprecated/fakelerootx1.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/isrg-root-ocsp-x1.pem +28 -0
- cert_utils/letsencrypt-certs/deprecated/isrg-root-x1-cross-signed.pem +31 -0
- cert_utils/letsencrypt-certs/deprecated/isrg-root-x2-cross-signed.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-e1.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-e2.pem +17 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r3-cross-signed.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r3.pem +30 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r4-cross-signed.pem +26 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-r4.pem +30 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x1-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x2-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x3-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/lets-encrypt-x4-cross-signed.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/letsencrypt-stg-root-x2-signed-by-x1.pem +27 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx1.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx2.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx3.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/letsencryptauthorityx4.pem +32 -0
- cert_utils/letsencrypt-certs/deprecated/r10.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r11.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r12.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r13.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/r14.pem +29 -0
- cert_utils/letsencrypt-certs/deprecated/trustid-x3-root.p7c +0 -0
- cert_utils/letsencrypt-certs/deprecated/trustid-x3-root.pem +20 -0
- cert_utils/letsencrypt-certs/int-ye1.pem +16 -0
- cert_utils/letsencrypt-certs/int-ye2.pem +16 -0
- cert_utils/letsencrypt-certs/int-ye3.pem +16 -0
- cert_utils/letsencrypt-certs/int-yr1.pem +28 -0
- cert_utils/letsencrypt-certs/int-yr2.pem +28 -0
- cert_utils/letsencrypt-certs/int-yr3.pem +28 -0
- cert_utils/letsencrypt-certs/isrg-root-x2.pem +14 -0
- cert_utils/letsencrypt-certs/isrgrootx1.pem +31 -0
- cert_utils/letsencrypt-certs/isrgrootx1.pkix +0 -0
- cert_utils/letsencrypt-certs/letsencrypt-stg-root-x1.pem +32 -0
- cert_utils/letsencrypt-certs/letsencrypt-stg-root-x2.pem +15 -0
- cert_utils/letsencrypt-certs/root-x2-by-x1.pem +26 -0
- cert_utils/letsencrypt-certs/root-ye-by-x2.pem +17 -0
- cert_utils/letsencrypt-certs/root-ye.pem +12 -0
- cert_utils/letsencrypt-certs/root-yr-by-x1.pem +34 -0
- cert_utils/letsencrypt-certs/root-yr.pem +30 -0
- cert_utils/letsencrypt_info/__init__.py +6 -0
- cert_utils/letsencrypt_info/certificates.py +1074 -0
- cert_utils/letsencrypt_info/rate_limits.py +414 -0
- cert_utils/letsencrypt_info/types.py +120 -0
- cert_utils/model.py +181 -0
- cert_utils/network.py +28 -0
- cert_utils/py.typed +0 -0
- cert_utils/scripts/__init__.py +0 -0
- cert_utils/scripts/cert_info.py +332 -0
- cert_utils/utils.py +388 -0
- cert_utils-1.2.0.dist-info/METADATA +259 -0
- cert_utils-1.2.0.dist-info/RECORD +79 -0
- cert_utils-1.2.0.dist-info/WHEEL +5 -0
- cert_utils-1.2.0.dist-info/entry_points.txt +2 -0
- cert_utils-1.2.0.dist-info/licenses/LICENSE.txt +224 -0
- cert_utils-1.2.0.dist-info/top_level.txt +1 -0
cert_utils/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from .convert import * # noqa: F401, F403
|
|
2
|
+
from .core import * # noqa: F401, F403
|
|
3
|
+
from .model import * # noqa: F401, F403
|
|
4
|
+
from .utils import * # noqa: F401, F403
|
|
5
|
+
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
__VERSION__ = "1.2.0"
|
|
File without changes
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# stdlib
|
|
2
|
+
import base64
|
|
3
|
+
import binascii
|
|
4
|
+
import hashlib
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from typing import Tuple
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
11
|
+
# locals
|
|
12
|
+
from .. import conditionals
|
|
13
|
+
from ..utils import CERT_PEM_REGEX
|
|
14
|
+
from ..utils import curve_to_nist
|
|
15
|
+
from ..utils import TECHNOLOGY_RETURN_VALUES
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from cryptography.x509 import Certificate
|
|
19
|
+
from cryptography.x509 import CertificateSigningRequest
|
|
20
|
+
from cryptography.x509.name import Name
|
|
21
|
+
|
|
22
|
+
from ..core import _PublicKeySPKI
|
|
23
|
+
from ..core import _TYPES_CRYPTOGRAPHY_PUBLICKEY
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ==============================================================================
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _format_cryptography_components(
|
|
30
|
+
x509_name: "Name",
|
|
31
|
+
) -> str:
|
|
32
|
+
"""
|
|
33
|
+
:param data: input
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
`get_components()` is somewhat structured
|
|
37
|
+
the following are valid:
|
|
38
|
+
* [('CN', 'Pebble Intermediate CA 601ea1')]
|
|
39
|
+
* [('C', 'US'), ('O', 'Internet Security Research Group'), ('CN', 'ISRG Root X2')]
|
|
40
|
+
* [('C', 'US'), ('O', 'Internet Security Research Group'), ('CN', 'ISRG Root X1')]
|
|
41
|
+
* [('O', 'Digital Signature Trust Co.'), ('CN', 'DST Root CA X3')]
|
|
42
|
+
cert = openssl_crypto.load_certificate(openssl_crypto.FILETYPE_PEM, cert_pem)
|
|
43
|
+
_issuer = cert.get_issuer().get_components()
|
|
44
|
+
_subject = cert.get_subject().get_components()
|
|
45
|
+
"""
|
|
46
|
+
# ";".join(["%s=%s" % (i[0], i[1]) for i in x509_name])
|
|
47
|
+
_out = []
|
|
48
|
+
for attr in x509_name:
|
|
49
|
+
_converted = [i.decode("utf8") if isinstance(i, bytes) else i for i in (attr.rfc4514_attribute_name, attr.value)] # type: ignore[attr-defined]
|
|
50
|
+
_out.append("=".join(_converted))
|
|
51
|
+
out = "\n".join(_out).strip()
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _cryptography__public_key_technology(
|
|
56
|
+
publickey: "_TYPES_CRYPTOGRAPHY_PUBLICKEY",
|
|
57
|
+
) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
58
|
+
"""
|
|
59
|
+
:param key: key object
|
|
60
|
+
:type key: instance of
|
|
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, ))
|
|
66
|
+
"""
|
|
67
|
+
if TYPE_CHECKING:
|
|
68
|
+
assert conditionals.crypto_rsa is not None
|
|
69
|
+
assert conditionals.crypto_ec is not None
|
|
70
|
+
if isinstance(publickey, conditionals.crypto_rsa.RSAPublicKey):
|
|
71
|
+
return ("RSA", (publickey.key_size,))
|
|
72
|
+
elif isinstance(publickey, conditionals.crypto_ec.EllipticCurvePublicKey):
|
|
73
|
+
curve_name = curve_to_nist(publickey.curve.name)
|
|
74
|
+
return ("EC", (curve_name,))
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _cryptography__public_key_spki_sha256(
|
|
79
|
+
cryptography_publickey: "_PublicKeySPKI",
|
|
80
|
+
as_b64: Optional[bool] = None,
|
|
81
|
+
) -> str:
|
|
82
|
+
"""
|
|
83
|
+
:param cryptography_publickey: a PublicKey from the cryptography package
|
|
84
|
+
:type cryptography_publickey: cryptography.hazmat.backends.openssl.rsa._RSAPublicKey
|
|
85
|
+
:param as_b64: Should the result be returned in Base64 encoding? default None
|
|
86
|
+
:type as_b64: boolean
|
|
87
|
+
:returns: spki_sha256
|
|
88
|
+
:rtype: str
|
|
89
|
+
"""
|
|
90
|
+
assert (
|
|
91
|
+
conditionals.crypto_serialization is not None
|
|
92
|
+
) # nest under `if TYPE_CHECKING` not needed
|
|
93
|
+
_public_bytes = cryptography_publickey.public_bytes( # type: ignore[union-attr]
|
|
94
|
+
conditionals.crypto_serialization.Encoding.DER,
|
|
95
|
+
conditionals.crypto_serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
96
|
+
)
|
|
97
|
+
spki_sha256 = hashlib.sha256(_public_bytes).digest()
|
|
98
|
+
if as_b64:
|
|
99
|
+
spki_sha256 = base64.b64encode(spki_sha256)
|
|
100
|
+
else:
|
|
101
|
+
spki_sha256 = binascii.b2a_hex(spki_sha256)
|
|
102
|
+
spki_sha256 = spki_sha256.upper()
|
|
103
|
+
_spki_sha256 = spki_sha256.decode("utf8")
|
|
104
|
+
return _spki_sha256
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _cryptography__cert_or_req__all_names(
|
|
108
|
+
loaded_cert_or_req: Union["Certificate", "CertificateSigningRequest"],
|
|
109
|
+
) -> List[str]:
|
|
110
|
+
assert conditionals.cryptography is not None
|
|
111
|
+
cn: Optional[str] = None
|
|
112
|
+
try:
|
|
113
|
+
_cn = loaded_cert_or_req.subject.get_attributes_for_oid(
|
|
114
|
+
conditionals.cryptography.x509.oid.NameOID.COMMON_NAME
|
|
115
|
+
)[0].value
|
|
116
|
+
cn = _cn.decode() if isinstance(_cn, bytes) else _cn
|
|
117
|
+
except IndexError:
|
|
118
|
+
pass
|
|
119
|
+
sans: List[str] = []
|
|
120
|
+
try:
|
|
121
|
+
ext = loaded_cert_or_req.extensions.get_extension_for_oid(
|
|
122
|
+
conditionals.cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
|
|
123
|
+
)
|
|
124
|
+
_sans = ext.value.get_values_for_type(conditionals.cryptography.x509.DNSName) # type: ignore[attr-defined]
|
|
125
|
+
sans = [san.decode() if isinstance(san, bytes) else san for san in _sans]
|
|
126
|
+
|
|
127
|
+
except conditionals.cryptography.x509.ExtensionNotFound:
|
|
128
|
+
pass
|
|
129
|
+
if cn is None:
|
|
130
|
+
return sans
|
|
131
|
+
return [cn] + [d for d in sans if d != cn]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def cryptography__cert_and_chain_from_fullchain(fullchain_pem: str) -> Tuple[str, str]:
|
|
135
|
+
"""Split fullchain_pem into cert_pem and chain_pem
|
|
136
|
+
|
|
137
|
+
:param str fullchain_pem: concatenated cert + chain
|
|
138
|
+
|
|
139
|
+
:returns: tuple of string cert_pem and chain_pem
|
|
140
|
+
:rtype: tuple
|
|
141
|
+
|
|
142
|
+
:raises errors.Error: If there are less than 2 certificates in the chain.
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
# First pass: find the boundary of each certificate in the chain.
|
|
146
|
+
# TODO: This will silently skip over any "explanatory text" in between boundaries,
|
|
147
|
+
# which is prohibited by RFC8555.
|
|
148
|
+
if TYPE_CHECKING:
|
|
149
|
+
assert conditionals.cryptography is not None
|
|
150
|
+
assert conditionals.crypto_serialization is not None
|
|
151
|
+
|
|
152
|
+
certs = CERT_PEM_REGEX.findall(fullchain_pem)
|
|
153
|
+
if len(certs) < 2:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
"failed to parse fullchain into cert and chain: "
|
|
156
|
+
+ "less than 2 certificates in chain"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Second pass: for each certificate found, parse it using OpenSSL and re-encode it,
|
|
160
|
+
# with the effect of normalizing any encoding variations (e.g. CRLF, whitespace).
|
|
161
|
+
certs_normalized = []
|
|
162
|
+
for cert_pem in certs:
|
|
163
|
+
cert = conditionals.cryptography.x509.load_pem_x509_certificate(
|
|
164
|
+
cert_pem.encode()
|
|
165
|
+
)
|
|
166
|
+
cert_pem = cert.public_bytes(
|
|
167
|
+
conditionals.crypto_serialization.Encoding.PEM
|
|
168
|
+
).decode()
|
|
169
|
+
certs_normalized.append(cert_pem)
|
|
170
|
+
|
|
171
|
+
# Since each normalized cert has a newline suffix, no extra newlines are required.
|
|
172
|
+
return (certs_normalized[0], "".join(certs_normalized[1:]))
|