Build the manifest
Before signing an image, you are required to generate a minimal C2PA manifest and compute its hash.
Required inputs
Image file path - Used as the title and for hash computation.
Author name - Entered manually.
Author email - Entered manually.
Both author name and email are typically fetched from your user certificate using the DigiCert credentials info API.
Process
Compute the SHA256 hash of the input image (base64-encoded).
Build a minimal manifest that includes:
A unique manifest UUID and instance ID.
Basic metadata such as title, format, and author details.
Assertions describing the image hash and creative work metadata.
Save the manifest to a local file (manifest.json).
Pretty-print the manifest JSON to the console.
Compute and display the SHA256 hash of the manifest (base64-encoded).
Output
A manifest.json file containing the to-be-signed manifest.
The SHA256 hash of the manifest string to be used for signing.
Python example
# This Python script generates a minimal C2PA manifest for a given image, saves it as #...manifest.json, prints the manifest then computes and displays its SHA256 hash in base64. import json import hashlib import base64 from datetime import datetime import uuid def compute_sha256(file_path): """Compute SHA256 hash of a file and return base64-encoded string""" sha256 = hashlib.sha256() with open(file_path, "rb") as f: while chunk := f.read(8192): sha256.update(chunk) return base64.b64encode(sha256.digest()).decode('utf-8') def compute_sha256_of_string(input_str): """Compute SHA256 hash of a string and return base64-encoded string""" sha256 = hashlib.sha256() sha256.update(input_str.encode('utf-8')) return base64.b64encode(sha256.digest()).decode('utf-8') def main(): print("=== Minimal C2PA Manifest Generator ===\n") # Ask for required inputs image_path = input("Enter path to the image file: ").strip() author_name = input("Enter author name: ").strip() author_email = input("Enter author email: ").strip() # Generate UUIDs for manifest and instance manifest_uuid = str(uuid.uuid4()) instance_id = f"xmp:iid:{uuid.uuid4()}" # Compute SHA256 hash of the image image_hash = compute_sha256(image_path) # Build minimal assertions assertions = [ { "label": "c2pa.hash.data", "kind": "Json", "data": { "alg": "sha256", "hash": image_hash, "name": "jumbf manifest", "pad": "" } }, { "label": "stds.schema-org.CreativeWork", "kind": "Json", "data": { "@context": "https://schema.org", "@type": "CreativeWork", "author": [ { "@type": "Person", "name": author_name, "email": author_email } ], "dateCreated": datetime.utcnow().isoformat() + "Z", "name": image_path } } ] # Build manifest manifest = { "active_manifest": f"urn:uuid:{manifest_uuid}", "manifests": { f"urn:uuid:{manifest_uuid}": { "claim_generator": "Custom C2PA Manifest Generator", "title": image_path, "format": "image/jpeg", "instance_id": instance_id, "ingredients": [], "assertions": assertions, "label": f"urn:uuid:{manifest_uuid}" } }, "validation_status": [] } # Convert manifest to pretty JSON string manifest_json_str = json.dumps(manifest, indent=2) manifest_file_path = "manifest.json" with open(manifest_file_path, "w") as f: f.write(manifest_json_str) # Print manifest print("\n=== Generated To-Be-Signed Manifest ===\n") print(manifest_json_str) # Compute SHA256 hash of the manifest itself manifest_hash = compute_sha256_of_string(manifest_json_str) print("\n=== SHA256 Hash of the Manifest (base64) ===\n") print(manifest_hash) if __name__ == "__main__": main()