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
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
# stdlib
|
|
2
|
+
import binascii
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from typing import Tuple
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
# pypi
|
|
11
|
+
import psutil
|
|
12
|
+
|
|
13
|
+
# local
|
|
14
|
+
from .. import core
|
|
15
|
+
from ..errors import FallbackError_FilepathRequired
|
|
16
|
+
from ..errors import OpenSslError
|
|
17
|
+
from ..errors import OpenSslError_InvalidCertificate
|
|
18
|
+
from ..errors import OpenSslError_InvalidCSR
|
|
19
|
+
from ..errors import OpenSslError_InvalidKey
|
|
20
|
+
from ..errors import OpenSslError_VersionTooLow
|
|
21
|
+
from ..utils import new_pem_tempfile
|
|
22
|
+
from ..utils import TECHNOLOGY_RETURN_VALUES
|
|
23
|
+
|
|
24
|
+
# ==============================================================================
|
|
25
|
+
|
|
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
|
+
|
|
75
|
+
def _cleanup_openssl_modulus(data: str) -> str:
|
|
76
|
+
data = data.strip()
|
|
77
|
+
if data[:8] == "Modulus=":
|
|
78
|
+
data = data[8:]
|
|
79
|
+
return data
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _format_openssl_components(
|
|
83
|
+
data: str,
|
|
84
|
+
fieldset: Optional[str] = None,
|
|
85
|
+
) -> str:
|
|
86
|
+
"""
|
|
87
|
+
different openssl versions give different responses. FUN.
|
|
88
|
+
|
|
89
|
+
To make things easier, just format this into the crypto compatible payload,
|
|
90
|
+
then invoke the crypto formattter
|
|
91
|
+
|
|
92
|
+
openssl = [0, 9, 8]
|
|
93
|
+
subject= /C=US/O=Internet Security Research Group/CN=ISRG Root X2
|
|
94
|
+
|
|
95
|
+
openssl = [1, 1, 1]
|
|
96
|
+
issuer=C = US, O = Internet Security Research Group, CN = ISRG Root X2
|
|
97
|
+
"""
|
|
98
|
+
# print(core.openssl_version, data)
|
|
99
|
+
if fieldset in ("issuer", "subject"):
|
|
100
|
+
if fieldset == "issuer":
|
|
101
|
+
if data.startswith("issuer= "):
|
|
102
|
+
data = data[8:]
|
|
103
|
+
elif data.startswith("issuer="):
|
|
104
|
+
data = data[7:]
|
|
105
|
+
elif fieldset == "subject":
|
|
106
|
+
if data.startswith("subject= "):
|
|
107
|
+
data = data[9:]
|
|
108
|
+
elif data.startswith("subject="):
|
|
109
|
+
data = data[8:]
|
|
110
|
+
data_list: List[str]
|
|
111
|
+
if "/" in data:
|
|
112
|
+
data_list = [i.strip() for i in data.split("/")]
|
|
113
|
+
elif "," in data:
|
|
114
|
+
data_list = [i.strip() for i in data.split(",")]
|
|
115
|
+
else:
|
|
116
|
+
data_list = [
|
|
117
|
+
data,
|
|
118
|
+
]
|
|
119
|
+
_out = []
|
|
120
|
+
for _cset in data_list:
|
|
121
|
+
_cset_split = _cset.split("=")
|
|
122
|
+
_cset_edited = tuple(i.strip() for i in _cset_split)
|
|
123
|
+
_out.append(_cset_edited)
|
|
124
|
+
return _format_crypto_components(_out, fieldset=fieldset)
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError("invalid fieldset")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _format_crypto_components(
|
|
130
|
+
data: Union[
|
|
131
|
+
List[str],
|
|
132
|
+
List[Tuple[str, ...]],
|
|
133
|
+
List[Tuple[bytes, bytes]],
|
|
134
|
+
],
|
|
135
|
+
fieldset: Optional[str] = None,
|
|
136
|
+
) -> str:
|
|
137
|
+
"""
|
|
138
|
+
:param data: input
|
|
139
|
+
:param fieldset: is unused. would be "issuer" or "subject"
|
|
140
|
+
|
|
141
|
+
`get_components()` is somewhat structured
|
|
142
|
+
the following are valid:
|
|
143
|
+
* [('CN', 'Pebble Intermediate CA 601ea1')]
|
|
144
|
+
* [('C', 'US'), ('O', 'Internet Security Research Group'), ('CN', 'ISRG Root X2')]
|
|
145
|
+
* [('C', 'US'), ('O', 'Internet Security Research Group'), ('CN', 'ISRG Root X1')]
|
|
146
|
+
* [('O', 'Digital Signature Trust Co.'), ('CN', 'DST Root CA X3')]
|
|
147
|
+
cert = openssl_crypto.load_certificate(openssl_crypto.FILETYPE_PEM, cert_pem)
|
|
148
|
+
_issuer = cert.get_issuer().get_components()
|
|
149
|
+
_subject = cert.get_subject().get_components()
|
|
150
|
+
"""
|
|
151
|
+
_out = []
|
|
152
|
+
for _in_set in data:
|
|
153
|
+
_converted = [i.decode("utf8") if isinstance(i, bytes) else i for i in _in_set] # type: ignore[attr-defined]
|
|
154
|
+
_out.append("=".join(_converted))
|
|
155
|
+
out = "\n".join(_out).strip()
|
|
156
|
+
return out
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _openssl_cert__normalize_pem(cert_pem: str) -> str:
|
|
160
|
+
"""
|
|
161
|
+
normalize a cert using openssl
|
|
162
|
+
NOTE: this is an openssl fallback routine
|
|
163
|
+
|
|
164
|
+
:param cert_pem: PEM encoded Certificate data
|
|
165
|
+
:type cert_pem: str
|
|
166
|
+
:returns: normalized Certificate
|
|
167
|
+
:rtype: str
|
|
168
|
+
|
|
169
|
+
This runs via OpenSSL:
|
|
170
|
+
|
|
171
|
+
openssl x509 -in {FILEPATH}
|
|
172
|
+
"""
|
|
173
|
+
if core.openssl_version is None:
|
|
174
|
+
core.check_openssl_version()
|
|
175
|
+
|
|
176
|
+
_tmpfile_pem = new_pem_tempfile(cert_pem)
|
|
177
|
+
try:
|
|
178
|
+
cert_pem_filepath = _tmpfile_pem.name
|
|
179
|
+
with psutil.Popen(
|
|
180
|
+
[core.openssl_path, "x509", "-in", cert_pem_filepath],
|
|
181
|
+
stdout=subprocess.PIPE,
|
|
182
|
+
stderr=subprocess.PIPE,
|
|
183
|
+
) as proc:
|
|
184
|
+
data_bytes, err = proc.communicate()
|
|
185
|
+
if not data_bytes:
|
|
186
|
+
raise OpenSslError_InvalidCertificate(err)
|
|
187
|
+
data_str = data_bytes.decode("utf8")
|
|
188
|
+
data_str = data_str.strip()
|
|
189
|
+
return data_str
|
|
190
|
+
except Exception as exc: # noqa: F841
|
|
191
|
+
raise
|
|
192
|
+
finally:
|
|
193
|
+
_tmpfile_pem.close()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _openssl_spki_hash_cert(
|
|
197
|
+
key_technology_basic: str = "",
|
|
198
|
+
cert_pem_filepath: str = "",
|
|
199
|
+
as_b64: Optional[bool] = None,
|
|
200
|
+
) -> str:
|
|
201
|
+
"""
|
|
202
|
+
:param key_technology_basic: Is the key an "EC" or "RSA" key?
|
|
203
|
+
:type key_technology_basic: str
|
|
204
|
+
:param cert_pem_filepath: REQUIRED filepath to PEM Certificate.
|
|
205
|
+
Used for commandline OpenSSL fallback operations.
|
|
206
|
+
:type cert_pem_filepath: str
|
|
207
|
+
:param as_b64: Should the result be returned in Base64 encoding? default None
|
|
208
|
+
:type as_b64: boolean
|
|
209
|
+
:returns: spki_hash
|
|
210
|
+
:rtype: str
|
|
211
|
+
|
|
212
|
+
The OpenSSL Equivalent / Fallback is::
|
|
213
|
+
|
|
214
|
+
openssl x509 -pubkey -noout -in {CERT_FILEPATH} | \
|
|
215
|
+
openssl {key_technology_basic} -pubout -outform DER -pubin | \
|
|
216
|
+
openssl dgst -sha256 -binary | \
|
|
217
|
+
openssl enc -base64
|
|
218
|
+
"""
|
|
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()
|
|
222
|
+
if not cert_pem_filepath:
|
|
223
|
+
raise FallbackError_FilepathRequired("Must submit `cert_pem_filepath`.")
|
|
224
|
+
if core.openssl_version is None:
|
|
225
|
+
core.check_openssl_version()
|
|
226
|
+
|
|
227
|
+
spki_hash = None
|
|
228
|
+
# convert to DER
|
|
229
|
+
p1 = p2 = p3 = proc4 = None
|
|
230
|
+
try:
|
|
231
|
+
# extract the key
|
|
232
|
+
p1 = psutil.Popen(
|
|
233
|
+
[
|
|
234
|
+
core.openssl_path,
|
|
235
|
+
"x509",
|
|
236
|
+
"-pubkey",
|
|
237
|
+
"-noout",
|
|
238
|
+
"-in",
|
|
239
|
+
cert_pem_filepath,
|
|
240
|
+
],
|
|
241
|
+
stdout=subprocess.PIPE,
|
|
242
|
+
stderr=subprocess.PIPE,
|
|
243
|
+
)
|
|
244
|
+
# convert to DER
|
|
245
|
+
p2 = psutil.Popen(
|
|
246
|
+
[
|
|
247
|
+
core.openssl_path,
|
|
248
|
+
key_technology_basic,
|
|
249
|
+
"-pubin",
|
|
250
|
+
"-pubout",
|
|
251
|
+
"-outform",
|
|
252
|
+
"DER",
|
|
253
|
+
],
|
|
254
|
+
stdin=p1.stdout,
|
|
255
|
+
stdout=subprocess.PIPE,
|
|
256
|
+
stderr=subprocess.PIPE,
|
|
257
|
+
)
|
|
258
|
+
# digest
|
|
259
|
+
p3 = psutil.Popen(
|
|
260
|
+
[
|
|
261
|
+
core.openssl_path,
|
|
262
|
+
"dgst",
|
|
263
|
+
"-sha256",
|
|
264
|
+
"-binary",
|
|
265
|
+
],
|
|
266
|
+
stdin=p2.stdout,
|
|
267
|
+
stdout=subprocess.PIPE,
|
|
268
|
+
stderr=subprocess.PIPE,
|
|
269
|
+
)
|
|
270
|
+
# encode
|
|
271
|
+
spki_hash = None
|
|
272
|
+
if as_b64:
|
|
273
|
+
with psutil.Popen(
|
|
274
|
+
[
|
|
275
|
+
core.openssl_path,
|
|
276
|
+
"enc",
|
|
277
|
+
"-base64",
|
|
278
|
+
],
|
|
279
|
+
stdin=p3.stdout,
|
|
280
|
+
stdout=subprocess.PIPE,
|
|
281
|
+
stderr=subprocess.PIPE,
|
|
282
|
+
) as proc4:
|
|
283
|
+
spki_hash, err = proc4.communicate()
|
|
284
|
+
if err:
|
|
285
|
+
raise OpenSslError("could not generate SPKI Hash")
|
|
286
|
+
else:
|
|
287
|
+
spki_hash, err = p3.communicate()
|
|
288
|
+
if err:
|
|
289
|
+
raise OpenSslError("could not generate SPKI Hash")
|
|
290
|
+
spki_hash = binascii.b2a_hex(spki_hash)
|
|
291
|
+
spki_hash = spki_hash.upper()
|
|
292
|
+
spki_hash = spki_hash.strip()
|
|
293
|
+
spki_hash = spki_hash.decode("utf8")
|
|
294
|
+
|
|
295
|
+
finally:
|
|
296
|
+
# Note: explicitly close what we opened
|
|
297
|
+
for _p in (
|
|
298
|
+
p1,
|
|
299
|
+
p2,
|
|
300
|
+
p3,
|
|
301
|
+
):
|
|
302
|
+
if _p is not None:
|
|
303
|
+
try:
|
|
304
|
+
_p.stdout.close()
|
|
305
|
+
_p.stderr.close()
|
|
306
|
+
_p.terminate()
|
|
307
|
+
_p.wait()
|
|
308
|
+
except psutil.NoSuchProcess:
|
|
309
|
+
pass
|
|
310
|
+
return spki_hash
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _openssl_spki_hash_csr(
|
|
314
|
+
key_technology_basic: str = "",
|
|
315
|
+
csr_pem_filepath: str = "",
|
|
316
|
+
as_b64: Optional[bool] = None,
|
|
317
|
+
) -> str:
|
|
318
|
+
"""
|
|
319
|
+
:param key_technology_basic: Is the key an "EC" or "RSA" key?
|
|
320
|
+
:type key_technology_basic: str
|
|
321
|
+
:param csr_pem_filepath: REQUIRED filepath to PEM CSR.
|
|
322
|
+
Used for commandline OpenSSL fallback operations.
|
|
323
|
+
:type csr_pem_filepath: str
|
|
324
|
+
:param as_b64: Should the result be returned in Base64 encoding? default None
|
|
325
|
+
:type as_b64: boolean
|
|
326
|
+
:returns: spki_hash
|
|
327
|
+
:rtype: str
|
|
328
|
+
|
|
329
|
+
The OpenSSL Equivalent / Fallback is::
|
|
330
|
+
|
|
331
|
+
openssl REQ -pubkey -noout -in {CSR_FILEPATH} | \
|
|
332
|
+
openssl {key_technology_basic} -pubout -outform DER -pubin | \
|
|
333
|
+
openssl dgst -sha256 -binary | \
|
|
334
|
+
openssl enc -base64
|
|
335
|
+
"""
|
|
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()
|
|
339
|
+
if not csr_pem_filepath:
|
|
340
|
+
raise FallbackError_FilepathRequired("Must submit `csr_pem_filepath`.")
|
|
341
|
+
if core.openssl_version is None:
|
|
342
|
+
core.check_openssl_version()
|
|
343
|
+
|
|
344
|
+
spki_hash = None
|
|
345
|
+
# convert to DER
|
|
346
|
+
p1 = p2 = p3 = proc4 = None
|
|
347
|
+
try:
|
|
348
|
+
# extract the key
|
|
349
|
+
p1 = psutil.Popen(
|
|
350
|
+
[
|
|
351
|
+
core.openssl_path,
|
|
352
|
+
"req",
|
|
353
|
+
"-pubkey",
|
|
354
|
+
"-noout",
|
|
355
|
+
"-in",
|
|
356
|
+
csr_pem_filepath,
|
|
357
|
+
],
|
|
358
|
+
stdout=subprocess.PIPE,
|
|
359
|
+
stderr=subprocess.PIPE,
|
|
360
|
+
)
|
|
361
|
+
# convert to DER
|
|
362
|
+
p2 = psutil.Popen(
|
|
363
|
+
[
|
|
364
|
+
core.openssl_path,
|
|
365
|
+
key_technology_basic,
|
|
366
|
+
"-pubin",
|
|
367
|
+
"-pubout",
|
|
368
|
+
"-outform",
|
|
369
|
+
"DER",
|
|
370
|
+
],
|
|
371
|
+
stdin=p1.stdout,
|
|
372
|
+
stdout=subprocess.PIPE,
|
|
373
|
+
stderr=subprocess.PIPE,
|
|
374
|
+
)
|
|
375
|
+
# digest
|
|
376
|
+
p3 = psutil.Popen(
|
|
377
|
+
[
|
|
378
|
+
core.openssl_path,
|
|
379
|
+
"dgst",
|
|
380
|
+
"-sha256",
|
|
381
|
+
"-binary",
|
|
382
|
+
],
|
|
383
|
+
stdin=p2.stdout,
|
|
384
|
+
stdout=subprocess.PIPE,
|
|
385
|
+
stderr=subprocess.PIPE,
|
|
386
|
+
)
|
|
387
|
+
# encode
|
|
388
|
+
spki_hash = None
|
|
389
|
+
if as_b64:
|
|
390
|
+
with psutil.Popen(
|
|
391
|
+
[
|
|
392
|
+
core.openssl_path,
|
|
393
|
+
"enc",
|
|
394
|
+
"-base64",
|
|
395
|
+
],
|
|
396
|
+
stdin=p3.stdout,
|
|
397
|
+
stdout=subprocess.PIPE,
|
|
398
|
+
stderr=subprocess.PIPE,
|
|
399
|
+
) as proc4:
|
|
400
|
+
spki_hash, err = proc4.communicate()
|
|
401
|
+
if err:
|
|
402
|
+
raise OpenSslError("could not generate SPKI Hash")
|
|
403
|
+
else:
|
|
404
|
+
spki_hash, err = p3.communicate()
|
|
405
|
+
if err:
|
|
406
|
+
raise OpenSslError("could not generate SPKI Hash")
|
|
407
|
+
spki_hash = binascii.b2a_hex(spki_hash)
|
|
408
|
+
spki_hash = spki_hash.upper()
|
|
409
|
+
spki_hash = spki_hash.strip()
|
|
410
|
+
spki_hash = spki_hash.decode("utf8")
|
|
411
|
+
finally:
|
|
412
|
+
# Note: explicitly close what we opened
|
|
413
|
+
for _p in (
|
|
414
|
+
p1,
|
|
415
|
+
p2,
|
|
416
|
+
p3,
|
|
417
|
+
):
|
|
418
|
+
if _p is not None:
|
|
419
|
+
try:
|
|
420
|
+
_p.stdout.close()
|
|
421
|
+
_p.stderr.close()
|
|
422
|
+
_p.terminate()
|
|
423
|
+
_p.wait()
|
|
424
|
+
except psutil.NoSuchProcess:
|
|
425
|
+
pass
|
|
426
|
+
return spki_hash
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _openssl_spki_hash_pkey(
|
|
430
|
+
key_technology_basic: str = "",
|
|
431
|
+
key_pem_filepath: str = "",
|
|
432
|
+
as_b64: Optional[bool] = None,
|
|
433
|
+
) -> str:
|
|
434
|
+
"""
|
|
435
|
+
:param key_technology_basic: Is the key an "EC" or "RSA" key?
|
|
436
|
+
:type key_technology_basic: str
|
|
437
|
+
:param key_pem_filepath: REQUIRED filepath to PEM encoded PrivateKey.
|
|
438
|
+
Used for commandline OpenSSL fallback operations.
|
|
439
|
+
:type key_pem_filepath: str
|
|
440
|
+
:param as_b64: Should the result be returned in Base64 encoding? default None
|
|
441
|
+
:type as_b64: boolean
|
|
442
|
+
:returns: spki_hash
|
|
443
|
+
:rtype: str
|
|
444
|
+
|
|
445
|
+
The OpenSSL Equivalent / Fallback is::
|
|
446
|
+
|
|
447
|
+
openssl rsa -in {KEY_FILEPATH} -pubout -outform der | \
|
|
448
|
+
openssl dgst -sha256 -binary | \
|
|
449
|
+
openssl enc -base64
|
|
450
|
+
"""
|
|
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()
|
|
454
|
+
if not key_pem_filepath:
|
|
455
|
+
raise FallbackError_FilepathRequired("Must submit `key_pem_filepath`.")
|
|
456
|
+
if core.openssl_version is None:
|
|
457
|
+
core.check_openssl_version()
|
|
458
|
+
|
|
459
|
+
spki_hash = None
|
|
460
|
+
# convert to DER
|
|
461
|
+
p1 = p2 = proc3 = None
|
|
462
|
+
try:
|
|
463
|
+
# convert to DER
|
|
464
|
+
p1 = psutil.Popen(
|
|
465
|
+
[
|
|
466
|
+
core.openssl_path,
|
|
467
|
+
key_technology_basic,
|
|
468
|
+
"-pubout",
|
|
469
|
+
"-outform",
|
|
470
|
+
"DER",
|
|
471
|
+
"-in",
|
|
472
|
+
key_pem_filepath,
|
|
473
|
+
],
|
|
474
|
+
stdout=subprocess.PIPE,
|
|
475
|
+
stderr=subprocess.PIPE,
|
|
476
|
+
)
|
|
477
|
+
# digest
|
|
478
|
+
p2 = psutil.Popen(
|
|
479
|
+
[
|
|
480
|
+
core.openssl_path,
|
|
481
|
+
"dgst",
|
|
482
|
+
"-sha256",
|
|
483
|
+
"-binary",
|
|
484
|
+
],
|
|
485
|
+
stdin=p1.stdout,
|
|
486
|
+
stdout=subprocess.PIPE,
|
|
487
|
+
stderr=subprocess.PIPE,
|
|
488
|
+
)
|
|
489
|
+
# encode
|
|
490
|
+
if as_b64:
|
|
491
|
+
with psutil.Popen(
|
|
492
|
+
[
|
|
493
|
+
core.openssl_path,
|
|
494
|
+
"enc",
|
|
495
|
+
"-base64",
|
|
496
|
+
],
|
|
497
|
+
stdin=p2.stdout,
|
|
498
|
+
stdout=subprocess.PIPE,
|
|
499
|
+
stderr=subprocess.PIPE,
|
|
500
|
+
) as proc3:
|
|
501
|
+
spki_hash, err = proc3.communicate()
|
|
502
|
+
if err:
|
|
503
|
+
raise OpenSslError("could not generate SPKI Hash")
|
|
504
|
+
else:
|
|
505
|
+
spki_hash, err = p2.communicate()
|
|
506
|
+
if err:
|
|
507
|
+
raise OpenSslError("could not generate SPKI Hash")
|
|
508
|
+
spki_hash = binascii.b2a_hex(spki_hash)
|
|
509
|
+
spki_hash = spki_hash.upper()
|
|
510
|
+
spki_hash = spki_hash.strip()
|
|
511
|
+
spki_hash = spki_hash.decode("utf8")
|
|
512
|
+
finally:
|
|
513
|
+
# Note: explicitly close what we opened
|
|
514
|
+
for _p in (
|
|
515
|
+
p1,
|
|
516
|
+
p2,
|
|
517
|
+
):
|
|
518
|
+
if _p is not None:
|
|
519
|
+
try:
|
|
520
|
+
_p.stdout.close()
|
|
521
|
+
_p.stderr.close()
|
|
522
|
+
_p.terminate()
|
|
523
|
+
_p.wait()
|
|
524
|
+
except psutil.NoSuchProcess:
|
|
525
|
+
pass
|
|
526
|
+
return spki_hash
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def _openssl_cert_single_op__pem(
|
|
530
|
+
cert_pem: str,
|
|
531
|
+
single_op: str,
|
|
532
|
+
) -> str:
|
|
533
|
+
"""
|
|
534
|
+
this just invokes `_openssl_cert_single_op__pem_filepath` with a tempfile
|
|
535
|
+
"""
|
|
536
|
+
_tmpfile_pem = new_pem_tempfile(cert_pem)
|
|
537
|
+
try:
|
|
538
|
+
cert_pem_filepath = _tmpfile_pem.name
|
|
539
|
+
return _openssl_cert_single_op__pem_filepath(cert_pem_filepath, single_op)
|
|
540
|
+
except Exception as exc: # noqa: F841
|
|
541
|
+
raise
|
|
542
|
+
finally:
|
|
543
|
+
_tmpfile_pem.close()
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def _openssl_cert_single_op__pem_filepath(
|
|
547
|
+
pem_filepath: str,
|
|
548
|
+
single_op: str,
|
|
549
|
+
) -> str:
|
|
550
|
+
"""
|
|
551
|
+
handles a single pem operation to `openssl x509`
|
|
552
|
+
|
|
553
|
+
:param pem_filepath: filepath to pem encoded cert
|
|
554
|
+
:type pem_filepath: str
|
|
555
|
+
:param single_op: operation
|
|
556
|
+
:type single_op: str
|
|
557
|
+
:returns: openssl output
|
|
558
|
+
:rtype: str
|
|
559
|
+
|
|
560
|
+
openssl x509 -noout -issuer -in cert.pem
|
|
561
|
+
openssl x509 -noout -issuer_hash -in cert.pem
|
|
562
|
+
|
|
563
|
+
openssl x509 -noout -issuer_hash -in {CERT}
|
|
564
|
+
returns the data found in
|
|
565
|
+
X509v3 extensions:
|
|
566
|
+
X509v3 Authority Key Identifier:
|
|
567
|
+
keyid:{VALUE}
|
|
568
|
+
|
|
569
|
+
openssl x509 -noout -subject_hash -in {CERT}
|
|
570
|
+
returns the data found in
|
|
571
|
+
X509v3 extensions:
|
|
572
|
+
X509v3 Subject Key Identifier:
|
|
573
|
+
{VALUE}
|
|
574
|
+
|
|
575
|
+
The OpenSSL Equivalent / Fallback is::
|
|
576
|
+
|
|
577
|
+
openssl x509 -noout {OPERATION} -in {FILEPATH})
|
|
578
|
+
"""
|
|
579
|
+
if single_op not in (
|
|
580
|
+
"-issuer_hash",
|
|
581
|
+
"-issuer",
|
|
582
|
+
"-subject_hash",
|
|
583
|
+
"-subject",
|
|
584
|
+
"-startdate",
|
|
585
|
+
"-enddate",
|
|
586
|
+
):
|
|
587
|
+
raise ValueError("invalid `single_op`")
|
|
588
|
+
if not pem_filepath:
|
|
589
|
+
raise FallbackError_FilepathRequired("Must submit `pem_filepath`.")
|
|
590
|
+
if core.openssl_version is None:
|
|
591
|
+
core.check_openssl_version()
|
|
592
|
+
|
|
593
|
+
with psutil.Popen(
|
|
594
|
+
[core.openssl_path, "x509", "-noout", single_op, "-in", pem_filepath],
|
|
595
|
+
stdout=subprocess.PIPE,
|
|
596
|
+
stderr=subprocess.PIPE,
|
|
597
|
+
) as proc:
|
|
598
|
+
data_bytes, err = proc.communicate()
|
|
599
|
+
if not data_bytes:
|
|
600
|
+
raise OpenSslError_InvalidCertificate(err)
|
|
601
|
+
data_str = data_bytes.decode("utf8")
|
|
602
|
+
data_str = data_str.strip()
|
|
603
|
+
return data_str
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def cert_ext__pem_filepath(
|
|
607
|
+
pem_filepath: str,
|
|
608
|
+
ext: str,
|
|
609
|
+
) -> str:
|
|
610
|
+
"""
|
|
611
|
+
handles a single pem operation to `openssl x509` with EXTENSION
|
|
612
|
+
/usr/local/bin/openssl x509 -noout -ext subjectAltName -in cert.pem
|
|
613
|
+
/usr/local/bin/openssl x509 -noout -ext authorityKeyIdentifier -in cert.pem
|
|
614
|
+
/usr/local/bin/openssl x509 -noout -ext authorityInfoAccess -in cert.pem
|
|
615
|
+
|
|
616
|
+
:param pem_filepath: filepath to the PEM encoded Certificate
|
|
617
|
+
:type pem_filepath: str
|
|
618
|
+
:param ext: a supported x509 extension
|
|
619
|
+
:type ext: str
|
|
620
|
+
:returns: openssl output value
|
|
621
|
+
:rtype: str
|
|
622
|
+
|
|
623
|
+
The OpenSSL Equivalent / Fallback is::
|
|
624
|
+
|
|
625
|
+
openssl x509 -noout -ext {EXT} -in {FILEPATH})
|
|
626
|
+
"""
|
|
627
|
+
if ext not in ("subjectAltName", "authorityKeyIdentifier", "authorityInfoAccess"):
|
|
628
|
+
raise ValueError("invalid `ext`")
|
|
629
|
+
if not pem_filepath:
|
|
630
|
+
raise FallbackError_FilepathRequired("Must submit `pem_filepath`.")
|
|
631
|
+
if core.openssl_version is None:
|
|
632
|
+
core.check_openssl_version()
|
|
633
|
+
|
|
634
|
+
with psutil.Popen(
|
|
635
|
+
[core.openssl_path, "x509", "-noout", "-ext", ext, "-in", pem_filepath],
|
|
636
|
+
stdout=subprocess.PIPE,
|
|
637
|
+
stderr=subprocess.PIPE,
|
|
638
|
+
) as proc:
|
|
639
|
+
data_bytes, err = proc.communicate()
|
|
640
|
+
if not data_bytes:
|
|
641
|
+
raise OpenSslError_InvalidCertificate(err)
|
|
642
|
+
data_str = data_bytes.decode("utf8")
|
|
643
|
+
data_str = data_str.strip()
|
|
644
|
+
return data_str
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def csr_single_op__pem_filepath(
|
|
648
|
+
pem_filepath: str,
|
|
649
|
+
single_op: str,
|
|
650
|
+
) -> str:
|
|
651
|
+
"""
|
|
652
|
+
handles a single pem operation to `openssl req` with EXTENSION
|
|
653
|
+
|
|
654
|
+
openssl req -noout -subject -in csr.pem
|
|
655
|
+
|
|
656
|
+
:param pem_filepath: filepath to the PEM encoded CSR.
|
|
657
|
+
:type pem_filepath: str
|
|
658
|
+
:param single_op: a supported `openssl req` operation
|
|
659
|
+
:type single_op: str
|
|
660
|
+
:returns: openssl output value
|
|
661
|
+
:rtype: str
|
|
662
|
+
|
|
663
|
+
The OpenSSL Equivalent / Fallback is::
|
|
664
|
+
|
|
665
|
+
openssl req -noout {OPERATION} -in {FILEPATH})
|
|
666
|
+
"""
|
|
667
|
+
if single_op not in ("-subject",):
|
|
668
|
+
raise ValueError("invalid `single_op`")
|
|
669
|
+
if not pem_filepath:
|
|
670
|
+
raise FallbackError_FilepathRequired("Must submit `pem_filepath`.")
|
|
671
|
+
if core.openssl_version is None:
|
|
672
|
+
core.check_openssl_version()
|
|
673
|
+
|
|
674
|
+
with psutil.Popen(
|
|
675
|
+
[core.openssl_path, "req", "-noout", single_op, "-in", pem_filepath],
|
|
676
|
+
stdout=subprocess.PIPE,
|
|
677
|
+
stderr=subprocess.PIPE,
|
|
678
|
+
) as proc:
|
|
679
|
+
data_bytes, err = proc.communicate()
|
|
680
|
+
if not data_bytes:
|
|
681
|
+
raise OpenSslError_InvalidCSR(err)
|
|
682
|
+
data_str = data_bytes.decode("utf8")
|
|
683
|
+
data_str = data_str.strip()
|
|
684
|
+
return data_str
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def key_single_op__pem_filepath(
|
|
688
|
+
keytype: str = "RSA",
|
|
689
|
+
pem_filepath: str = "",
|
|
690
|
+
single_op: str = "",
|
|
691
|
+
) -> str:
|
|
692
|
+
"""
|
|
693
|
+
:param keytype: the type of key: RSA or EC
|
|
694
|
+
:type keytype: str
|
|
695
|
+
:param pem_filepath: filepath to the PEM encoded Key
|
|
696
|
+
:type pem_filepath: str
|
|
697
|
+
:param single_op: a supported `openssl rsa/ec` operation
|
|
698
|
+
:type single_op: str
|
|
699
|
+
:returns: openssl output value
|
|
700
|
+
:rtype: str
|
|
701
|
+
|
|
702
|
+
THIS SHOULD NOT BE USED BY INTERNAL CODE
|
|
703
|
+
|
|
704
|
+
This is a bit odd...
|
|
705
|
+
|
|
706
|
+
1. If "-check" is okay (or reading is okay), there may be no output on stdout
|
|
707
|
+
HOWEVER
|
|
708
|
+
the read message (success) may happen on stderr
|
|
709
|
+
2. If openssl can't read the file, it will raise an exception
|
|
710
|
+
|
|
711
|
+
earlier versions of openssl DO NOT HAVE `ec --check`
|
|
712
|
+
current versions do
|
|
713
|
+
|
|
714
|
+
The OpenSSL Equivalent / Fallback is::
|
|
715
|
+
|
|
716
|
+
openssl {KEYTYPE} -noout {OPERATION} -in {FILEPATH})
|
|
717
|
+
|
|
718
|
+
Such as:
|
|
719
|
+
|
|
720
|
+
openssl rsa -noout -check -in {KEY}
|
|
721
|
+
openssl rsa -noout -modulus -in {KEY}
|
|
722
|
+
openssl rsa -noout -text -in {KEY}
|
|
723
|
+
|
|
724
|
+
openssl ec -noout -in {KEY}
|
|
725
|
+
openssl ec -noout -modulus -in {KEY}
|
|
726
|
+
openssl ec -noout -text -in {KEY}
|
|
727
|
+
"""
|
|
728
|
+
if keytype not in ("RSA", "EC"):
|
|
729
|
+
raise ValueError("keytype must be `RSA or EC`")
|
|
730
|
+
if single_op not in ("-check", "-modulus", "-text"):
|
|
731
|
+
raise ValueError("invalid `single_op`")
|
|
732
|
+
if not pem_filepath:
|
|
733
|
+
raise FallbackError_FilepathRequired("Must submit `pem_filepath`.")
|
|
734
|
+
if core.openssl_version is None:
|
|
735
|
+
core.check_openssl_version()
|
|
736
|
+
|
|
737
|
+
with psutil.Popen(
|
|
738
|
+
[core.openssl_path, keytype.lower(), "-noout", single_op, "-in", pem_filepath],
|
|
739
|
+
stdout=subprocess.PIPE,
|
|
740
|
+
stderr=subprocess.PIPE,
|
|
741
|
+
) as proc:
|
|
742
|
+
data_bytes, err = proc.communicate()
|
|
743
|
+
if not data_bytes:
|
|
744
|
+
if err.startswith(b"unknown option -check"):
|
|
745
|
+
raise OpenSslError_VersionTooLow(err)
|
|
746
|
+
elif proc.returncode or err not in (
|
|
747
|
+
b"read EC key\nEC Key valid.\n",
|
|
748
|
+
b"EC Key valid.\n",
|
|
749
|
+
):
|
|
750
|
+
# this happens, where some versions give an error and no data!
|
|
751
|
+
raise OpenSslError_InvalidKey(err)
|
|
752
|
+
data_str = data_bytes.decode("utf8")
|
|
753
|
+
data_str = data_str.strip()
|
|
754
|
+
return data_str
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def san_domains_from_text(text: str) -> List[str]:
|
|
758
|
+
"""
|
|
759
|
+
Helper function to extract SAN domains from a chunk of text in a x509 object
|
|
760
|
+
|
|
761
|
+
:param text: string extracted from a x509 document
|
|
762
|
+
:type text: str
|
|
763
|
+
:returns: list of domains
|
|
764
|
+
:rtype: list
|
|
765
|
+
"""
|
|
766
|
+
san_domains = set([])
|
|
767
|
+
_subject_alt_names = RE_openssl_x509_san.search(text)
|
|
768
|
+
if _subject_alt_names is not None:
|
|
769
|
+
for _san in _subject_alt_names.group(1).split(", "):
|
|
770
|
+
if _san.startswith("DNS:"):
|
|
771
|
+
san_domains.add(_san[4:].lower())
|
|
772
|
+
return sorted(list(san_domains))
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def authority_key_identifier_from_text(text: str) -> Optional[str]:
|
|
776
|
+
"""
|
|
777
|
+
:param text: string extracted from a x509 document
|
|
778
|
+
:type text: str
|
|
779
|
+
:returns: authority_key_identifier
|
|
780
|
+
:rtype: str
|
|
781
|
+
|
|
782
|
+
openssl will print a uppercase hex pairs, separated by a colon
|
|
783
|
+
we should remove the colons
|
|
784
|
+
"""
|
|
785
|
+
results = RE_openssl_x509_authority_key_identifier.findall(text)
|
|
786
|
+
if results:
|
|
787
|
+
authority_key_identifier = results[0]
|
|
788
|
+
# ensure we have a key_id and not "URI:" or other convention
|
|
789
|
+
if authority_key_identifier[2] == ":":
|
|
790
|
+
return authority_key_identifier.replace(":", "")
|
|
791
|
+
return None
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def serial_from_text(text: str) -> Optional[int]:
|
|
795
|
+
"""
|
|
796
|
+
:param text: string extracted from a x509 document
|
|
797
|
+
:type text: str
|
|
798
|
+
:returns: serial
|
|
799
|
+
:rtype: int
|
|
800
|
+
"""
|
|
801
|
+
results = RE_openssl_x509_serial.findall(text)
|
|
802
|
+
if results:
|
|
803
|
+
serial = results[0]
|
|
804
|
+
return int(serial)
|
|
805
|
+
return None
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def issuer_uri_from_text(text: str) -> Optional[str]:
|
|
809
|
+
"""
|
|
810
|
+
:param text: string extracted from a x509 document
|
|
811
|
+
:type text: str
|
|
812
|
+
:returns: issuer_uri
|
|
813
|
+
:rtype: str
|
|
814
|
+
"""
|
|
815
|
+
results = RE_openssl_x509_issuer_uri.findall(text)
|
|
816
|
+
if results:
|
|
817
|
+
return results[0]
|
|
818
|
+
return None
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
def _x509_pubkey_technology__text(cert_text: str) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
822
|
+
m = RE_openssl_x509_keytype_rsa.search(cert_text)
|
|
823
|
+
if m:
|
|
824
|
+
v = m.groups()
|
|
825
|
+
return ("RSA", (int(v[0]),))
|
|
826
|
+
m = RE_openssl_x509_keytype_ec.search(cert_text)
|
|
827
|
+
if m:
|
|
828
|
+
v = m.groups()
|
|
829
|
+
c = None
|
|
830
|
+
if v[2]:
|
|
831
|
+
c = v[2]
|
|
832
|
+
elif v[0]:
|
|
833
|
+
c = "P-%s" % v[0]
|
|
834
|
+
else:
|
|
835
|
+
# TODO? better parsing
|
|
836
|
+
return None
|
|
837
|
+
return ("EC", (c,))
|
|
838
|
+
return None
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def _cert_pubkey_technology__text(cert_text: str) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
842
|
+
"""
|
|
843
|
+
:param cert_text: string extracted from a x509 document
|
|
844
|
+
:type cert_text: str
|
|
845
|
+
:returns: Pubkey type: "RSA" or "EC"
|
|
846
|
+
:rtype: strfRE_openssl_x509_keytype_rsa
|
|
847
|
+
"""
|
|
848
|
+
# `cert_text` is the output of of `openssl x509 -noout -text -in MYCERT `
|
|
849
|
+
return _x509_pubkey_technology__text(cert_text)
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def _csr_pubkey_technology__text(csr_text: str) -> Optional[TECHNOLOGY_RETURN_VALUES]:
|
|
853
|
+
"""
|
|
854
|
+
:param csr_text: string extracted from a CSR document
|
|
855
|
+
:type csr_text: str
|
|
856
|
+
:returns: Pubkey type: "RSA" or "EC"
|
|
857
|
+
:rtype: str
|
|
858
|
+
"""
|
|
859
|
+
# `csr_text` is the output of of `openssl req -noout -text -in MYCERT`
|
|
860
|
+
return _x509_pubkey_technology__text(csr_text)
|