Skip to main content

CMS signer stub registered on the Swarmauri SigningBase registry

Project description

Swarmauri Signing CMS

PyPI Version Python Versions License Repo views


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.1.0.dev27.tar.gz (16.4 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.1.0.dev27-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file swarmauri_signing_cms-0.1.0.dev27.tar.gz.

File metadata

  • Download URL: swarmauri_signing_cms-0.1.0.dev27.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","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.1.0.dev27.tar.gz
Algorithm Hash digest
SHA256 643a69ddae501da86f4ca08fd54ecfed53f3661a9ab4d8336e38f900eb70e76d
MD5 b58e3e0bcdc31a6911d165c3bcefadba
BLAKE2b-256 86adf1cbb7e490670bffd036a770e7dbe721fe1c10e36b77f0017a6a4c43cbc3

See more details on using hashes here.

File details

Details for the file swarmauri_signing_cms-0.1.0.dev27-py3-none-any.whl.

File metadata

  • Download URL: swarmauri_signing_cms-0.1.0.dev27-py3-none-any.whl
  • Upload date:
  • Size: 15.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","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.1.0.dev27-py3-none-any.whl
Algorithm Hash digest
SHA256 1bd9e4653fa39490fe84c686dbf37af3070e9d19c1f06efcf1064ac7e0fce08c
MD5 1863212ed6c36227124036faaa745aff
BLAKE2b-256 71bae4947d64c80bae4693cc79d8a41df55e6da4f6173ebbd2602a163a854a21

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