Sample external signer executable (Rust) – CSC API C2PA integration
The below sample shows how to implement an external signer in Rust that:
Reads claim bytes from stdin (or from a source buffer).
Computes a SHA-256 hash of the data.
Fetches a Signature Activation Data (SAD) token from the CSC API.
Requests a digital signature for the hash from the CSC API.
Decodes the Base64 signature and outputs the raw signed data.
Embeds the signature into a C2PA manifest using the Rust C2PA library.
Compute SHA-256 hash
use sha2::{Digest, Sha256}; use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine}; use std::io::{self, Read}; /// Reads claim bytes from stdin and returns the Base64-encoded SHA-256 hash fn calculate_hash() -> io::Result<String> { let mut input_data = Vec::new(); io::stdin().read_to_end(&mut input_data)?; let mut hasher = Sha256::new(); hasher.update(&input_data); let hash_bytes = hasher.finalize(); let base64_hash = BASE64_STANDARD.encode(hash_bytes); Ok(base64_hash) }Fetch SAD
use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize}; use std::error::Error; const SAD_API_URL: &str = "https://clientauth.demo.one.digicert.com/documentmanager/csc/v1/credentials/authorize"; const CLIENT_CERT_PATH: &str = "/path/to/Certificate_pkcs12.p12"; const CLIENT_CERT_PASS: &str = "your_cert_password"; #[derive(Serialize)] struct SADRequest { credentialID: String, numSignatures: u8, hash: Vec<String>, PIN: String, } #[derive(Deserialize)] struct SADResponse { pub expiresIn: u64, SAD: String, } /// Builds a request client using PKCS#12 certificate fn build_client_with_pkcs12() -> Result<Client, Box<dyn Error>> { use reqwest::tls::Identity; use std::fs::File; use std::io::Read; let mut buf = Vec::new(); File::open(CLIENT_CERT_PATH)?.read_to_end(&mut buf)?; let identity = Identity::from_pkcs12_der(&buf, CLIENT_CERT_PASS)?; let client = Client::builder() .identity(identity) .danger_accept_invalid_certs(true) .build()?; Ok(client) } /// Fetch SAD for a given Base64 hash pub fn fetch_sad(hash: &str) -> Result<String, Box<dyn Error>> { let client = build_client_with_pkcs12()?; let mut headers = HeaderMap::new(); headers.insert("Content-Type", HeaderValue::from_static("application/json")); let request_body = SADRequest { credentialID: "your_credential_id".to_string(), numSignatures: 1, hash: vec![hash.to_string()], PIN: "your_pin".to_string(), }; let response: SADResponse = client .post(SAD_API_URL) .headers(headers) .json(&request_body) .send()? .error_for_status()? .json()?; Ok(response.SAD) }Request signature from CSC API
const SIGNING_API_URL: &str = "https://clientauth.demo.one.digicert.com/documentmanager/csc/v1/signatures/signHash"; #[derive(Serialize)] struct SignRequest { credentialID: String, SAD: String, hash: Vec<String>, signAlgo: String, signAlgoParams: String, } #[derive(Deserialize)] struct SignResponse { signatures: Vec<String>, } pub fn sign_via_api(hash: &str, sad: &str) -> Result<String, Box<dyn Error>> { let client = build_client_with_pkcs12()?; let mut headers = HeaderMap::new(); headers.insert("Content-Type", HeaderValue::from_static("application/json")); let request_body = SignRequest { credentialID: "your_credential_id".to_string(), SAD: sad.to_string(), hash: vec![hash.to_string()], signAlgo: "1.2.840.113549.1.1.10".to_string(), // RSASSA-PSS signAlgoParams: "base64_encoded_params".to_string(), }; let response: SignResponse = client .post(SIGNING_API_URL) .headers(headers) .json(&request_body) .send()? .error_for_status()? .json()?; Ok(response.signatures.first().cloned().ok_or_else(|| { Box::new(std::io::Error::new( std::io::ErrorKind::Other, "Empty signature response", )) })?) }Decode signature and write to stdout
use std::io::{self, Write}; pub fn write_signed_data(base64_signature: &str) -> io::Result<()> { let signature_bytes = BASE64_STANDARD.decode(base64_signature) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; io::stdout().write_all(&signature_bytes)?; Ok(()) }Example integration in main.rs
fn main() { let base64_hash = calculate_hash().expect("Failed to calculate hash"); let sad = lib::fetch_sad(&base64_hash).expect("Failed to fetch SAD"); let signed_signature = lib::sign_via_api(&base64_hash, &sad).expect("Failed to sign hash"); write_signed_data(&signed_signature).expect("Failed to write signature"); }