Skip to main content

CMS signer stub registered on the Swarmauri SigningBase registry

Project description

Swarmauri Logo

PyPI - Downloads Hits PyPI - Python Version PyPI - License PyPI - swarmauri_signing_cms Discord

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

swarmauri_signing_cms-0.11.0.dev1.tar.gz (16.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

swarmauri_signing_cms-0.11.0.dev1-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file swarmauri_signing_cms-0.11.0.dev1.tar.gz.

File metadata

  • Download URL: swarmauri_signing_cms-0.11.0.dev1.tar.gz
  • Upload date:
  • Size: 16.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","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

Hashes for swarmauri_signing_cms-0.11.0.dev1.tar.gz
Algorithm Hash digest
SHA256 bcd4c63511be9a2209acc7369c5b303fb1ec45cc1a28c0884f76d32ca6ce5d84
MD5 ca18e91460407f951025fd0f25fdac3f
BLAKE2b-256 caaa6bf8a79cdf8b76d0fa4a8046a5a389b98ee31dfe74b71fc524a2bbb61143

See more details on using hashes here.

File details

Details for the file swarmauri_signing_cms-0.11.0.dev1-py3-none-any.whl.

File metadata

  • Download URL: swarmauri_signing_cms-0.11.0.dev1-py3-none-any.whl
  • Upload date:
  • Size: 16.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","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

Hashes for swarmauri_signing_cms-0.11.0.dev1-py3-none-any.whl
Algorithm Hash digest
SHA256 4a51c8c7b28d8636ef1f75ce2541edf9575d2cb6ade04d59a2a513e0c090f650
MD5 63227175724f558faa22ceb5ef318f8b
BLAKE2b-256 5b3cb13152d15030d972501584d254da30b8fafbc1f056a24cf178e046c36501

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page