Signing Assets
Overview
Signing is the process of cryptographically attaching a C2PA manifest to a digital asset. This action embeds a secure, tamper-evident "birth certificate" and a detailed history directly into the file. It is the fundamental step in creating authentic, verifiable content. This guide is for developers, platforms, and creators who need to prove the authenticity of their digital media.
What, Why, and How
-
What is Signing? Signing uses the Que service to embed a C2PA manifest—a data structure containing provenance information like author, creation tools, and actions—into an asset. The entire package is then cryptographically signed to prevent undetected modifications.
-
Why Sign Assets? In an environment filled with synthetic and modified media, signing provides a proactive solution. It allows you to protect your brand, establish trust with your audience, and give consumers a reliable way to verify the origin and history of your content.
-
How to Get Started The process involves preparing your asset, defining a manifest, and making a single call to the Que SDK. The quickstart example below shows the simplest way to sign an asset.
The Signing Workflow
The typical workflow for signing an asset with Que is straightforward and designed for security and efficiency, especially with large files.
- Define a Manifest: Construct a JSON object that describes your asset and the actions performed on it. This is the core provenance data you will embed. For a detailed guide, see Manifests.
- Upload Your Asset: For security and performance, upload your asset to a location Que can access. The recommended method is using Que's Presigned Uploads for direct, secure S3 uploads. You can also use publicly accessible URL Inputs, although additional costs are incurred.
- Call the
sign
Function: Use the Que SDK to call thesign
function, providing the asset's location and your JSON manifest. - Retrieve the Signed Asset: Que returns the location of the newly signed asset, which is stored securely and ready for distribution.
Quickstart
This example demonstrates the minimum code required to sign an asset. First, ensure your asset is uploaded and you have its S3 key.
import { Que } from "que-sdk";
const que = new Que({
apiKeyAuth: process.env["QUE_API_KEY_AUTH"] ?? "",
});
const asset = {
bucket: 'your-asset-bucket',
key: 'uploads/photo.jpg',
};
const manifest = JSON.stringify({
title: 'Original Photograph',
assertions: [
{
label: 'stds.schema-org.CreativeWork',
data: {
'@context': 'https://schema.org',
'@type': 'CreativeWork',
author: [{ '@type': 'Person', name: 'Jane Photographer' }],
},
},
],
});
async function signAsset() {
try {
const result = await que.signAsset({
asset,
manifestJson: manifest,
});
console.log('Signing successful:', result);
} catch (error) {
console.error('Signing failed:', error);
}
}
signAsset();
import os
from que_sdk import QueClient, AssetReference
que = QueClient(api_key=os.getenv("QUE_API_KEY"))
asset = AssetReference(
bucket="your-asset-bucket",
key="uploads/photo.jpg"
)
manifest = """
{
"title": "Original Photograph",
"assertions": [
{
"label": "stds.schema-org.CreativeWork",
"data": {
"@context": "https://schema.org",
"@type": "CreativeWork",
"author": [{ "@type": "Person", "name": "Jane Photographer" }]
}
}
]
}
"""
try:
result = que.sign(
asset=asset,
manifest_json=manifest
)
print(f"Signing successful: {result}")
except Exception as e:
print(f"Signing failed: {e}")
package main
import (
"context"
"fmt"
"os"
"github.com/que-platform/que-sdk-go"
)
func main() {
client := quesdk.NewClient(os.Getenv("QUE_API_KEY"))
asset := quesdk.AssetReference{
Bucket: "your-asset-bucket",
Key: "uploads/photo.jpg",
}
manifest := `{
"title": "Original Photograph",
"assertions": [{
"label": "stds.schema-org.CreativeWork",
"data": {
"@context": "https://schema.org",
"@type": "CreativeWork",
"author": [{"@type": "Person", "name": "Jane Photographer"}]
}
}]
}`
result, err := client.Sign(context.Background(), &quesdk.SignRequest{
Asset: asset,
ManifestJSON: manifest,
})
if err != nil {
fmt.Printf("Signing failed: %v\n", err)
return
}
fmt.Printf("Signing successful: %+v\n", result)
}
// The Que SDK for Rust is not yet available.
// The following is a conceptual example.
use que_sdk::{QueClient, types::{AssetReference, SignRequest}};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = QueClient::new(std::env::var("QUE_API_KEY")?);
let asset = AssetReference::S3 {
bucket: "your-asset-bucket".to_string(),
key: "uploads/photo.jpg".to_string(),
};
let manifest = r#"{
"title": "Original Photograph",
"assertions": [{
"label": "stds.schema-org.CreativeWork",
"data": {
"@context": "https://schema.org",
"@type": "CreativeWork",
"author": [{"@type": "Person", "name": "Jane Photographer"}]
}
}]
}"#.to_string();
let request = SignRequest { asset, manifest_json: manifest, ..Default::default() };
let result = client.sign(request).await?;
println!("Signing successful: {:?}", result);
Ok(())
}
package hello.world;
import java.lang.Exception;
import org.openapis.openapi.Que;
import org.openapis.openapi.models.components.*;
import org.openapis.openapi.models.errors.ProblemResponseException;
import org.openapis.openapi.models.operations.SignAssetResponse;
public class SignAssetExample {
public static void main(String[] args) throws ProblemResponseException, ProblemResponseException, ProblemResponseException, Exception {
Que sdk = Que.builder()
.apiKeyAuth(System.getenv().getOrDefault("API_KEY_AUTH", ""))
.build();
SignRequest req = SignRequest.builder()
.asset(AssetRefDto.of(S3.builder()
.bucket("your-asset-bucket")
.key("uploads/photo.jpg")
.build()))
.manifestJson("{\"title\":\"Original Photograph\",\"assertions\":[{\"label\":\"stds.schema-org.CreativeWork\",\"data\":{\"@context\":\"https://schema.org\",\"@type\":\"CreativeWork\",\"author\":[{\"@type\":\"Person\",\"name\":\"Jane Photographer\"}]}}]}")
.build();
SignAssetResponse res = sdk.signAsset()
.request(req)
.call();
if (res.signResponse().isPresent()) {
System.out.println("Signing successful: " + res.signResponse().get().asset().s3Uri());
}
}
}
// The Que SDK for PHP is not yet available.
// The following is a conceptual example.
<?php
require_once 'vendor/autoload.php';
use Que\Sdk\QueClient;
$que = new QueClient(['api_key' => getenv('QUE_API_KEY')]);
$asset = [
'bucket' => 'your-asset-bucket',
'key' => 'uploads/photo.jpg'
];
$manifest = json_encode([
'title' => 'Original Photograph',
'assertions' => [[
'label' => 'stds.schema-org.CreativeWork',
'data' => [
'@context' => 'https://schema.org',
'@type' => 'CreativeWork',
'author' => [['@type' => 'Person', 'name' => 'Jane Photographer']]
]
]]
]);
try {
$result = $que->sign([
'asset' => $asset,
'manifest_json' => $manifest
]);
print_r($result);
} catch (Exception $e) {
echo 'Signing failed: ' . $e->getMessage();
}
Expected Response
A successful signing operation returns a JSON object with the location of the signed asset and cryptographic evidence of the signature.
{
"assurance": "server_measured",
"evidence": {
"signer": "env_dev",
"alg": "ES256"
},
"asset_s3_uri": "s3://que-signed-assets/a1b2c3d4/signed-photo.jpg"
}
Configuring the Sign Request
Beyond the basics, you can configure the signing process to include enhanced identity claims or set resource limits.
Creator Identity (CAWG)
The Content Authenticity Working Group (CAWG) defines a standard for adding a verifiable creator identity assertion to your manifest. This provides a stronger link between the content and its creator. You can enable it by providing a cawg
configuration object.
For a complete guide, see Creator Identity (CAWG).
Resource Limits
To manage costs and prevent abuse, you can specify processing limits for any signing operation. These control factors like maximum file size and processing timeouts.
Que applies sensible default limits to all operations. You only need to override them for special use cases, such as processing exceptionally large files.
For more information, see Limits and Timeouts.
Understanding the Response
The response object from a successful sign
call contains three key fields:
asset_s3_uri
: The location of your signed asset. This is an S3 URI pointing to the new file with the C2PA manifest embedded.assurance
: The signing method used.server_measured
indicates that Que's servers streamed and hashed the asset, providing the highest level of assurance.evidence
: An object containing cryptographic details about the signature, including the signer ID (signer
) and the algorithm used (alg
).
Best Practices and Error Handling
Warning: Validate your Manifest JSON
The manifest_json
parameter must be a valid, stringified JSON object. A common source of errors is passing a malformed string or a native object instead of a string. Always validate your JSON structure before sending the request.
If the signing process fails, the SDK will raise an error containing a code and a descriptive message. Common failures include an invalid asset reference, a malformed manifest, or exceeding resource limits. For a full list of possible errors, see Errors and Troubleshooting.