|
11 | 11 | # each license. |
12 | 12 |
|
13 | 13 | # This example shows how to sign an image with a C2PA manifest |
14 | | -# and read the metadata added to the image. |
| 14 | +# using a callback signer and read the metadata added to the image. |
15 | 15 |
|
16 | 16 | import os |
17 | 17 | import c2pa |
| 18 | +from cryptography.hazmat.primitives import hashes, serialization |
| 19 | +from cryptography.hazmat.primitives.asymmetric import ec |
| 20 | +from cryptography.hazmat.backends import default_backend |
18 | 21 |
|
19 | 22 | fixtures_dir = os.path.join(os.path.dirname(__file__), "../tests/fixtures/") |
20 | 23 | output_dir = os.path.join(os.path.dirname(__file__), "../output/") |
21 | 24 |
|
22 | | -# ensure the output directory exists |
| 25 | +# Ensure the output directory exists |
23 | 26 | if not os.path.exists(output_dir): |
24 | 27 | os.makedirs(output_dir) |
25 | 28 |
|
|
33 | 36 | reader = c2pa.Reader("image/jpeg", file) |
34 | 37 | print(reader.json()) |
35 | 38 |
|
36 | | -# Create a signer from certificate and key files |
| 39 | +# Load certificates and private key (here from the test fixtures) |
| 40 | +# This is OK for development, but in production you should use a |
| 41 | +# secure way to load the certificates and private key. |
37 | 42 | certs = open(fixtures_dir + "es256_certs.pem", "rb").read() |
38 | 43 | key = open(fixtures_dir + "es256_private.key", "rb").read() |
39 | 44 |
|
40 | | -signer_info = c2pa.C2paSignerInfo( |
41 | | - alg=b"es256", # Use bytes instead of encoded string |
42 | | - sign_cert=certs, |
43 | | - private_key=key, |
44 | | - ta_url=b"http://timestamp.digicert.com" # Use bytes and add timestamp URL |
45 | | -) |
| 45 | +# Define a callback signer function |
| 46 | +def callback_signer_es256(data: bytes) -> bytes: |
| 47 | + """Callback function that signs data using ES256 algorithm.""" |
| 48 | + private_key = serialization.load_pem_private_key( |
| 49 | + key, |
| 50 | + password=None, |
| 51 | + backend=default_backend() |
| 52 | + ) |
| 53 | + signature = private_key.sign( |
| 54 | + data, |
| 55 | + ec.ECDSA(hashes.SHA256()) |
| 56 | + ) |
| 57 | + return signature |
46 | 58 |
|
47 | | -signer = c2pa.Signer.from_info(signer_info) |
| 59 | +# Create a signer using the callback function we defined |
| 60 | +signer = c2pa.Signer.from_callback( |
| 61 | + callback=callback_signer_es256, |
| 62 | + alg=c2pa.C2paSigningAlg.ES256, |
| 63 | + certs=certs.decode('utf-8'), |
| 64 | + tsa_url="http://timestamp.digicert.com" |
| 65 | +) |
48 | 66 |
|
49 | 67 | # Create a manifest definition as a dictionary |
| 68 | +# This manifest follows the V2 manifest format |
50 | 69 | manifest_definition = { |
51 | 70 | "claim_generator": "python_example", |
52 | 71 | "claim_generator_info": [{ |
53 | 72 | "name": "python_example", |
54 | 73 | "version": "0.0.1", |
55 | 74 | }], |
| 75 | + "claim_version": 2, |
56 | 76 | "format": "image/jpeg", |
57 | 77 | "title": "Python Example Image", |
58 | 78 | "ingredients": [], |
|
73 | 93 | ] |
74 | 94 | } |
75 | 95 |
|
| 96 | +# Create the builder with the manifest definition |
76 | 97 | builder = c2pa.Builder(manifest_definition) |
77 | 98 |
|
78 | | -# Sign the image |
79 | | -print("\nSigning the image...") |
80 | | -with open(fixtures_dir + "C.jpg", "rb") as source: |
81 | | - with open(output_dir + "C_signed.jpg", "wb") as dest: |
82 | | - result = builder.sign(signer, "image/jpeg", source, dest) |
| 99 | +# Sign the image with the signer created above, |
| 100 | +# which will use the callback signer |
| 101 | +print("\nSigning the image file...") |
| 102 | +builder.sign_file( |
| 103 | + source_path=fixtures_dir + "C.jpg", |
| 104 | + dest_path=output_dir + "C_signed.jpg", |
| 105 | + signer=signer |
| 106 | +) |
| 107 | + |
| 108 | +# Clean up the signer |
| 109 | +signer.close() |
83 | 110 |
|
84 | 111 | # Read the signed image to verify |
85 | 112 | print("\nReading signed image metadata:") |
|
0 commit comments