TerraformPilot

DevOps

Terraform for Digital Provenance: C2PA Content Signing on AWS

Provision digital provenance and C2PA content signing infrastructure with Terraform: certificate authorities, signing services, ledgers, and verification APIs.

LLuca Berton1 min read

Digital provenance — proving where a piece of media came from — moved from research to mainstream in 2026 thanks to deepfakes and the C2PA standard adopted by major camera, browser, and AI vendors. Building a provenance pipeline means running a private CA, a signing service, a tamper-evident ledger, and a verification API. Terraform stitches all of those into a single deployable stack.

This guide shows how to provision a C2PA-aligned content provenance backend on AWS.

Components

#
ComponentAWS service
Private CAAWS Private CA
Signing keysKMS asymmetric keys
Signing serviceLambda or Fargate
Tamper-evident logQLDB or DynamoDB + KMS-signed digests
Verification APIAPI Gateway + Lambda
Asset storageS3 with object lock

Private Certificate Authority

#
resource "aws_acmpca_certificate_authority" "provenance" {
  type = "ROOT"
 
  certificate_authority_configuration {
    key_algorithm     = "EC_secp384r1"
    signing_algorithm = "SHA384WITHECDSA"
 
    subject {
      common_name  = "Acme Provenance Root CA"
      organization = "Acme"
      country      = "US"
    }
  }
 
  permanent_deletion_time_in_days = 30
}
 
resource "aws_acmpca_certificate" "root" {
  certificate_authority_arn   = aws_acmpca_certificate_authority.provenance.arn
  certificate_signing_request = aws_acmpca_certificate_authority.provenance.certificate_signing_request
  signing_algorithm           = "SHA384WITHECDSA"
  template_arn                = "arn:aws:acm-pca:::template/RootCACertificate/V1"
 
  validity {
    type  = "YEARS"
    value = 10
  }
}
 
resource "aws_acmpca_certificate_authority_certificate" "root" {
  certificate_authority_arn = aws_acmpca_certificate_authority.provenance.arn
  certificate              = aws_acmpca_certificate.root.certificate
  certificate_chain        = aws_acmpca_certificate.root.certificate_chain
}

Signing Keys (KMS)

#
resource "aws_kms_key" "signing" {
  description              = "C2PA manifest signing key"
  customer_master_key_spec = "ECC_NIST_P384"
  key_usage                = "SIGN_VERIFY"
  enable_key_rotation      = false # rotate via key alias swap
}
 
resource "aws_kms_alias" "signing" {
  name          = "alias/c2pa-signer-current"
  target_key_id = aws_kms_key.signing.id
}

Tamper-Evident Append-Only Ledger

#
resource "aws_dynamodb_table" "manifest_log" {
  name         = "c2pa_manifest_log"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "manifest_id"
 
  attribute {
    name = "manifest_id"
    type = "S"
  }
 
  stream_enabled   = true
  stream_view_type = "NEW_IMAGE"
 
  point_in_time_recovery { enabled = true }
  server_side_encryption {
    enabled     = true
    kms_key_arn = aws_kms_key.ledger.arn
  }
}
 
# A scheduled Lambda hashes the day's entries and signs the digest with KMS,
# publishing it to a public S3 bucket for external auditors.
resource "aws_cloudwatch_event_rule" "daily_digest" {
  name                = "c2pa-daily-digest"
  schedule_expression = "cron(0 0 * * ? *)"
}

Verification API

#
resource "aws_apigatewayv2_api" "verify" {
  name          = "c2pa-verify"
  protocol_type = "HTTP"
}
 
resource "aws_lambda_function" "verify" {
  function_name = "c2pa-verify"
  role          = aws_iam_role.verify.arn
  package_type  = "Image"
  image_uri     = "${aws_ecr_repository.verify.repository_url}:${var.verify_tag}"
  timeout       = 10
  memory_size   = 1024
 
  environment {
    variables = {
      ROOT_CA_ARN  = aws_acmpca_certificate_authority.provenance.arn
      LEDGER_TABLE = aws_dynamodb_table.manifest_log.name
    }
  }
}

Tamper-Resistant Asset Storage

#
resource "aws_s3_bucket" "signed_assets" {
  bucket              = "acme-signed-assets"
  object_lock_enabled = true
}
 
resource "aws_s3_bucket_object_lock_configuration" "signed_assets" {
  bucket = aws_s3_bucket.signed_assets.id
  rule {
    default_retention {
      mode = "COMPLIANCE"
      years = 7
    }
  }
}

Best Practices

#
  • Hold root CA private key offline — Terraform configures the AWS Private CA, but keep root signing operations behind break-glass IAM.
  • Rotate signing aliases, not keys: KMS keys are immutable; you swap alias/c2pa-signer-current to roll over.
  • Object Lock the assets so even an attacker with admin can't tamper with signed media within retention.
  • Publish daily Merkle digests to make the ledger externally auditable.
#
#Terraform#Security#C2PA#AWS#PKI

Share this article