pki-core 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.
- pki_core-0.3.1/LICENSE +28 -0
- pki_core-0.3.1/PKG-INFO +358 -0
- pki_core-0.3.1/README.md +324 -0
- pki_core-0.3.1/pyproject.toml +89 -0
- pki_core-0.3.1/setup.cfg +4 -0
- pki_core-0.3.1/src/pki/core/__init__.py +36 -0
- pki_core-0.3.1/src/pki/core/algorithms.py +123 -0
- pki_core-0.3.1/src/pki/core/certificate.py +120 -0
- pki_core-0.3.1/src/pki/core/crl.py +374 -0
- pki_core-0.3.1/src/pki/core/identity.py +124 -0
- pki_core-0.3.1/src/pki/core/providers.py +123 -0
- pki_core-0.3.1/src/pki/core/revocation.py +313 -0
- pki_core-0.3.1/src/pki/core/selectors.py +41 -0
- pki_core-0.3.1/src/pki/core/trust_store.py +224 -0
- pki_core-0.3.1/src/pki/core/validation.py +257 -0
- pki_core-0.3.1/src/pki_core.egg-info/PKG-INFO +358 -0
- pki_core-0.3.1/src/pki_core.egg-info/SOURCES.txt +35 -0
- pki_core-0.3.1/src/pki_core.egg-info/dependency_links.txt +1 -0
- pki_core-0.3.1/src/pki_core.egg-info/requires.txt +12 -0
- pki_core-0.3.1/src/pki_core.egg-info/top_level.txt +1 -0
- pki_core-0.3.1/tests/test_algorithms.py +210 -0
- pki_core-0.3.1/tests/test_certificate.py +136 -0
- pki_core-0.3.1/tests/test_chain_validation.py +95 -0
- pki_core-0.3.1/tests/test_crl.py +117 -0
- pki_core-0.3.1/tests/test_fuzz_algorithms.py +170 -0
- pki_core-0.3.1/tests/test_fuzz_certificate.py +230 -0
- pki_core-0.3.1/tests/test_fuzz_crl.py +185 -0
- pki_core-0.3.1/tests/test_fuzz_identity.py +234 -0
- pki_core-0.3.1/tests/test_fuzz_providers.py +213 -0
- pki_core-0.3.1/tests/test_fuzz_revocation.py +201 -0
- pki_core-0.3.1/tests/test_fuzz_trust_store.py +148 -0
- pki_core-0.3.1/tests/test_identity.py +52 -0
- pki_core-0.3.1/tests/test_providers.py +88 -0
- pki_core-0.3.1/tests/test_revocation.py +242 -0
- pki_core-0.3.1/tests/test_selectors.py +50 -0
- pki_core-0.3.1/tests/test_trust_store.py +85 -0
- pki_core-0.3.1/tests/test_validation.py +43 -0
pki_core-0.3.1/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, MeV Technology Consulting, LLC
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
pki_core-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pki-core
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Generic X.509 certificate utilities: parsing, CRL caching, identity extraction, provider registry, and validation pipeline.
|
|
5
|
+
Author: MeV Technology Consulting, LLC
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://oss.mevtc.com
|
|
8
|
+
Project-URL: Repository, https://github.com/mevtc/pki-core
|
|
9
|
+
Project-URL: Issues, https://github.com/mevtc/pki-core/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/mevtc/pki-core/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: pki,x509,certificate,crl,identity,validation
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Security :: Cryptography
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: cryptography>=44.0
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Requires-Dist: platformdirs>=4.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=6.0; extra == "dev"
|
|
28
|
+
Requires-Dist: hypothesis>=6.100; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.9.0; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.14; extra == "dev"
|
|
31
|
+
Requires-Dist: bandit>=1.8; extra == "dev"
|
|
32
|
+
Requires-Dist: pre-commit>=4.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# pki-core
|
|
36
|
+
|
|
37
|
+
Generic X.509 certificate utilities for PKI-enabled applications.
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Certificate parsing** — load PEM/DER x509 certificates, extract policy OIDs, emails, SAN URIs/UUIDs, FASC-N, and fingerprints
|
|
42
|
+
- **Identity extraction** — parse certificate identity using a pluggable provider registry with callable CN parsers and ID selectors
|
|
43
|
+
- **Chain validation** — RFC 5280 certificate path validation via `verify_chain()`
|
|
44
|
+
- **Revocation checking** — pluggable strategies (`CRL`, `OCSP`) with configurable order and fallback
|
|
45
|
+
- **Algorithm enforcement** — `AlgorithmPolicy` validates key type/size and signature hash
|
|
46
|
+
- **Trust store management** — download, merge, and deduplicate CA bundles from provider-defined sources
|
|
47
|
+
- **Validation pipeline** — `validate_certificate()` composes chain validation, algorithm checking, identity extraction, expiry, and revocation into a single call
|
|
48
|
+
- **Provider registry** — pluggable authentication provider definitions with OID matching and heuristic detection
|
|
49
|
+
|
|
50
|
+
## Architecture
|
|
51
|
+
|
|
52
|
+
pki-core is a PKI framework with a pluggable provider system. **Provider packs** supply ecosystem-specific definitions (OIDs, CN parsers, trust store sources) while pki-core handles the generic infrastructure.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
pki.core ← framework (this package)
|
|
56
|
+
pki.federal ← DoD CAC / Federal PIV / ECA provider pack
|
|
57
|
+
pki.mycorp ← your organization's provider pack
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
All packages share the `pki` namespace via Python's implicit namespace packages (PEP 420). Each is installed independently.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install pki-core
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
### Minimal — parse and validate a certificate
|
|
71
|
+
|
|
72
|
+
The simplest usage: load a certificate, check expiry, and extract identity.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from pki.core.certificate import load_certificate
|
|
76
|
+
from pki.core.validation import CertificatePolicy, ValidationStatus, validate_certificate
|
|
77
|
+
|
|
78
|
+
cert = load_certificate(pem_bytes)
|
|
79
|
+
result = validate_certificate(cert)
|
|
80
|
+
|
|
81
|
+
if result.status == ValidationStatus.VALID:
|
|
82
|
+
print(f"Valid: {result.identity.cn}")
|
|
83
|
+
else:
|
|
84
|
+
print(f"Failed: {result.status} — {result.error}")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Full — chain validation, algorithm policy, CRL + OCSP, Federal PKI
|
|
88
|
+
|
|
89
|
+
A production configuration using pki-federal with all validation steps enabled.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from pki.core.certificate import load_certificate
|
|
93
|
+
from pki.core.crl import CRLConfig, load_ca_certs_from_pem
|
|
94
|
+
from pki.core.revocation import CRL, OCSP, RevocationPolicy
|
|
95
|
+
from pki.core.validation import CertificatePolicy, ValidationStatus, validate_certificate
|
|
96
|
+
from pki.federal import SP800_78_ALGORITHM_POLICY, default_registry
|
|
97
|
+
|
|
98
|
+
# Load the certificate
|
|
99
|
+
cert = load_certificate(pem_bytes)
|
|
100
|
+
|
|
101
|
+
# Load CA bundle (built from build_ca_bundle_for_providers() or downloaded)
|
|
102
|
+
ca_certs = load_ca_certs_from_pem(open("/etc/pki/ca-bundle.pem", "rb").read())
|
|
103
|
+
|
|
104
|
+
# Configure the full validation pipeline
|
|
105
|
+
policy = CertificatePolicy(
|
|
106
|
+
# Step 0: RFC 5280 chain validation
|
|
107
|
+
check_chain=True,
|
|
108
|
+
trust_store=ca_certs,
|
|
109
|
+
|
|
110
|
+
# Step 0b: SP 800-78 algorithm enforcement
|
|
111
|
+
algorithm_policy=SP800_78_ALGORITHM_POLICY,
|
|
112
|
+
|
|
113
|
+
# Step 1: Identity extraction with Federal PKI providers (CAC + PIV)
|
|
114
|
+
registry=default_registry(),
|
|
115
|
+
|
|
116
|
+
# Step 2: Validity period (enabled by default)
|
|
117
|
+
check_validity_period=True,
|
|
118
|
+
|
|
119
|
+
# Step 3: Revocation — try CRL first, fall back to OCSP
|
|
120
|
+
revocation=RevocationPolicy(
|
|
121
|
+
checks=(CRL, OCSP),
|
|
122
|
+
issuer_certs=ca_certs,
|
|
123
|
+
crl_config=CRLConfig(cache_dir="/var/cache/pki/crls", cache_ttl=3600),
|
|
124
|
+
strict=True,
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
result = validate_certificate(cert, policy)
|
|
129
|
+
|
|
130
|
+
match result.status:
|
|
131
|
+
case ValidationStatus.VALID:
|
|
132
|
+
print(f"Identity: {result.identity.primary_id}")
|
|
133
|
+
print(f"Credential: {result.identity.credential_type}")
|
|
134
|
+
print(f"Name: {result.identity.firstname} {result.identity.lastname}")
|
|
135
|
+
case ValidationStatus.CHAIN_UNTRUSTED:
|
|
136
|
+
print(f"Untrusted chain: {result.error}")
|
|
137
|
+
case ValidationStatus.ALGORITHM_NONCOMPLIANT:
|
|
138
|
+
print(f"Algorithm rejected: {result.error}")
|
|
139
|
+
case ValidationStatus.EXPIRED:
|
|
140
|
+
print(f"Expired: {result.error}")
|
|
141
|
+
case ValidationStatus.REVOKED:
|
|
142
|
+
print(f"Revoked: {result.error}")
|
|
143
|
+
case _:
|
|
144
|
+
print(f"Error: {result.error}")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Custom revocation strategy
|
|
148
|
+
|
|
149
|
+
Implement `RevocationCheck` to add your own revocation source:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from pki.core.revocation import CRL, OCSP, RevocationCheck, RevocationPolicy, RevocationResult
|
|
153
|
+
|
|
154
|
+
class InternalCRLDatabase(RevocationCheck):
|
|
155
|
+
"""Check revocation against an internal database."""
|
|
156
|
+
|
|
157
|
+
def check(self, cert, policy):
|
|
158
|
+
serial = format(cert.serial_number, "x")
|
|
159
|
+
if self._is_revoked(serial):
|
|
160
|
+
return RevocationResult.REVOKED, f"Revoked in internal DB (serial {serial})"
|
|
161
|
+
return RevocationResult.GOOD, "Not revoked in internal DB"
|
|
162
|
+
|
|
163
|
+
def _is_revoked(self, serial):
|
|
164
|
+
... # query your database
|
|
165
|
+
|
|
166
|
+
# Internal DB first, then CRL, then OCSP
|
|
167
|
+
policy = CertificatePolicy(
|
|
168
|
+
revocation=RevocationPolicy(checks=(InternalCRLDatabase(), CRL, OCSP)),
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Configuration architecture
|
|
173
|
+
|
|
174
|
+
The validation pipeline is configured through three nested dataclasses,
|
|
175
|
+
each owning a distinct concern:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
CertificatePolicy ← top-level pipeline config
|
|
179
|
+
├── check_chain: bool ← chain validation on/off
|
|
180
|
+
├── trust_store: list[Certificate] ← root CAs for chain validation
|
|
181
|
+
├── algorithm_policy: AlgorithmPolicy ← key/hash requirements (opt-in)
|
|
182
|
+
├── registry: ProviderRegistry ← identity extraction providers
|
|
183
|
+
│
|
|
184
|
+
└── revocation: RevocationPolicy ← revocation checking (or None to skip)
|
|
185
|
+
├── checks: Sequence[RevocationCheck] ← ordered strategies (CRL, OCSP, custom)
|
|
186
|
+
├── issuer_certs: list[Certificate] ← CAs for CRL sig verification + OCSP requests
|
|
187
|
+
├── strict: bool ← fail-closed when all checks unavailable
|
|
188
|
+
│
|
|
189
|
+
└── crl_config: CRLConfig ← cache mechanics
|
|
190
|
+
├── cache_dir: str ← where to store cached CRLs
|
|
191
|
+
├── cache_ttl: int ← seconds before stale (triggers background refresh)
|
|
192
|
+
├── max_crl_bytes: int ← reject CRLs larger than this
|
|
193
|
+
├── max_acceptable_age: int ← force-refresh if older than this
|
|
194
|
+
└── fetch_timeout: int ← HTTP timeout
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**`CertificatePolicy`** owns the pipeline — which steps to run and in what order.
|
|
198
|
+
|
|
199
|
+
**`RevocationPolicy`** owns the security decisions — which strategies to try, what CA certs to use, and whether to fail open or closed. It is separate from `CertificatePolicy` so it can be shared across multiple validation calls or constructed independently.
|
|
200
|
+
|
|
201
|
+
**`CRLConfig`** owns the cache mechanics — where files are stored, how long they're fresh, size limits. Provider packs like `pki-federal` subclass it to set deployment-specific defaults (e.g., 20 MB max for DoD CRLs, 18-hour max age per FIPS 201-3).
|
|
202
|
+
|
|
203
|
+
## Validation pipeline
|
|
204
|
+
|
|
205
|
+
`validate_certificate()` runs these steps in order, short-circuiting on first failure:
|
|
206
|
+
|
|
207
|
+
| Step | Check | Controlled by | Status on failure |
|
|
208
|
+
|------|-------|---------------|-------------------|
|
|
209
|
+
| 0 | Chain validation | `check_chain`, `trust_store` | `CHAIN_UNTRUSTED` |
|
|
210
|
+
| 0b | Algorithm compliance | `algorithm_policy` | `ALGORITHM_NONCOMPLIANT` |
|
|
211
|
+
| 1 | Identity extraction | `registry` | `ERROR` |
|
|
212
|
+
| 2 | Validity period | `check_validity_period` | `NOT_YET_VALID` / `EXPIRED` |
|
|
213
|
+
| 3 | Revocation | `revocation` | `REVOKED` / `ERROR` |
|
|
214
|
+
|
|
215
|
+
Identity is populated even on failure (when possible) so callers can log who the failed certificate belonged to.
|
|
216
|
+
|
|
217
|
+
### Validation boundary
|
|
218
|
+
|
|
219
|
+
pki-core handles chain validation, identity extraction, algorithm enforcement, and revocation checking. The following must be handled by the TLS terminator (nginx, AWS ALB) or the application:
|
|
220
|
+
|
|
221
|
+
- **TLS challenge-response** — proof of private key possession (FIPS 201-3 §6.2.3.1 steps 3-6)
|
|
222
|
+
- **Certificate policy OID constraints in path building** — `cryptography`'s verifier does not support RFC 5280 §6.1.1 `initial-policy-set`. Use scoped trust stores and `get_policy_oids()` for post-validation policy checking.
|
|
223
|
+
- **Certificate Transparency** — SCT verification is not performed.
|
|
224
|
+
|
|
225
|
+
## Security
|
|
226
|
+
|
|
227
|
+
### FIPS 140 cryptographic module status
|
|
228
|
+
|
|
229
|
+
pki-core does not implement cryptographic primitives directly. All
|
|
230
|
+
cryptographic operations (signature verification, hash computation, CRL/OCSP
|
|
231
|
+
validation) are performed by the
|
|
232
|
+
[cryptography](https://cryptography.io/) library, which uses OpenSSL as its
|
|
233
|
+
backend.
|
|
234
|
+
|
|
235
|
+
**pki-core itself is not FIPS 140 validated.** FIPS 140 validation applies
|
|
236
|
+
to the underlying cryptographic module (OpenSSL), not to libraries that
|
|
237
|
+
call it. To deploy pki-core in a FIPS 140 compliant environment:
|
|
238
|
+
|
|
239
|
+
1. Use an OpenSSL build that has a FIPS 140 validation certificate
|
|
240
|
+
(e.g., the OpenSSL FIPS Object Module or a vendor-validated build).
|
|
241
|
+
2. Ensure the FIPS provider is active in the OpenSSL configuration
|
|
242
|
+
(`openssl list -providers` should show `fips`).
|
|
243
|
+
3. The `cryptography` library will automatically use the FIPS provider
|
|
244
|
+
when OpenSSL is configured for FIPS mode.
|
|
245
|
+
|
|
246
|
+
The `AlgorithmPolicy` class (and `SP800_78_ALGORITHM_POLICY` in pki-federal)
|
|
247
|
+
enforces that certificates use approved algorithms (RSA 2048+, P-256/P-384,
|
|
248
|
+
SHA-256+), but this is an application-level check — it does not replace
|
|
249
|
+
FIPS 140 validation of the cryptographic module itself.
|
|
250
|
+
|
|
251
|
+
### SBOM
|
|
252
|
+
|
|
253
|
+
CycloneDX Software Bills of Materials are generated in CI on every pipeline
|
|
254
|
+
run and published as artifacts. SBOMs list all direct and transitive Python
|
|
255
|
+
dependencies with version numbers, enabling vulnerability tracking and
|
|
256
|
+
supply chain risk management per OMB M-22-18.
|
|
257
|
+
|
|
258
|
+
### NIST SP 800-53 controls
|
|
259
|
+
|
|
260
|
+
See [SP800-53-CONTROLS.md](SP800-53-CONTROLS.md) for a full mapping of
|
|
261
|
+
26 security controls to implementation evidence across the pki ecosystem.
|
|
262
|
+
|
|
263
|
+
pki-core directly implements:
|
|
264
|
+
|
|
265
|
+
- **IA-2(12)** — chain validation, FASC-N/UUID extraction, algorithm enforcement
|
|
266
|
+
- **IA-5 / IA-5(2)** — certificate lifecycle (expiration, revocation, path validation)
|
|
267
|
+
- **SC-12** — trust store management, secure CRL caching
|
|
268
|
+
- **SC-13** — cryptographic operations via FIPS-capable OpenSSL backend
|
|
269
|
+
- **SC-17** — certificate validation pipeline
|
|
270
|
+
- **SI-10** — input validation (certificate parsing, CRL size limits, algorithm checks)
|
|
271
|
+
- **SA-11 / SA-11(1) / SA-11(8)** — fuzz testing, static analysis, dependency scanning
|
|
272
|
+
- **SR-4 / CM-8** — CycloneDX SBOMs
|
|
273
|
+
|
|
274
|
+
### Security testing and static analysis
|
|
275
|
+
|
|
276
|
+
See [SECURITY.md](SECURITY.md) for vulnerability reporting, fuzz testing
|
|
277
|
+
coverage, and a full list of static analysis suppressions with
|
|
278
|
+
justifications.
|
|
279
|
+
|
|
280
|
+
## Provider packs
|
|
281
|
+
|
|
282
|
+
A provider pack defines `AuthProvider` instances for a specific PKI ecosystem. Each provider specifies:
|
|
283
|
+
|
|
284
|
+
- **auth_oids** — certificate policy OIDs that identify this credential type
|
|
285
|
+
- **cn_parser** — callable that extracts name fields from the certificate CN
|
|
286
|
+
- **primary_id_selector** — callable that picks the primary identifier (EDIPI, UUID, email, etc.)
|
|
287
|
+
- **heuristics** — fallback rules for matching certificates without recognized OIDs
|
|
288
|
+
- **trust_store_sources** — URLs and formats for downloading CA bundles
|
|
289
|
+
|
|
290
|
+
### Defining a custom provider
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from pki.core.providers import AuthProvider, ProviderRegistry, HeuristicRule
|
|
294
|
+
from pki.core.selectors import select_email_first
|
|
295
|
+
|
|
296
|
+
def parse_company_cn(identity):
|
|
297
|
+
parts = identity.cn.split(" ")
|
|
298
|
+
identity.firstname = parts[0]
|
|
299
|
+
identity.lastname = parts[-1]
|
|
300
|
+
|
|
301
|
+
provider = AuthProvider(
|
|
302
|
+
name="ACME",
|
|
303
|
+
display_name="ACME Corp",
|
|
304
|
+
auth_oids=frozenset({"1.2.3.4.5.6"}),
|
|
305
|
+
cn_parser=parse_company_cn,
|
|
306
|
+
primary_id_selector=select_email_first,
|
|
307
|
+
heuristics=(HeuristicRule(field="org", pattern="acme"),),
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
registry = ProviderRegistry()
|
|
311
|
+
registry.register(provider)
|
|
312
|
+
|
|
313
|
+
policy = CertificatePolicy(registry=registry)
|
|
314
|
+
result = validate_certificate(cert, policy)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Combining provider packs
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from pki.core.providers import ProviderRegistry
|
|
321
|
+
from pki.core.validation import CertificatePolicy, validate_certificate
|
|
322
|
+
from pki.federal import CAC_PROVIDER, PIV_PROVIDER
|
|
323
|
+
from pki.mycorp import MYCORP_PROVIDER
|
|
324
|
+
|
|
325
|
+
registry = ProviderRegistry()
|
|
326
|
+
registry.register(CAC_PROVIDER)
|
|
327
|
+
registry.register(PIV_PROVIDER)
|
|
328
|
+
registry.register(MYCORP_PROVIDER)
|
|
329
|
+
|
|
330
|
+
policy = CertificatePolicy(registry=registry)
|
|
331
|
+
result = validate_certificate(cert, policy)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Creating a reusable provider pack
|
|
335
|
+
|
|
336
|
+
To distribute providers as a package, follow the same layout as [pki-federal](https://github.com/mevtc/pki-federal):
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
pki-mycorp/
|
|
340
|
+
├── src/pki/mycorp/
|
|
341
|
+
│ ├── __init__.py # export provider instances
|
|
342
|
+
│ ├── oids.py # policy OID constants
|
|
343
|
+
│ ├── cn_parsers.py # CN parsing functions
|
|
344
|
+
│ ├── providers.py # AuthProvider instances and registry factories
|
|
345
|
+
│ └── trust_store.py # CA bundle fetchers (if applicable)
|
|
346
|
+
├── pyproject.toml # depends on pki-core
|
|
347
|
+
└── tests/
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
No `pki/__init__.py` — use Python's implicit namespace packages so `pki.core`, `pki.federal`, and `pki.mycorp` coexist.
|
|
351
|
+
|
|
352
|
+
## Available provider packs
|
|
353
|
+
|
|
354
|
+
- [pki-federal](https://github.com/mevtc/pki-federal) — DoD CAC, Federal PIV, and ECA
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
BSD-3-Clause — see [LICENSE](LICENSE).
|