This release is a pre-release and may not be stable for production use.
c2pie is an open‑source Python library for constructing C2PA Content Credentials manifests that validate with c2patool and other common C2PA consumers.
As far as we know, c2pie is the world's first Python package to implement signing PDF files according to the C2PA Standard.
The package supports building claims, assertions, and COSE signatures and embedding the manifest store into JPG/JPEG and PDF files.
🔸 Supported file extensions: JPG, JPEG, PDF
🔸 Supported Python versions: 3.10.0 - 3.14.5
🔸 Supported C2PA Spec Versions: 2.4.
Support for C2PA 2.2 is planned for future releases.
For more detailed feature specification, please look at the Features section.
Table of Contents
🥧 Quick start
Running signing from a Docker container
- Run a Docker container from a Python image:
docker run --rm -it --entrypoint bash --name c2pie-test python:3.14
- Inside the container execute the following bash commands:
# Generate private key and certificate chain:
openssl genpkey \
-algorithm RSA-PSS \
-pkeyopt rsa_keygen_bits:2048 \
-pkeyopt rsa_pss_keygen_md:sha256 \
-pkeyopt rsa_pss_keygen_mgf1_md:sha256 \
-pkeyopt rsa_pss_keygen_saltlen:32 \
-out private_key.key
openssl req -new -x509 \
-key private_key.key \
-sha256 -days 365 \
-subj "/C=US/ST=CA/L=Somewhere/O=C2PA Test Signing Cert/OU=FOR TESTING_ONLY/CN=C2PA PSS Signer/emailAddress=pie@example.com" \
-addext "basicConstraints=critical,CA:false" \
-addext "keyUsage=critical,digitalSignature,nonRepudiation" \
-addext "extendedKeyUsage=critical,emailProtection" \
-out certificate_chain.pem
# Export created private key and certificate chain files into env variables:
export C2PIE_PRIVATE_KEY_FILE=./private_key.key
export C2PIE_CERTIFICATE_CHAIN_FILE=./certificate_chain.pem
# Install package
pip install c2pie
# Download test image from this repo
wget https://raw.githubusercontent.com/TourmalineCore/c2pie/refs/heads/master/example_app/test_files/test_image.jpg
# Sign downloaded image
c2pie sign --input_file ./test_image.jpg
- In a separate terminal, execute the following commands to copy the file from the container to the folder you're currently in:
docker cp c2pie-test:signed_test_image.jpg .
[!NOTE] You can use the
c2pie-testcontainer to experiment with other JPG/JPEG or PDF files.Once you exit the container, it will be deleted automatically.
After being copied to host machine, signed files can then be validated using either of the methods from Validation section: C2PA Verify Tool or c2patool.
Running from your local environment using globally installed Python
Prerequisites
-
Python environment. Currently supported Python versions: 3.10.0 - 3.14.5. Make sure to create and activate virtual environment to avoid installing packages globally and any errors caused by that.
-
Private key and certificate chain pair. The repo contains pre-generated mock credentials in
tests/credentials. You can either download and use them for a quick start or go to Certificates for instructions on how to generate a similar key-certificate pair. -
Key and certificate chain filepaths exported into the current environment with (pay attention to filenames):
export C2PIE_PRIVATE_KEY_FILE=private_key.key export C2PIE_CERTIFICATE_CHAIN_FILE=certificate_chain.pem
-
c2pie package installed in your current environment:
pip install c2pie
Usage
Command Line Interface
You can run the following command to sign an input JPG or PDF file:
c2pie sign --input_file path/to/input_file
By default, signed file will be saved to the same directory as the input file with the signed_ prefix. To explicitly set output path, use:
c2pie sign --input_file path/to/input_file --output_file path/to/output_file
If the file has been successfully signed, you'll see a message like this:
Successfully signed the file tests/test_files/test_doc.pdf!
The result was saved to tests/test_files/signed_test_doc.pdf.
Code
To sign a file and save the output to the same directory:
from c2pie.signing import sign_file
input_file_path = "path/to/file"
sign_file(input_path=input_file_path)
To set a custom output path:
from c2pie.signing import sign_file
input_file_path = "path/to/file"
output_file_path = "path/to/another/file/"
sign_file(input_path=input_file_path, output_path=output_file_path)
If the file has been successfully signed, you'll see a message like this:
Successfully signed the file tests/test_files/test_doc.pdf!
The result was saved to tests/test_files/signed_test_doc.pdf.
Running example apps with Docker Compose
For a quick test of c2pie's functionality with pre-prepared environment, test files and credentials, you can run our example apps.
[!IMPORTANT] Docker is essential for running example apps.
Follow the steps:
-
Clone the c2pie repository.
-
Go to
example_appdirectory:
cd example_app
[!NOTE] By default, example apps use the latest available stable c2pie version. If you'd like to test some particular version, you can change the value of
C2PIE_PACKAGE_VERSIONinexample_app/.example-app-env.
-
To test signing a JPG file, run:
docker compose up c2pie-test-signing-jpg
To test signing a PDF file, run:
docker compose up c2pie-test-signing-pdf
After running either of these commands, you'll see a resulting signed file appear in
example_app/test_filesdirectory with asigned-prefix and a corresponding message with c2patool validation results in your terminal like this:Successfully signed the file test_files/test_image.jpg! The result was saved to test_files/signed_test_image.jpg. c2patool_validation_results: { "active_manifest": "urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9", "manifests": { "urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9": { "claim_generator_info": { "name": "c2pie", ... }, ... } }, "validation_results": { "activeManifest": { "success": [ { "code": "claimSignature.insideValidity", "url": "self#jumbf=/c2pa/urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9/c2pa.signature", "explanation": "claim signature valid" }, ................ }, "validation_state": "Valid" }
You can also set up a Jupyter Lab environment and test c2pie there by running:
docker compose up c2pie-notebooks
After running this command you should be able to access Jupyter Lab at localhost:8888 from your browser.
The existing notebooks directory already contains an example notebook with commands to test signing functionality.
Validation
C2PA Verify Tool
You can verify signed files using Verify tool.
Simply upload the file you'd like to verify.
[!IMPORTANT] Files embedded with self-signed certificates (like the ones this repository contains) won't be verified.
You'll get the following message:
The Content Credential issuer couldn’t be recognized. This file may not come from where it claims to.Please proceed to production credentials section to find out about generating verifiable credentials.
c2patool
c2patool is a command line tool for working with C2PA manifests and media assets (audio, image or video files) provided by the C2PA Rust Library.
If you already have Rust, install c2patool with:
cargo install c2patool
To validate files with c2patool, run:
c2patool path/to/your_output.jpg
c2patool path/to/your_output.pdf
If the file has been correctly signed and validation is successful, the results you'll see in the terminal will look similar to this:
{
"active_manifest": "urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9",
"manifests": {
"urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9": {
"claim_generator_info": {
"name": "c2pie",
...
},
...
}
},
"validation_results": {
"activeManifest": {
"success": [
{
"code": "claimSignature.insideValidity",
"url": "self#jumbf=/c2pa/urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9/c2pa.signature",
"explanation": "claim signature valid"
},
................
},
"validation_state": "Valid"
}
Validating test image with a Docker container
- Run a container with Rust:
docker run --rm -it --entrypoint bash --name c2pie-validate rust:1.90.0-bullseye
- Install c2patool in the container:
cargo install c2patool
-
To validate the image previously signed using a Docker container and copied to your working directory:
In a separate terminal, copy the signed image into the Rust container.
Make sure the signed file is in the same directory that you're running this
docker cpfrom!docker cp ./signed_test_image.jpg c2pie-validate:signed_test_image.jpg
Then go back to the container's terminal and validate the copied image with:
c2patool signed_test_image.jpgIf the validation was successful, you'll get an output similar to this:
c2patool_validation_results: { "active_manifest": "urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9", "manifests": { "urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9": { "claim_generator_info": { "name": "c2pie", ... }, ... } }, "validation_results": { "activeManifest": { "success": [ { "code": "claimSignature.insideValidity", "url": "self#jumbf=/c2pa/urn:c2pa:f0ce8560b76342d1bb3085cfbe6cc5e9/c2pa.signature", "explanation": "claim signature valid" }, ................ }, "validation_state": "Valid" }
[!NOTE] You can validate other files in the same
c2pie-validatecontainer.Once you exit, the container will be deleted automatically.
🥧 Certificates
Example certificate chain and key file are located in tests/credentials.
[!WARNING] This repository's credentials are suitable for development only!
Generating test credentials
You can generate your own private key and certificate chain pair for testing the package by following these steps:
-
Generate a private key:
openssl genpkey \ -algorithm RSA-PSS \ -pkeyopt rsa_keygen_bits:2048 \ -pkeyopt rsa_pss_keygen_md:sha256 \ -pkeyopt rsa_pss_keygen_mgf1_md:sha256 \ -pkeyopt rsa_pss_keygen_saltlen:32 \ -out private_key.key
-
Generate a Self-Signed Certificate:
openssl req -new -x509 \ -key private_key.key \ -sha256 -days 365 \ -subj "/C=US/ST=CA/L=Somewhere/O=C2PA Test Signing Cert/OU=FOR TESTING_ONLY/CN=C2PA PSS Signer/emailAddress=pie@example.com" \ -addext "basicConstraints=critical,CA:false" \ -addext "keyUsage=critical,digitalSignature,nonRepudiation" \ -addext "extendedKeyUsage=critical,emailProtection" \ -out certificate_chain.pem
[!IMPORTANT] Remember to update environment variables
C2PIE_PRIVATE_KEY_FILEandC2PIE_CERTIFICATE_CHAIN_FILEto use your newly generated key (private_key.key) and certificate chain (certificate_chain.pem) files.
[!NOTE] You can change certificate's validity period with
-daysoption at the last step.
-subjoption allows to set signature info used to sign the certificate. You can change the values to fit your info. Here's what each field letter code stands for:/C - Counry Code, /ST - State or Province name, /L - Locality Name (e.g. city), /O - Organization Name, /OU - Organization Unit Name, /CN - Common Name (e.g. your name), /emailAdress - email adress
Getting credentials for production
🔸 Use a real document‑signing certificate (RSA‑PSS or ECDSA per C2PA);
🔸 Provide a leaf + intermediates bundle (no root);
🔸 Configure trust anchors/allow‑lists in your validator environment.
For detailed information on signing and certificates please explore the corresponding section in the Content Authenticity Initiative (CAI) documentation.
🥧 Features
🔸 C2PA Claim (c2pa.claim) with canonical CBOR, dc:format, alg, and hashed‑URIs for assertions.
🔸 C2PA Signature (c2pa.signature) using COSE_Sign1 (PS256) with detached payload and x5chain in protected header.
🔸 Assertion Store with common assertions (e.g., c2pa.hash.data hard‑binding, schema.org CreativeWork, etc.).
🔸 Embedding
- JPG via APP11 segments (size‑driven iterative layout).
- PDF via incremental update at EOF (xref/trailer preserved;
/AF+/Names/EmbeddedFiles).
🔸 Validation with c2patool (structure + signatures).
Workflow of test applications
-
Load a sample asset (
tests/test_files/..); -
Build a manifest with
c2pie_GenerateAssertion,c2pie_GenerateHashDataAssertion,c2pie_GenerateManifestStore; -
Embed the manifest (
c2pie_EmplaceManifest); -
Write a new asset with C2PA.
Notes for PDF vs JPG/JPEG
🔸 PDF: we append an incremental update. The c2pa.hash.data exclusion starts at len(original_pdf) and its length equals the final tail size (computed iteratively).
🔸 JPG/JPEG: we insert APP11 segments. The exclusion start is the APP11 insertion offset; the length is the final APP11 payload length (also computed iteratively).
The library takes care of iterative sizing, so the c2pa.hash.data matches exactly, otherwise validators return assertion.dataHash.mismatch.
🥧 Relevant links
🥧 License
Apache License. See c2pie repository's license for more information.
Metadata
Release files for c2pie 1.0.0a3.dev877697923
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| c2pie-1.0.0a3.dev877697923.tar.gz | 38.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| c2pie-1.0.0a3.dev877697923-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 84.7 kB
Release files / c2pie-1.0.0a3.dev877697923.tar.gz
| Download URL | c2pie-1.0.0a3.dev877697923.tar.gz |
|---|---|
| Size | 38.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
77f7860312f7c9bbf7bee36e7803adee340919ae99873492e0ea793f2fe3dfd0
|
|
BLAKE2b-256 checksum How to use checksums |
f05dd064aa34ed0cd6cc2a55073455892eedbf8784969bab73c3fbefbcc82e02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / c2pie-1.0.0a3.dev877697923-py3-none-any.whl
| Download URL | c2pie-1.0.0a3.dev877697923-py3-none-any.whl |
|---|---|
| Size | 46.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
afbe552bff66d773bf44081a4b51697433026d0befe750dd9905f4b8cbd1d3ab
|
|
BLAKE2b-256 checksum How to use checksums |
598cbc44cc563f72c8ae3e848e37956c13386d08a5bd0c60f1d7eab8d5b10cb3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logSecurity for c2pie 1.0.0a3.dev877697923
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for c2pie-1.0.0a3.dev877697923.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.