Skip to main content

Signature Algorithm

AiHint uses RSA-SHA256 with PKCS#1 v1.5 padding for cryptographic signatures. This page describes the exact signing and verification process.

Overview

Every AiHint document contains a signature field that allows consumers to verify:

  1. Authenticity — The document was created by the claimed issuer.
  2. Integrity — The document has not been modified since signing.

The signing process uses standard RSA cryptography with widely available libraries, ensuring that any platform can both produce and verify signatures.

Key Format

  • Algorithm: RSA
  • Minimum key size: 2048 bits (4096 bits recommended)
  • Format: PEM-encoded PKCS#8 (private) and SPKI (public)

Generating Keys

Using OpenSSL:

# Generate a 4096-bit RSA private key
openssl genrsa -out private_key.pem 4096

# Extract the public key
openssl rsa -in private_key.pem -pubout -out public_key.pem

The public key must be published at a URL accessible to verifiers (the URL goes in the public_key_url field).

Signing Process

Step 1: Prepare the Canonical Payload

To sign an AiHint document, create a copy of the document JSON with the signature field removed. This produces the canonical payload.

Given this document:

{
"version": "0.1",
"type": "global",
"target": "https://example.com",
"issuer": "https://trust.aihint.org",
"score": 0.92,
"method": "aihint-core-v1",
"issued_at": "2025-06-15T12:00:00Z",
"expires_at": "2026-06-15T12:00:00Z",
"comment": "Verified domain",
"signature": "",
"public_key_url": "https://trust.aihint.org/pubkey.pem"
}

The canonical payload is:

{
"version": "0.1",
"type": "global",
"target": "https://example.com",
"issuer": "https://trust.aihint.org",
"score": 0.92,
"method": "aihint-core-v1",
"issued_at": "2025-06-15T12:00:00Z",
"expires_at": "2026-06-15T12:00:00Z",
"comment": "Verified domain",
"public_key_url": "https://trust.aihint.org/pubkey.pem"
}

Step 2: Serialize to JSON String

Serialize the canonical payload to a JSON string. The serialization MUST use:

  • No trailing whitespace
  • UTF-8 encoding
note

The exact JSON formatting (compact vs. pretty-printed, key ordering) does not matter as long as the same serialization is used for both signing and verification. Implementations should serialize the payload once and use that exact byte sequence for signing.

Step 3: Sign with RSA-SHA256

Sign the UTF-8 byte representation of the JSON string using:

  • Hash: SHA-256
  • Padding: PKCS#1 v1.5
  • Key: The issuer's RSA private key

Step 4: Encode as Base64

Encode the raw signature bytes as a standard Base64 string (RFC 4648). Place this value in the signature field of the document.

Verification Process

Step 1: Fetch the Public Key

Download the issuer's public key from the URL in public_key_url. The key MUST be in PEM format.

curl -o issuer_pubkey.pem https://trust.aihint.org/pubkey.pem

Step 2: Extract the Canonical Payload

Remove the signature field from the document to reconstruct the canonical payload. Serialize it to a JSON string using the same method as signing.

Step 3: Decode the Signature

Decode the Base64 string from the signature field into raw bytes.

Step 4: Verify with RSA-SHA256

Verify the signature using:

  • Hash: SHA-256
  • Padding: PKCS#1 v1.5
  • Key: The issuer's RSA public key
  • Data: The UTF-8 bytes of the canonical payload JSON string
  • Signature: The decoded raw signature bytes

If verification succeeds, the document is authentic and unmodified.

Example: Signing with OpenSSL

# 1. Create the canonical payload (remove "signature" key from JSON)
cat aihint.json | jq 'del(.signature)' > payload.json

# 2. Sign the payload
openssl dgst -sha256 -sign private_key.pem -out signature.bin payload.json

# 3. Encode as Base64
base64 -i signature.bin -o signature.b64

# 4. Insert the signature back into the document
cat aihint.json | jq --arg sig "$(cat signature.b64)" '.signature = $sig' > aihint-signed.json

Example: Verifying with OpenSSL

# 1. Extract the canonical payload
cat aihint.json | jq 'del(.signature)' > payload.json

# 2. Extract and decode the signature
cat aihint.json | jq -r '.signature' | base64 -d > signature.bin

# 3. Fetch the public key
curl -o pubkey.pem $(cat aihint.json | jq -r '.public_key_url')

# 4. Verify
openssl dgst -sha256 -verify pubkey.pem -signature signature.bin payload.json
# Output: "Verified OK" if valid

Security Considerations

  • Minimum key size: 2048 bits. Keys smaller than this are considered insecure.
  • Key rotation: Issuers should rotate keys periodically. Old public keys should remain available for verifying previously-issued hints until they expire.
  • HTTPS only: The public_key_url MUST use HTTPS to prevent man-in-the-middle attacks during key retrieval.
  • Expiration: Always check expires_at before trusting a hint, even if the signature is valid.

Next Steps