CMS signer stub registered on the Swarmauri SigningBase registry
Project description
Swarmauri Signing CMS
CMSSigner delivers production-ready
Cryptographic Message Syntax (CMS)
and S/MIME signing utilities for the
Swarmauri platform. The signer integrates with the common
SigningBase registry and works seamlessly with Swarmauri certificate
providers so that applications can create, distribute, and verify PKCS#7
signatures end-to-end.
Features
- Detached and attached PKCS#7 signatures with DER or PEM serialization
- S/MIME friendly defaults for email payloads
- Streaming, digest, and structured-envelope signing helpers
- Signature verification with certificate chain enforcement
- First-class interoperability with Swarmauri key providers and certificate authorities
Installation
pip
pip install swarmauri_signing_cms
uv
Add it to your managed project:
uv add swarmauri_signing_cms
Or install directly into the active environment:
uv pip install swarmauri_signing_cms
Usage
The snippets below illustrate how to bootstrap certificates, create CMS and S/MIME signatures, and verify results with Swarmauri components.
Bootstrapping CMS credentials
Use swarmauri_certs_x509 together with a key provider to issue a
CMS-capable signing certificate with the correct email protection
Extended Key Usage.
import asyncio
from swarmauri_core.crypto.types import KeyUse, ExportPolicy
from swarmauri_core.key_providers.types import KeyAlg, KeyClass, KeySpec
from swarmauri_certs_x509 import X509CertService
from swarmauri_keyprovider_local import LocalKeyProvider
async def build_smime_identity():
key_provider = LocalKeyProvider()
certs = X509CertService()
ca_spec = KeySpec(
klass=KeyClass.asymmetric,
alg=KeyAlg.ECDSA_P256_SHA256,
uses=(KeyUse.SIGN,),
export_policy=ExportPolicy.SECRET_WHEN_ALLOWED,
label="Example CMS CA",
)
ca_key = await key_provider.create_key(ca_spec)
ca_cert = await certs.create_self_signed(
ca_key,
{"CN": "Example CMS Root", "O": "Example Org"},
extensions={
"basic_constraints": {"ca": True, "path_len": 0},
"key_usage": {
"digital_signature": True,
"content_commitment": True,
"key_cert_sign": True,
"crl_sign": True,
},
},
)
leaf_spec = KeySpec(
klass=KeyClass.asymmetric,
alg=KeyAlg.ECDSA_P256_SHA256,
uses=(KeyUse.SIGN,),
export_policy=ExportPolicy.SECRET_WHEN_ALLOWED,
label="CMS Signer",
)
leaf_key = await key_provider.create_key(leaf_spec)
csr = await certs.create_csr(
leaf_key,
{"CN": "cms-signer.example", "emailAddress": "signer@example.org"},
san={"email": ["signer@example.org"]},
)
leaf_cert = await certs.sign_cert(
csr,
ca_key,
ca_cert=ca_cert,
extensions={
"basic_constraints": {"ca": False},
"key_usage": {
"digital_signature": True,
"content_commitment": True,
},
"extended_key_usage": {"oids": ["emailProtection"]},
},
)
cms_key_ref = {
"kind": "pem",
"private_key": leaf_key.material,
"certificate": leaf_cert,
"extra_certificates": [ca_cert],
}
trust_chain = [ca_cert]
return cms_key_ref, trust_chain
cms_key_ref, trust_chain = asyncio.run(build_smime_identity())
cms_key_ref can now be supplied to the CMSSigner for both CMS and S/MIME
operations. The returned trust_chain is useful when verifying signatures.
Detached CMS signing & verification
import asyncio
from swarmauri_signing_cms import CMSSigner
async def sign_and_verify(message: bytes):
signer = CMSSigner()
signatures = await signer.sign_bytes(
cms_key_ref,
message,
alg="SHA256",
opts={"encoding": "der"},
)
ok = await signer.verify_bytes(
message,
signatures,
opts={"trusted_certs": trust_chain},
)
return signatures[0].artifact, ok
artifact, verified = asyncio.run(sign_and_verify(b"important document"))
print(f"Signature length: {len(artifact)} bytes, verified={verified}")
sign_bytes produces a detached PKCS#7 signature suitable for archival or
transport alongside the original payload. Verification succeeds when at least
one signature chains back to a trusted certificate.
S/MIME attached signing
Attached S/MIME signatures encapsulate the message body and are typically
distributed using the application/pkcs7-mime content type.
import asyncio
from email.message import EmailMessage
from email import policy
from swarmauri_signing_cms import CMSSigner
async def create_smime(message: EmailMessage):
payload = message.as_bytes(policy=policy.SMTP)
signer = CMSSigner()
signature = await signer.sign_bytes(
cms_key_ref,
payload,
opts={"attached": True, "encoding": "pem"},
)
verified = await signer.verify_bytes(
payload,
signature,
opts={"trusted_certs": trust_chain},
)
return signature[0].artifact, verified
msg = EmailMessage()
msg["From"] = "signer@example.org"
msg["To"] = "recipient@example.net"
msg["Subject"] = "Quarterly Statement"
msg.set_content("Please review the attached statement.")
smime_bytes, ok = asyncio.run(create_smime(msg))
if ok:
with open("statement.p7m", "wb") as fp:
fp.write(smime_bytes)
The resulting statement.p7m file contains a fully formed S/MIME envelope that
can be delivered via email or downloaded from an API.
Example workflows
The package ships ready-to-run demonstrations inside
swarmauri_signing_cms/examples/cms_and_smime_examples.py. They generate
ephemeral key material using the cryptography library so you can explore CMS
and S/MIME flows without external tooling.
Prepare signing material
from swarmauri_signing_cms.examples.cms_and_smime_examples import build_ephemeral_identity
key_ref, trust_anchor = build_ephemeral_identity("demo.swarmauri")
key_ref is the structure expected by CMSSigner. It contains the PEM encoded
private key, end-entity certificate, and (optionally) a chain. The helper also
returns a PEM trust anchor that can be supplied during verification.
Create a detached CMS signature
import asyncio
from swarmauri_signing_cms import CMSSigner
from swarmauri_signing_cms.examples.cms_and_smime_examples import (
build_ephemeral_identity,
cms_detached_signature,
)
async def main() -> None:
key_ref, trust_anchor = build_ephemeral_identity("demo.swarmauri")
signer = CMSSigner()
signature = await cms_detached_signature(signer, key_ref, [trust_anchor])
print("Signature mode:", signature.mode)
print("Hash algorithm:", signature.alg)
if __name__ == "__main__":
asyncio.run(main())
The helper signs raw bytes, verifies the result using the provided trust store,
and returns the resulting Signature instance. Detached artifacts are encoded
as DER by default, but you can pass opts={"attached": False, "encoding": "pem"}
when you need PEM output for downstream systems.
Assemble an S/MIME message
import asyncio
from email import policy
from email.generator import BytesGenerator
from io import BytesIO
from swarmauri_signing_cms import CMSSigner
from swarmauri_signing_cms.examples.cms_and_smime_examples import (
build_ephemeral_identity,
smime_attached_message,
)
async def main() -> None:
key_ref, trust_anchor = build_ephemeral_identity("demo.swarmauri")
envelope = await smime_attached_message(CMSSigner(), key_ref, [trust_anchor])
buffer = BytesIO()
BytesGenerator(buffer, policy=policy.SMTP).flatten(envelope)
print(buffer.getvalue().decode("utf-8"))
if __name__ == "__main__":
asyncio.run(main())
smime_attached_message requests an attached CMS signature in PEM form
(smime.p7s) and wraps the payload inside a multipart/signed envelope. The
helper verifies the artifact against the provided trust anchor before returning
the message container.
Inspect the CMS certificate chain
import asyncio
from swarmauri_signing_cms import CMSSigner
from swarmauri_signing_cms.examples.cms_and_smime_examples import (
build_ephemeral_identity,
cms_detached_signature,
describe_certificate_chain,
)
async def describe() -> None:
key_ref, trust_anchor = build_ephemeral_identity("demo.swarmauri")
signature = await cms_detached_signature(CMSSigner(), key_ref, [trust_anchor])
for subject in describe_certificate_chain(signature):
print(subject)
asyncio.run(describe())
Signature.cert_chain_der contains DER-encoded certificates returned by the
signing routine. describe_certificate_chain converts those blobs back into
cryptography.x509.Certificate instances so you can display or persist the
chain.
Contributing
Pull requests and issue reports are welcome! Please review the contribution guide before submitting changes.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swarmauri_signing_cms-0.1.0.dev37.tar.gz.
File metadata
- Download URL: swarmauri_signing_cms-0.1.0.dev37.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9767882a06a4eaa6b825936f4625f47bd50dc16119019702138e62610f041f54
|
|
| MD5 |
e14b843d7c10d1d979a70f6547248dc7
|
|
| BLAKE2b-256 |
6fe02bacfef4a4a4a73c3820978fff97d2816f30de3c89960c20c5dc542a4a3e
|
File details
Details for the file swarmauri_signing_cms-0.1.0.dev37-py3-none-any.whl.
File metadata
- Download URL: swarmauri_signing_cms-0.1.0.dev37-py3-none-any.whl
- Upload date:
- Size: 15.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
953cc9c91eefd0c3c852f832a46a995ed621c24773780237362e9b070b4aa90d
|
|
| MD5 |
f9e922a6f33c04614ea084dc2460eb8f
|
|
| BLAKE2b-256 |
714a0bc851a41f768d95c0e5c076e95a2b6902f1a9c4c6b846a9769e2dcda9b8
|