Embed signed manifest into image
3 minute read
Generate a minimal C2PA manifest, sign it (using a demo CSC signer) and embed the signed manifest into a JPEG image. The result is a signed image with embedded provenance information.
Inputs
- Path to the JPEG image file.
- Path to save the signed image.
- Author name – Enter manually.
- Author email – Enter manually (optional).
- A certificate chain file (chain.pem) available locally required by the signer initialization.
Process
- Collect input values from the user (image path, output path, author details).
Note
Author details are fetched from your User certificate using the DigiCert credentials info API. - Construct a minimal manifest as a JSON object containing the image metadata and author information.
- Load the image bytes from the input file.
- Load the certificate chain from chain.pem.
- Initialize a Builder with the manifest JSON and set up a signer (demo CSC signer in this case, which base64-encodes the hash).
- Use the builder to sign the manifest and embed it into the JPEG image.
- Save the signed image to the specified output path.
Output
- A signed JPEG image file that contains the embedded manifest.
- The manifest includes author information and provenance data, making the image verifiable by C2PA-compliant tools.
Python example
# This program takes a JPEG image, builds a C2PA manifest with author information, #...simulates signing the manifest using a dummy CSC signer (which just base64-...encodes #...the manifest hash instead of performing a real cryptographic signature), embeds the #...“signed” manifest into the image, and writes out the signed image to disk.
#Dummy parts: #demo_csc_signer is a placeholder and does not perform actual CSC signing.
#In the production environment, this function calls the CSC API with your client certificate, private #...key, and PIN to obtain a legitimate signed hash (SAD) for the manifest.
#While the program demonstrates the full flow of building a manifest, loading an #...image, and embedding a signature, the cryptographic signing step is mocked and #...must be replaced with actual credentials and CSC API calls in production.
#For a working example to see how you can sign an image with a c2pa #...complaint manifest using DigiCert DTM CSC API, see next section.
import io
import json
import base64
import logging
import getpass
from c2pa import Builder, create_signer, SigningAlg
# --- Configuration ---
CERT_CHAIN_PATH = "chain.pem"
SIGNING_ALG = SigningAlg.PS256
TIMESTAMP_URL = "http://timestamp.digicert.com"
# Load certificate chain
with open(CERT_CHAIN_PATH, "rb") as f:
CERT_CHAIN = f.read()
def log_step(msg: str):
print("\n" + "="*60 + "\n")
logging.info(msg)
# --- Dummy CSC signer for demonstration ---
def demo_csc_signer(data: bytes):
"""Simulate signing: return a base64-encoded hash as a 'signed manifest'"""
hash_b64 = base64.b64encode(data).decode("utf-8")
print(f"Hash of manifest to be signed (base64): {hash_b64}")
# In production, call CSC APIs here
return hash_b64.encode("utf-8")
def main():
logging.basicConfig(level=logging.INFO, format="%(message)s")
# --- Step 1: Inputs ---
log_step("Step 1: Enter required inputs")
image_path = input("Enter path to JPEG image: ").strip()
output_path = input("Enter output path for signed image: ").strip()
author_name = input("Enter author name: ").strip()
author_email = input("Enter author email (optional): ").strip()
# --- Step 2: Build manifest ---
log_step("Step 2: Build manifest")
manifest_dict = {
"title": image_path,
"format": "image/jpeg",
"claim_generator_info": [{"name": "Demo C2PA Builder", "version": "1.0.0"}],
"assertions": [
{
"label": "stds.schema-org.CreativeWork",
"data": {
"@context": "https://schema.org",
"@type": "CreativeWork",
"author": [{"@type": "Person", "name": author_name, "email": author_email}],
},
"kind": "Json",
}
],
}
manifest_json = json.dumps(manifest_dict)
# --- Step 3: Load image ---
log_step("Step 3: Load image")
with open(image_path, "rb") as f:
img_bytes = f.read()
# --- Step 4: Create builder and signer ---
log_step("Step 4: Prepare builder and signer")
builder = Builder(manifest_json)
signer = create_signer(demo_csc_signer, SIGNING_ALG, CERT_CHAIN, TIMESTAMP_URL)
# --- Step 5: Embed signed manifest into image ---
log_step("Step 5: Embed signed manifest")
result = io.BytesIO()
builder.sign(signer, "image/jpeg", io.BytesIO(img_bytes), result)
# --- Step 6: Save signed image ---
log_step("Step 6: Save signed image")
with open(output_path, "wb") as f:
f.write(result.getvalue())
logging.info(f"✅ Signed image with embedded manifest saved to {output_path}")
if __name__ == "__main__":
main()
Was this page helpful?
Provide feedback