swarmauri_certs_azure 0.3.0.dev4__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.
- swarmauri_certs_azure-0.3.0.dev4/PKG-INFO +39 -0
- swarmauri_certs_azure-0.3.0.dev4/README.md +13 -0
- swarmauri_certs_azure-0.3.0.dev4/pyproject.toml +68 -0
- swarmauri_certs_azure-0.3.0.dev4/swarmauri_certs_azure/__init__.py +0 -0
- swarmauri_certs_azure-0.3.0.dev4/swarmauri_certs_azure/certs/AzureKeyVaultCertService.py +101 -0
- swarmauri_certs_azure-0.3.0.dev4/swarmauri_certs_azure/certs/__init__.py +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: swarmauri_certs_azure
|
|
3
|
+
Version: 0.3.0.dev4
|
|
4
|
+
Summary: Azure Key Vault certificate utilities for the Swarmauri ecosystem
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Author: Community
|
|
7
|
+
Requires-Python: >=3.10,<3.13
|
|
8
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Provides-Extra: azure
|
|
13
|
+
Provides-Extra: crypto
|
|
14
|
+
Requires-Dist: asn1crypto
|
|
15
|
+
Requires-Dist: asn1crypto ; extra == "crypto"
|
|
16
|
+
Requires-Dist: azure-identity
|
|
17
|
+
Requires-Dist: azure-identity ; extra == "azure"
|
|
18
|
+
Requires-Dist: azure-keyvault-keys
|
|
19
|
+
Requires-Dist: azure-keyvault-keys ; extra == "azure"
|
|
20
|
+
Requires-Dist: cryptography
|
|
21
|
+
Requires-Dist: cryptography ; extra == "crypto"
|
|
22
|
+
Requires-Dist: swarmauri_base
|
|
23
|
+
Requires-Dist: swarmauri_core
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# swarmauri_certs_azure
|
|
27
|
+
|
|
28
|
+
Community-maintained utilities for working with X.509 certificates via Azure Key Vault.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
- AzureKeyVaultCertService for issuing and managing certificates.
|
|
32
|
+
- Helper utilities aligned with RFC 5280 and RFC 7468.
|
|
33
|
+
|
|
34
|
+
## Testing
|
|
35
|
+
Run tests with:
|
|
36
|
+
```bash
|
|
37
|
+
uv run --package swarmauri_certs_azure --directory community pytest
|
|
38
|
+
```
|
|
39
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# swarmauri_certs_azure
|
|
2
|
+
|
|
3
|
+
Community-maintained utilities for working with X.509 certificates via Azure Key Vault.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- AzureKeyVaultCertService for issuing and managing certificates.
|
|
7
|
+
- Helper utilities aligned with RFC 5280 and RFC 7468.
|
|
8
|
+
|
|
9
|
+
## Testing
|
|
10
|
+
Run tests with:
|
|
11
|
+
```bash
|
|
12
|
+
uv run --package swarmauri_certs_azure --directory community pytest
|
|
13
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "swarmauri_certs_azure"
|
|
3
|
+
version = "0.3.0.dev4"
|
|
4
|
+
description = "Azure Key Vault certificate utilities for the Swarmauri ecosystem"
|
|
5
|
+
license = "Apache-2.0"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
repository = "http://github.com/swarmauri/swarmauri-sdk"
|
|
8
|
+
requires-python = ">=3.10,<3.13"
|
|
9
|
+
classifiers = [
|
|
10
|
+
"License :: OSI Approved :: Apache Software License",
|
|
11
|
+
"Programming Language :: Python :: 3.10",
|
|
12
|
+
"Programming Language :: Python :: 3.11",
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
]
|
|
15
|
+
authors = [{name = "Community"}]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"swarmauri_core",
|
|
18
|
+
"swarmauri_base",
|
|
19
|
+
"azure-identity",
|
|
20
|
+
"azure-keyvault-keys",
|
|
21
|
+
"cryptography",
|
|
22
|
+
"asn1crypto",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
azure = ["azure-identity", "azure-keyvault-keys"]
|
|
27
|
+
crypto = ["cryptography", "asn1crypto"]
|
|
28
|
+
|
|
29
|
+
[tool.uv.sources]
|
|
30
|
+
swarmauri_core = { workspace = true }
|
|
31
|
+
swarmauri_base = { workspace = true }
|
|
32
|
+
|
|
33
|
+
[tool.pytest.ini_options]
|
|
34
|
+
markers = [
|
|
35
|
+
"test: standard test",
|
|
36
|
+
"unit: Unit tests",
|
|
37
|
+
"i9n: Integration tests",
|
|
38
|
+
"r8n: Regression tests",
|
|
39
|
+
"timeout: mark test to timeout after X seconds",
|
|
40
|
+
"xpass: Expected passes",
|
|
41
|
+
"xfail: Expected failures",
|
|
42
|
+
"acceptance: Acceptance tests",
|
|
43
|
+
"perf: Performance tests that measure execution time and resource usage",
|
|
44
|
+
]
|
|
45
|
+
timeout = 300
|
|
46
|
+
log_cli = true
|
|
47
|
+
log_cli_level = "INFO"
|
|
48
|
+
log_cli_format = "%(asctime)s [%(levelname)s] %(message)s"
|
|
49
|
+
log_cli_date_format = "%Y-%m-%d %H:%M:%S"
|
|
50
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
51
|
+
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"pytest>=8.0",
|
|
55
|
+
"pytest-asyncio>=0.24.0",
|
|
56
|
+
"pytest-xdist>=3.6.1",
|
|
57
|
+
"pytest-json-report>=1.5.0",
|
|
58
|
+
"python-dotenv",
|
|
59
|
+
"requests>=2.32.3",
|
|
60
|
+
"flake8>=7.0",
|
|
61
|
+
"pytest-timeout>=2.3.1",
|
|
62
|
+
"ruff>=0.9.9",
|
|
63
|
+
"pytest-benchmark>=4.0.0",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[build-system]
|
|
67
|
+
requires = ["poetry-core>=1.0.0"]
|
|
68
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import datetime as dt
|
|
5
|
+
import random
|
|
6
|
+
from typing import Any, Dict, Iterable, Literal, Mapping, Optional
|
|
7
|
+
|
|
8
|
+
from azure.identity import DefaultAzureCredential
|
|
9
|
+
from azure.keyvault.keys import KeyClient
|
|
10
|
+
from cryptography import x509
|
|
11
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
12
|
+
from cryptography.x509.oid import NameOID
|
|
13
|
+
|
|
14
|
+
from swarmauri_base.certs.CertServiceBase import CertServiceBase
|
|
15
|
+
from swarmauri_core.certs.ICertService import AltNameSpec, SubjectSpec
|
|
16
|
+
from swarmauri_core.crypto.types import KeyRef
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# Helpers
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _now_utc() -> dt.datetime:
|
|
25
|
+
"""Return current UTC time."""
|
|
26
|
+
return dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _serial_or_random(serial: Optional[int]) -> int:
|
|
30
|
+
"""Return provided serial number or a random 128-bit value per RFC 5280."""
|
|
31
|
+
if serial is not None:
|
|
32
|
+
return int(serial)
|
|
33
|
+
return random.SystemRandom().getrandbits(128)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _pem_cert(der_bytes: bytes) -> bytes:
|
|
37
|
+
"""Encode DER certificate bytes into PEM as described in RFC 7468."""
|
|
38
|
+
b64 = base64.encodebytes(der_bytes).decode("ascii").replace("\n", "")
|
|
39
|
+
lines = [b64[i : i + 64] for i in range(0, len(b64), 64)]
|
|
40
|
+
return (
|
|
41
|
+
"-----BEGIN CERTIFICATE-----\n"
|
|
42
|
+
+ "\n".join(lines)
|
|
43
|
+
+ "\n-----END CERTIFICATE-----\n"
|
|
44
|
+
).encode("ascii")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _to_x509_name(spec: SubjectSpec) -> x509.Name:
|
|
48
|
+
rdns = []
|
|
49
|
+
if cn := spec.get("CN"):
|
|
50
|
+
rdns.append(x509.NameAttribute(NameOID.COMMON_NAME, cn))
|
|
51
|
+
return x509.Name(rdns)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Service
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AzureKeyVaultCertService(CertServiceBase):
|
|
60
|
+
"""Minimal Azure Key Vault backed certificate service."""
|
|
61
|
+
|
|
62
|
+
type: Literal["AzureKeyVaultCertService"] = "AzureKeyVaultCertService"
|
|
63
|
+
|
|
64
|
+
def __init__(self, vault_url: str, *, credential: Optional[Any] = None) -> None:
|
|
65
|
+
super().__init__()
|
|
66
|
+
self._cred = credential or DefaultAzureCredential()
|
|
67
|
+
self._keys = KeyClient(vault_url=vault_url, credential=self._cred)
|
|
68
|
+
|
|
69
|
+
def supports(self) -> Mapping[str, Iterable[str]]:
|
|
70
|
+
return {
|
|
71
|
+
"key_algs": ("RSA-2048",),
|
|
72
|
+
"sig_algs": ("RSA-SHA256",),
|
|
73
|
+
"features": ("csr",),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async def create_csr(
|
|
77
|
+
self,
|
|
78
|
+
key: KeyRef,
|
|
79
|
+
subject: SubjectSpec,
|
|
80
|
+
*,
|
|
81
|
+
san: Optional[AltNameSpec] = None,
|
|
82
|
+
extensions: Optional[Dict[str, Any]] = None,
|
|
83
|
+
sig_alg: Optional[str] = None,
|
|
84
|
+
challenge_password: Optional[str] = None,
|
|
85
|
+
output_der: bool = False,
|
|
86
|
+
opts: Optional[Dict[str, Any]] = None,
|
|
87
|
+
) -> bytes:
|
|
88
|
+
"""Create a PKCS#10 CSR using the supplied key material."""
|
|
89
|
+
pem_priv = key.material
|
|
90
|
+
if not pem_priv:
|
|
91
|
+
raise NotImplementedError(
|
|
92
|
+
"Non-exportable keys are not supported in this simplified service."
|
|
93
|
+
)
|
|
94
|
+
subject_name = _to_x509_name(subject)
|
|
95
|
+
builder = x509.CertificateSigningRequestBuilder().subject_name(subject_name)
|
|
96
|
+
priv = serialization.load_pem_private_key(pem_priv, password=None)
|
|
97
|
+
csr = builder.sign(priv, hashes.SHA256())
|
|
98
|
+
encoding = (
|
|
99
|
+
serialization.Encoding.DER if output_der else serialization.Encoding.PEM
|
|
100
|
+
)
|
|
101
|
+
return csr.public_bytes(encoding)
|
|
File without changes
|