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()