Skip to main content

Sample external signer executable (Rust) – CSC API C2PA integration

The below sample shows how to implement an external signer in Rust that:

  1. Reads claim bytes from stdin (or from a source buffer).

  2. Computes a SHA-256 hash of the data.

  3. Fetches a Signature Activation Data (SAD) token from the CSC API.

  4. Requests a digital signature for the hash from the CSC API.

  5. Decodes the Base64 signature and outputs the raw signed data.

  6. Embeds the signature into a C2PA manifest using the Rust C2PA library.

  1. 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) 
    
    } 
    
     
  2. 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) 
    
    } 
  3. 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", 
    
            )) 
    
        })?) 
    
    } 
  4. 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"); 
      
      }