Embed signed manifest into image
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()