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.dev33.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.dev33-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: swarmauri_signing_cms-0.1.0.dev33.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

Hashes for swarmauri_signing_cms-0.1.0.dev33.tar.gz
Algorithm Hash digest
SHA256 4ec8a8ab06f246c628f8648e92717df4718d0d9ceca40b4af8b1a13ead6ee37a
MD5 17ed33afec840a00871230ab2a566db6
BLAKE2b-256 5ea8343613cbc2062297a219391930406ee2a456a5404a9e915c499790ded683

See more details on using hashes here.

File details

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

File metadata

  • Download URL: swarmauri_signing_cms-0.1.0.dev33-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

Hashes for swarmauri_signing_cms-0.1.0.dev33-py3-none-any.whl
Algorithm Hash digest
SHA256 6d552fa94a50d4eaac956d770f585f78f653be742b918ff730b1f548d1e87088
MD5 aa21def541fe0ce4e2bbdb1af22045f6
BLAKE2b-256 8613b311bc87abaea596a3079bc9c6fe55eeec0db8c8ea2f3b33c24d1b2defe8

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