x509kit 0.3.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.
- x509kit-0.3.1/CHANGELOG.md +120 -0
- x509kit-0.3.1/LICENSE +21 -0
- x509kit-0.3.1/MANIFEST.in +13 -0
- x509kit-0.3.1/PKG-INFO +304 -0
- x509kit-0.3.1/README.md +268 -0
- x509kit-0.3.1/SECURITY.md +158 -0
- x509kit-0.3.1/check_dispatcher.py +141 -0
- x509kit-0.3.1/check_docs.py +256 -0
- x509kit-0.3.1/check_no_hidden.py +124 -0
- x509kit-0.3.1/compat_test.py +176 -0
- x509kit-0.3.1/examples/01_root_ca.py +29 -0
- x509kit-0.3.1/examples/02_issue_from_csr.py +42 -0
- x509kit-0.3.1/examples/03_self_signed_server.py +63 -0
- x509kit-0.3.1/examples/04_mtls_server.py +102 -0
- x509kit-0.3.1/examples/05_crl_revocation.py +50 -0
- x509kit-0.3.1/examples/06_ocsp_responder.py +48 -0
- x509kit-0.3.1/examples/07_pkcs12_export.py +49 -0
- x509kit-0.3.1/examples/08_parse_real_cert.py +42 -0
- x509kit-0.3.1/examples/09_encrypted_key.py +37 -0
- x509kit-0.3.1/examples/10_verify_chain.py +47 -0
- x509kit-0.3.1/examples/11_name_canonicalization.py +27 -0
- x509kit-0.3.1/examples/12_extensions.py +40 -0
- x509kit-0.3.1/examples/README.md +37 -0
- x509kit-0.3.1/flask/test_live_ca.py +407 -0
- x509kit-0.3.1/flask/test_mtls.py +362 -0
- x509kit-0.3.1/fuzz_x509.py +298 -0
- x509kit-0.3.1/pyproject.toml +78 -0
- x509kit-0.3.1/setup.cfg +4 -0
- x509kit-0.3.1/src/x509kit/__init__.py +338 -0
- x509kit-0.3.1/src/x509kit/py.typed +0 -0
- x509kit-0.3.1/src/x509kit/x509.py +6383 -0
- x509kit-0.3.1/src/x509kit.egg-info/PKG-INFO +304 -0
- x509kit-0.3.1/src/x509kit.egg-info/SOURCES.txt +35 -0
- x509kit-0.3.1/src/x509kit.egg-info/dependency_links.txt +1 -0
- x509kit-0.3.1/src/x509kit.egg-info/requires.txt +6 -0
- x509kit-0.3.1/src/x509kit.egg-info/top_level.txt +1 -0
- x509kit-0.3.1/test_all.py +1025 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project.
|
|
4
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
Versions follow [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Planned
|
|
10
|
+
- Full type annotations on public API
|
|
11
|
+
- `Name.from_string("CN=x, O=y, C=XX")` parser
|
|
12
|
+
- `Cert.from_url("https://...")` convenience
|
|
13
|
+
- `X25519Keys` for ECDH
|
|
14
|
+
- ACME (RFC 8555) compatibility
|
|
15
|
+
- Coverage reporting in CI
|
|
16
|
+
|
|
17
|
+
## [0.3.0] - 2026-10-10
|
|
18
|
+
|
|
19
|
+
First public release.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
**Cryptography:**
|
|
24
|
+
- RSA PKCS#1 v1.5 + RSASSA-PSS with blinding and CRT
|
|
25
|
+
- Ed25519 (RFC 8032)
|
|
26
|
+
- ECDSA (P-256, P-384, P-521, secp256k1) with RFC 6979
|
|
27
|
+
- AES-256 (FIPS 197) and AES-256-GCM (NIST SP 800-38D), pure Python
|
|
28
|
+
- PKCS#8 EncryptedPrivateKeyInfo (PBES2 + PBKDF2 + AES-256-CBC/GCM)
|
|
29
|
+
|
|
30
|
+
**X.509 v3:**
|
|
31
|
+
- Full DER parser and encoder (strict, minimal-length, depth-limited)
|
|
32
|
+
- PEM codec
|
|
33
|
+
- All standard extensions: SAN, BasicConstraints, KeyUsage,
|
|
34
|
+
Extended Key Usage, Name Constraints (with intersection), AKI/SKI,
|
|
35
|
+
CRL Distribution Points, AIA
|
|
36
|
+
- RFC 4518 DN canonicalization
|
|
37
|
+
- RFC 6125 hostname validation
|
|
38
|
+
|
|
39
|
+
**Protocols:**
|
|
40
|
+
- PKCS#10 CertificationRequest (create, parse, verify)
|
|
41
|
+
- CRL (RFC 5280): builder, parser, verification
|
|
42
|
+
- OCSP (RFC 6960): request, response, nonce
|
|
43
|
+
- PKCS#12 (RFC 7292): export, import, MAC verification
|
|
44
|
+
|
|
45
|
+
**API:**
|
|
46
|
+
- Fluent builders: `CertificateBuilder`, `CSRBuilder`, `CRLBuilder`,
|
|
47
|
+
`OCSPResponseBuilder`, `PKCS12Builder`
|
|
48
|
+
- `X509Error` with fix hints, `X509Warning` for non-fatal issues
|
|
49
|
+
- Crypto dispatcher: single point for all key-type logic
|
|
50
|
+
- SAN convenience accessors on `Cert` and `CSR`: `dns_names`,
|
|
51
|
+
`ip_addresses`, `emails`, `uris`
|
|
52
|
+
- `Cert.crl_distribution_points` reader
|
|
53
|
+
- `__eq__` / `__hash__` for `Name`, `Cert`, `RSAKeys`, `Ed25519Keys`,
|
|
54
|
+
`ECDSAKeys`, `CertBundle`, `CRL`, `CSR`
|
|
55
|
+
|
|
56
|
+
**Safety:**
|
|
57
|
+
- DoS limits: `_MAX_DER_SIZE = 512 KB`, `_MAX_PEM_SIZE = 1 MB`,
|
|
58
|
+
`_MAX_DEPTH = 32`
|
|
59
|
+
- `_safe_parse` decorator wraps all parser constructors; malformed
|
|
60
|
+
input raises `X509ParseError` instead of leaking `ValueError`,
|
|
61
|
+
`TypeError`, `IndexError`
|
|
62
|
+
- `check_no_hidden.py` — AST-based proof of zero third-party imports
|
|
63
|
+
|
|
64
|
+
**Tests and tools:**
|
|
65
|
+
- `test_all.py` — ~210 test cases
|
|
66
|
+
- `fuzz_x509.py` — random input fuzzing
|
|
67
|
+
- `stress_x509kit.py` — heavy stress test (parser fuzz, mutation,
|
|
68
|
+
crypto ops, GC churn)
|
|
69
|
+
- `compat_test.py` — cross-verify with `cryptography` (12/12)
|
|
70
|
+
- `test_live_ca.py` — full PKI cycle through OpenSSL CLI
|
|
71
|
+
- `examples/` — 12 runnable example scripts
|
|
72
|
+
- `check_docs.py`, `check_dispatcher.py`
|
|
73
|
+
|
|
74
|
+
**CI:**
|
|
75
|
+
- GitHub Actions: tests on Python 3.9–3.13
|
|
76
|
+
- Doc and dispatcher checks
|
|
77
|
+
|
|
78
|
+
### Cross-verified against
|
|
79
|
+
- `cryptography` (RSA, Ed25519, ECDSA, X.509 parsing)
|
|
80
|
+
- OpenSSL 3.6.5 CLI (`verify`, `s_client`, `crl_check`, `pkey`,
|
|
81
|
+
`pkcs12`)
|
|
82
|
+
- RFC 8032 Ed25519 test vectors (byte-exact)
|
|
83
|
+
- RFC 6979 ECDSA test vectors (byte-exact)
|
|
84
|
+
- FIPS 197 AES-256 test vector
|
|
85
|
+
- NIST SP 800-38D GCM test vectors
|
|
86
|
+
|
|
87
|
+
### Known limitations
|
|
88
|
+
- Not constant-time (see `SECURITY.md`)
|
|
89
|
+
- No formal audit
|
|
90
|
+
- No production hardening
|
|
91
|
+
- Not for adversarial environments
|
|
92
|
+
|
|
93
|
+
## [0.2.0] - development
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
- Ed25519 keys
|
|
97
|
+
- ECDSA keys (P-256, P-384, P-521, secp256k1) with RFC 6979
|
|
98
|
+
- RSA blinding and CRT
|
|
99
|
+
- AES-256 (FIPS 197) and AES-256-GCM (NIST SP 800-38D)
|
|
100
|
+
- PKCS#8 EncryptedPrivateKeyInfo
|
|
101
|
+
- X.509 v3: SAN, BasicConstraints, KeyUsage, EKU, AKI/SKI
|
|
102
|
+
- CRL (RFC 5280)
|
|
103
|
+
- Fuzzing tests
|
|
104
|
+
- Live CA test with OpenSSL CLI
|
|
105
|
+
- mTLS test with PKCS#12
|
|
106
|
+
|
|
107
|
+
### Changed
|
|
108
|
+
- RSA minimum key size raised to 2048 bits
|
|
109
|
+
- `weak_hash` and `weak` flags added
|
|
110
|
+
|
|
111
|
+
## [0.1.0] - initial
|
|
112
|
+
|
|
113
|
+
### Added
|
|
114
|
+
- ASN.1 DER parser and encoder
|
|
115
|
+
- PEM codec
|
|
116
|
+
- X.509 v3 parsing and verification
|
|
117
|
+
- PKCS#10 CSR
|
|
118
|
+
- Basic chain validation with path_len
|
|
119
|
+
- RFC 6125 hostname validation
|
|
120
|
+
- Mini HTTP CA server
|
x509kit-0.3.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
include CHANGELOG.md
|
|
4
|
+
include SECURITY.md
|
|
5
|
+
recursive-include examples *.py *.md
|
|
6
|
+
recursive-include flask *.py
|
|
7
|
+
include test_all.py
|
|
8
|
+
include compat_test.py
|
|
9
|
+
include check_docs.py
|
|
10
|
+
include check_dispatcher.py
|
|
11
|
+
include check_no_hidden.py
|
|
12
|
+
include fuzz_x509.py
|
|
13
|
+
include stress_x509kit.py
|
x509kit-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: x509kit
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Pure-Python X.509 / PKCS#10 / CRL / OCSP / PKCS#12 toolkit — stdlib only
|
|
5
|
+
Author-email: ArtProsto <artemkamirn@mail.ru>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/artem122py/x509kit
|
|
8
|
+
Project-URL: Documentation, https://github.com/artem122py/x509kit#readme
|
|
9
|
+
Project-URL: Source, https://github.com/artem122py/x509kit
|
|
10
|
+
Project-URL: Issues, https://github.com/artem122py/x509kit/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/artem122py/x509kit/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: x509,certificate,pki,csr,crl,ocsp,pkcs12,asn1,der,pem,rsa,ed25519,ecdsa,tls,mtls,cryptography,stdlib
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Security :: Cryptography
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
32
|
+
Requires-Dist: build; extra == "dev"
|
|
33
|
+
Requires-Dist: twine; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# x509kit
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+

|
|
41
|
+

|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
Pure-Python X.509 / PKCS#10 / CRL / OCSP / PKCS#12 toolkit.
|
|
46
|
+
**Standard library only.**
|
|
47
|
+
|
|
48
|
+
No third-party packages. No C extensions. No vendored code.
|
|
49
|
+
|
|
50
|
+
## Zero third-party dependencies
|
|
51
|
+
|
|
52
|
+
`x509kit` uses only the Python standard library. No `cryptography`,
|
|
53
|
+
no `pyOpenSSL`, no `asn1crypto`, no `pycryptodome`, no C extensions.
|
|
54
|
+
|
|
55
|
+
$ pip show x509kit | grep "^Requires"
|
|
56
|
+
Requires:
|
|
57
|
+
|
|
58
|
+
$ grep -rhE "^\s*(import|from) " src/x509kit/ \
|
|
59
|
+
| awk '{print $2}' | cut -d. -f1 | sort -u
|
|
60
|
+
base64
|
|
61
|
+
datetime
|
|
62
|
+
hashlib
|
|
63
|
+
hmac
|
|
64
|
+
math
|
|
65
|
+
os
|
|
66
|
+
re
|
|
67
|
+
secrets
|
|
68
|
+
socket
|
|
69
|
+
unicodedata
|
|
70
|
+
warnings
|
|
71
|
+
|
|
72
|
+
All of these are in the Python standard library. Nothing else.
|
|
73
|
+
|
|
74
|
+
### Why stdlib only
|
|
75
|
+
|
|
76
|
+
- **No transitive CVEs.** Fewer packages — fewer places for something
|
|
77
|
+
to go wrong.
|
|
78
|
+
- **Embedded / restricted environments** where you cannot install
|
|
79
|
+
third-party packages.
|
|
80
|
+
- **Air-gapped systems** — drop a single package and run.
|
|
81
|
+
- **Readable.** The whole implementation is in one place.
|
|
82
|
+
- **Auditable.** The attack surface is exactly one package.
|
|
83
|
+
|
|
84
|
+
### What is implemented from scratch
|
|
85
|
+
|
|
86
|
+
Every protocol and primitive is our own Python code:
|
|
87
|
+
|
|
88
|
+
- ASN.1 DER parser and encoder (strict, minimal-length)
|
|
89
|
+
- RSA PKCS#1 v1.5 and RSASSA-PSS (with blinding and CRT)
|
|
90
|
+
- Ed25519 (RFC 8032)
|
|
91
|
+
- ECDSA (RFC 6979 deterministic nonces)
|
|
92
|
+
- AES-256 block cipher (FIPS 197) and GCM mode (NIST SP 800-38D)
|
|
93
|
+
- PBKDF2 and PKCS#12 KDF
|
|
94
|
+
- Every X.509 v3, PKCS#10, PKCS#12, CRL, OCSP structure
|
|
95
|
+
- RFC 4518 DN canonicalization
|
|
96
|
+
|
|
97
|
+
Hashing (`hashlib`), MAC (`hmac`), and randomness (`secrets`) come from
|
|
98
|
+
the standard library — writing a custom SHA-256 in Python would be
|
|
99
|
+
slower and less tested than what the interpreter already ships.
|
|
100
|
+
|
|
101
|
+
`cryptography` and the `openssl` binary appear only in tests, as
|
|
102
|
+
independent verifiers of our output.
|
|
103
|
+
|
|
104
|
+
## Verify it is really stdlib-only
|
|
105
|
+
|
|
106
|
+
Do not grep for the word "cryptography" — it appears in documentation
|
|
107
|
+
comments saying "we do not use it". Instead, run this:
|
|
108
|
+
|
|
109
|
+
python check_no_hidden.py
|
|
110
|
+
|
|
111
|
+
This script uses Python's `ast` module to extract every import statement
|
|
112
|
+
from every `.py` file under `src/`. It compares the result against
|
|
113
|
+
`sys.stdlib_module_names` and a small blacklist of crypto libraries.
|
|
114
|
+
|
|
115
|
+
Expected output ends with:
|
|
116
|
+
|
|
117
|
+
RESULT: OK - stdlib only
|
|
118
|
+
|
|
119
|
+
If it prints anything else, that is a bug — please open an issue.
|
|
120
|
+
|
|
121
|
+
## Verify in 30 seconds
|
|
122
|
+
|
|
123
|
+
Not asking you to trust me — asking you to run this:
|
|
124
|
+
|
|
125
|
+
git clone https://github.com/artem122py/x509kit
|
|
126
|
+
cd x509kit
|
|
127
|
+
pip install -e .
|
|
128
|
+
python flask/test_live_ca.py
|
|
129
|
+
|
|
130
|
+
In one go, this script:
|
|
131
|
+
|
|
132
|
+
- Generates Root CA + Intermediate in memory (nothing on disk)
|
|
133
|
+
- Issues a server leaf certificate for a Flask server
|
|
134
|
+
- Starts Flask on HTTPS with the leaf + intermediate chain
|
|
135
|
+
- Calls the real OpenSSL CLI to generate a key and a CSR
|
|
136
|
+
- POSTs the CSR to the server, gets a leaf back
|
|
137
|
+
- Runs `openssl verify -CAfile root -untrusted inter leaf.pem`
|
|
138
|
+
→ expected output: `leaf.pem: OK`
|
|
139
|
+
- Revokes the leaf via `/revoke`
|
|
140
|
+
- Runs `openssl verify -CRLfile crl.pem -crl_check`
|
|
141
|
+
→ expected output: `error 23 at 0 depth lookup: certificate revoked`
|
|
142
|
+
|
|
143
|
+
The verification step is done by OpenSSL 3.x, called via subprocess.
|
|
144
|
+
It is not our own verify() method. If our DER encoding were wrong,
|
|
145
|
+
OpenSSL would reject it — and it doesn't.
|
|
146
|
+
|
|
147
|
+
## Cross-verify matrix
|
|
148
|
+
|
|
149
|
+
The library is not self-tested. It is tested against external
|
|
150
|
+
implementations, in both directions:
|
|
151
|
+
|
|
152
|
+
| Component | Independent verifier | Result |
|
|
153
|
+
|-----------|---------------------|--------|
|
|
154
|
+
| RSA (PKCS#1 v1.5, PSS) | cryptography + openssl pkeyutl | both ways |
|
|
155
|
+
| Ed25519 | RFC 8032 test vectors (byte-exact), cryptography | both ways |
|
|
156
|
+
| ECDSA (P-256/384/521/secp256k1) | RFC 6979 test vectors (byte-exact), cryptography | both ways |
|
|
157
|
+
| AES-256-CBC | FIPS 197 test vector | byte-exact |
|
|
158
|
+
| AES-256-GCM | NIST SP 800-38D test vectors | byte-exact |
|
|
159
|
+
| X.509 v3 certificates | openssl x509 -text, openssl verify | OK |
|
|
160
|
+
| Chains (root → intermediate → leaf) | Python ssl TLS handshake | OK |
|
|
161
|
+
| CRL | openssl crl -text, openssl verify -crl_check | error 23 |
|
|
162
|
+
| PKCS#12 (.p12) | openssl pkcs12 -info -nodes | 3 blocks extracted |
|
|
163
|
+
| PKCS#8 EncryptedPrivateKeyInfo | openssl pkey | OK |
|
|
164
|
+
| mTLS | openssl s_client -cert -key | handshake OK |
|
|
165
|
+
|
|
166
|
+
Run it yourself:
|
|
167
|
+
|
|
168
|
+
python test_all.py
|
|
169
|
+
|
|
170
|
+
## Compatibility with cryptography
|
|
171
|
+
|
|
172
|
+
Run this yourself — it verifies that x509kit and `cryptography` agree
|
|
173
|
+
in **both directions**, for every primitive:
|
|
174
|
+
|
|
175
|
+
python compat_test.py
|
|
176
|
+
|
|
177
|
+
Expected output:
|
|
178
|
+
|
|
179
|
+
============================================================
|
|
180
|
+
x509kit ↔ cryptography
|
|
181
|
+
============================================================
|
|
182
|
+
|
|
183
|
+
1. RSA PKCS#1 v1.5
|
|
184
|
+
✓ наш RSA sign → их verify
|
|
185
|
+
✓ их RSA sign → наш verify
|
|
186
|
+
|
|
187
|
+
2. Ed25519 (RFC 8032)
|
|
188
|
+
✓ seed → тот же pubkey
|
|
189
|
+
✓ наш Ed sign → их verify
|
|
190
|
+
✓ их Ed sign → наш verify
|
|
191
|
+
|
|
192
|
+
3. ECDSA P-256
|
|
193
|
+
✓ наш EC sign → их verify
|
|
194
|
+
✓ их EC sign → наш verify
|
|
195
|
+
|
|
196
|
+
4. X.509 сертификат
|
|
197
|
+
✓ наш PEM → их x509 парсер
|
|
198
|
+
✓ серийник совпадает
|
|
199
|
+
✓ их PEM → наш Cert парсер
|
|
200
|
+
|
|
201
|
+
5. Полная цепочка
|
|
202
|
+
✓ их SAN читает наш leaf
|
|
203
|
+
✓ их CA парсится нашим CACert
|
|
204
|
+
|
|
205
|
+
============================================================
|
|
206
|
+
12/12 passed
|
|
207
|
+
============================================================
|
|
208
|
+
|
|
209
|
+
This is not a self-test. Every check calls into `cryptography` (which
|
|
210
|
+
links against OpenSSL) and asks it to verify our output, or vice versa.
|
|
211
|
+
If any row fails, our implementation is wrong.
|
|
212
|
+
|
|
213
|
+
Requires: `pip install cryptography`
|
|
214
|
+
|
|
215
|
+
## Examples
|
|
216
|
+
|
|
217
|
+
See [`examples/`](examples/) — 12 runnable scripts covering the whole
|
|
218
|
+
library:
|
|
219
|
+
|
|
220
|
+
- `01_root_ca.py` — self-signed CA
|
|
221
|
+
- `02_issue_from_csr.py` — issue leaf from CSR
|
|
222
|
+
- `03_self_signed_server.py` — HTTPS with self-signed cert
|
|
223
|
+
- `04_mtls_server.py` — mutual TLS
|
|
224
|
+
- `05_crl_revocation.py` — revoke and generate CRL
|
|
225
|
+
- `06_ocsp_responder.py` — OCSP response
|
|
226
|
+
- `07_pkcs12_export.py` — export to `.p12`
|
|
227
|
+
- `08_parse_real_cert.py` — fetch and parse a live HTTPS cert
|
|
228
|
+
- `09_encrypted_key.py` — encrypted PKCS#8 keys
|
|
229
|
+
- `10_verify_chain.py` — chain verification scenarios
|
|
230
|
+
- `11_name_canonicalization.py` — RFC 4518
|
|
231
|
+
- `12_extensions.py` — all extensions in one cert
|
|
232
|
+
|
|
233
|
+
Each is self-contained, prints what it does, and shows the OpenSSL
|
|
234
|
+
command to verify the result.
|
|
235
|
+
|
|
236
|
+
## What this is
|
|
237
|
+
|
|
238
|
+
A learning project that turned into a working implementation. It exists
|
|
239
|
+
to answer the question: what actually happens inside an X.509
|
|
240
|
+
certificate — by writing it from scratch, on the standard library, and
|
|
241
|
+
checking the result against OpenSSL until they agree.
|
|
242
|
+
|
|
243
|
+
## What this is not
|
|
244
|
+
|
|
245
|
+
Not a replacement for OpenSSL or cryptography in production. See
|
|
246
|
+
[SECURITY.md](SECURITY.md) for the full list of what is covered and
|
|
247
|
+
what is not. Short version: no constant-time guarantees, no formal
|
|
248
|
+
audit, no production hardening.
|
|
249
|
+
|
|
250
|
+
If you need production-grade crypto: use cryptography.
|
|
251
|
+
If you want to understand X.509 by reading Python that OpenSSL agrees
|
|
252
|
+
with: this is for you.
|
|
253
|
+
|
|
254
|
+
## Install
|
|
255
|
+
|
|
256
|
+
pip install x509kit
|
|
257
|
+
|
|
258
|
+
## Quick example
|
|
259
|
+
|
|
260
|
+
from x509kit import CertificateBuilder, Ed25519Keys, RSAKeys
|
|
261
|
+
|
|
262
|
+
ca_key = Ed25519Keys.generate()
|
|
263
|
+
ca = (CertificateBuilder(is_ca=True)
|
|
264
|
+
.add_subject({"C": "XX", "O": "MyOrg", "CN": "MyOrg Root CA"})
|
|
265
|
+
.add_public_key(ca_key)
|
|
266
|
+
.add_serial_random().add_validity(days=3650)
|
|
267
|
+
.add_basic_constraints(path_len=0)
|
|
268
|
+
.add_key_usage("keyCertSign", "cRLSign")
|
|
269
|
+
.self_signed().sign(ca_key))
|
|
270
|
+
|
|
271
|
+
leaf_key = RSAKeys.generate(2048)
|
|
272
|
+
leaf = (CertificateBuilder()
|
|
273
|
+
.add_subject({"C": "XX", "O": "MyOrg", "CN": "example.com"})
|
|
274
|
+
.add_issuer(ca).add_public_key(leaf_key)
|
|
275
|
+
.add_serial_random().add_validity(days=365)
|
|
276
|
+
.add_san_dns("example.com", "www.example.com")
|
|
277
|
+
.add_extended_key_usage("serverAuth")
|
|
278
|
+
.sign(ca_key))
|
|
279
|
+
|
|
280
|
+
assert ca.verify_chain([leaf])
|
|
281
|
+
|
|
282
|
+
## Features
|
|
283
|
+
|
|
284
|
+
- ASN.1 DER parser and encoder (strict, minimal-length)
|
|
285
|
+
- PEM codec
|
|
286
|
+
- RSA PKCS#1 v1.5 + RSASSA-PSS (blinding + CRT)
|
|
287
|
+
- Ed25519 (RFC 8032), ECDSA (P-256/384/521/secp256k1, RFC 6979)
|
|
288
|
+
- X.509 v3: SAN, BasicConstraints, KeyUsage, Extended Key Usage,
|
|
289
|
+
Name Constraints (with intersection), AKI/SKI, CRL Distribution Points, AIA
|
|
290
|
+
- PKCS#10 CSR, CRL (RFC 5280), OCSP (RFC 6960), PKCS#12 (RFC 7292)
|
|
291
|
+
- PKCS#8 EncryptedPrivateKeyInfo (PBES2 + PBKDF2 + AES-256-CBC/GCM)
|
|
292
|
+
- RFC 4518 DN canonicalization
|
|
293
|
+
- Fluent builders: CertificateBuilder, CSRBuilder, CRLBuilder,
|
|
294
|
+
OCSPResponseBuilder, PKCS12Builder
|
|
295
|
+
- X509Error with fix hints, X509Warning for non-fatal issues
|
|
296
|
+
|
|
297
|
+
## Status
|
|
298
|
+
|
|
299
|
+
Alpha (0.3.0). Educational. Not for production. See
|
|
300
|
+
[SECURITY.md](SECURITY.md).
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|