Signing Keys
Ed25519 key generation, management, rotation, and security best practices for evidence bundles.
Ed25519 Overview
Algorithm: EdDSA (Edwards-curve Digital Signature Algorithm) with Curve25519
Properties:
- Fast: Signature generation and verification < 1ms
- Small: 64-byte signatures, 32-byte keys
- Secure: 128-bit security level
- Deterministic: Same input always produces same signature
- Standard: RFC 8032
Use in evidence SDK:
- Sign
checksums.sha256file - Signature stored in
signature.sig - Public key embedded in
manifest.json - Verification performed by auditors or evidence CLI
Key Generation
Using evidence CLI (Recommended)
Generate key pair:
evidence init --generate-keysOutput:
Generating Ed25519 key pair...
✓ Private key: ~/.evidence/keys/private.pem
✓ Public key: ~/.evidence/keys/public.pem
⚠ IMPORTANT:
- Keep private.pem SECRET (never commit to git)
- Share public.pem with auditors for bundle verification
- Back up private key securely (encrypted)
Key ID: evidence-sdk-v1
Fingerprint: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz0567Created files:
~/.evidence/keys/
├── private.pem # 32 bytes (256 bits) - KEEP SECRET
└── public.pem # 32 bytes (256 bits) - OK to shareManual Generation (Advanced)
Using OpenSSL:
# Generate private key
openssl genpkey -algorithm ed25519 -out private.pem
# Extract public key
openssl pkey -in private.pem -pubout -out public.pemUsing Node.js (noble-ed25519):
import * as ed from '@noble/ed25519';
import { writeFileSync } from 'fs';
// Generate key pair
const privateKey = ed.utils.randomPrivateKey();
const publicKey = await ed.getPublicKey(privateKey);
// Convert to PEM format
const privatePem = `-----BEGIN PRIVATE KEY-----
${Buffer.from(privateKey).toString('base64')}
-----END PRIVATE KEY-----`;
const publicPem = `-----BEGIN PUBLIC KEY-----
${Buffer.from(publicKey).toString('base64')}
-----END PUBLIC KEY-----`;
// Save to files
writeFileSync('private.pem', privatePem);
writeFileSync('public.pem', publicPem);Key Storage
Local Development
Standard location:
~/.evidence/keys/
├── private.pem
└── public.pemFile permissions:
# Private key: readable only by owner
chmod 600 ~/.evidence/keys/private.pem
# Public key: readable by all (OK to share)
chmod 644 ~/.evidence/keys/public.pem
# Verify
ls -l ~/.evidence/keys/
# -rw------- 1 user user ... private.pem
# -rw-r--r-- 1 user user ... public.pemProduction Servers
Secure location:
# Create secure directory
sudo mkdir -p /etc/evidence/keys
sudo chown root:root /etc/evidence/keys
sudo chmod 700 /etc/evidence/keys
# Copy keys
sudo cp private.pem /etc/evidence/keys/
sudo chmod 600 /etc/evidence/keys/private.pem
# Configurationbundle:
signing:
private_key_path: /etc/evidence/keys/private.pemCI/CD Environments
GitHub Actions:
name: Evidence Collection
on:
schedule:
- cron: '0 0 1 * *'
jobs:
collect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup signing key
run: |
mkdir -p ~/.evidence/keys
echo '${{ secrets.EVIDENCE_SIGNING_KEY }}' > ~/.evidence/keys/private.pem
chmod 600 ~/.evidence/keys/private.pem
- name: Collect evidence
run: evidence collect
- name: Cleanup
if: always()
run: rm -f ~/.evidence/keys/private.pemAdd secret:
- Copy private key content:
cat ~/.evidence/keys/private.pem | pbcopy - Repository → Settings → Secrets and variables → Actions
- New repository secret
- Name:
EVIDENCE_SIGNING_KEY - Value: Paste private key content
- Click "Add secret"
Key Rotation
Why Rotate Keys
Security best practices:
- Limit exposure window if key compromised
- Comply with security policies (annual rotation)
- Maintain cryptographic hygiene
Recommended rotation schedule:
- Production: Every 365 days
- After security incident: Immediately
- After team member with key access departs: Within 7 days
Rotation Procedure
Step 1: Generate new key pair
evidence init --generate-keys --key-id production-2027-01Or with custom location:
openssl genpkey -algorithm ed25519 -out ~/.evidence/keys/private-2027-01.pem
openssl pkey -in ~/.evidence/keys/private-2027-01.pem -pubout -out ~/.evidence/keys/public-2027-01.pemStep 2: Update configuration
bundle:
signing:
private_key_path: ~/.evidence/keys/private-2027-01.pem
key_id: production-2027-01Step 3: Test collection
evidence collectVerify:
# Check bundle was signed with new key
tar -xzf evidence-bundle-*.tar.gz
cat manifest.json | jq '.signer.key_id'
# Output: "production-2027-01"Step 4: Archive old key
# Move to archive (don't delete yet!)
mkdir -p ~/.evidence/keys/archive
mv ~/.evidence/keys/private.pem ~/.evidence/keys/archive/private-2026-01.pem
mv ~/.evidence/keys/public.pem ~/.evidence/keys/archive/public-2026-01.pemWhy keep old keys?
- Verify historical bundles
- Auditors may request verification of old bundles
- Recommended retention: 3 years
Step 5: Update public key distribution
- Share new public key with auditors
- Upload to evidence platform (Settings → Signing Keys)
- Update documentation
- Notify compliance team
Zero-Downtime Rotation
For CI/CD environments:
Step 1: Add new key alongside old key
# GitHub Actions - both keys available
- name: Setup signing keys
run: |
mkdir -p ~/.evidence/keys
echo '${{ secrets.EVIDENCE_SIGNING_KEY_OLD }}' > ~/.evidence/keys/private-old.pem
echo '${{ secrets.EVIDENCE_SIGNING_KEY_NEW }}' > ~/.evidence/keys/private-new.pemStep 2: Update configuration to use new key
bundle:
signing:
private_key_path: ~/.evidence/keys/private-new.pemStep 3: Test in staging
Step 4: Deploy to production
Step 5: Remove old key (after 30 days)
Key Distribution
Sharing Public Keys
Public keys are safe to share - they only verify signatures, cannot create them.
Distribution methods:
1. Email to auditor:
cat ~/.evidence/keys/public.pem | mail -s "Evidence Signing Public Key" auditor@compliance-firm.com2. Upload to evidence platform:
- Log in to evidence platform
- Settings → Signing Keys
- Click "Upload Public Key"
- Select
public.pem - Add key ID and description
- Click "Save"
3. Include in documentation:
## Public Key Verification
Verify evidence bundles with our public key:
\`\`\`
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAXyz9876abc123def456ghi789jkl012mno345
-----END PUBLIC KEY-----
\`\`\`
Key ID: production-2026-01
Fingerprint: abc123def456ghi789jkl012mno345pqr678stu901vwx234Out-of-Band Verification
Best practice for auditors:
- Receive bundle (email, file transfer)
- Receive public key via separate channel (phone, Slack, verified email)
- Verify fingerprint (voice call, video call)
- Verify bundle with confirmed public key
Generate fingerprint:
openssl pkey -pubin -in public.pem -outform DER | \
openssl dgst -sha256 -binary | \
base64Example fingerprint:
abc123def456ghi789jkl012mno345pqr678stu901vwx234=Confirm via phone:
- "Alpha Bravo Charlie one two three..."
- Read back for verification
- Document confirmation
Multi-Environment Keys
Separate Keys Per Environment
Development:
bundle:
signing:
private_key_path: ~/.evidence/keys/dev-private.pem
key_id: development-2026Staging:
bundle:
signing:
private_key_path: ~/.evidence/keys/staging-private.pem
key_id: staging-2026Production:
bundle:
signing:
private_key_path: /etc/evidence/keys/production-private.pem
key_id: production-2026-01Benefits:
- Compromise of dev key doesn't affect production
- Different access control per environment
- Clear distinction in bundle metadata
Key Inventory
Maintain key inventory document:
# Evidence Signing Key Inventory
## Active Keys
| Key ID | Environment | Created | Expires | Public Key Fingerprint |
|--------|-------------|---------|---------|------------------------|
| production-2026-01 | Production | 2026-01-01 | 2027-01-01 | abc123... |
| staging-2026-01 | Staging | 2026-01-15 | 2027-01-15 | def456... |
| development-2026 | Development | 2026-01-01 | Never | ghi789... |
## Archived Keys
| Key ID | Environment | Created | Rotated | Retention Until |
|--------|-------------|---------|---------|-----------------|
| production-2025-01 | Production | 2025-01-01 | 2026-01-01 | 2029-01-01 |
| staging-2025-01 | Staging | 2025-01-15 | 2026-01-15 | 2029-01-15 |
## Access Log
| Date | Action | Key ID | User | Notes |
|------|--------|--------|------|-------|
| 2026-01-01 | Generated | production-2026-01 | admin | Annual rotation |
| 2026-01-02 | Distributed | production-2026-01 | admin | Sent to auditor |
| 2026-01-30 | Archived | production-2025-01 | admin | 30-day grace period |Key Backup
Encrypted Backup
Create encrypted backup:
# Backup with GPG
gpg --symmetric --cipher-algo AES256 ~/.evidence/keys/private.pem
# Output: private.pem.gpg (encrypted)
# Restore
gpg --decrypt private.pem.gpg > private.pem
chmod 600 private.pemPassword requirements:
- Minimum 20 characters
- Mix of uppercase, lowercase, numbers, symbols
- Store password in separate location (password manager)
Cold Storage
For long-term archival:
-
Encrypt key:
gpg --symmetric --cipher-algo AES256 private.pem -
Create redundant copies:
- Copy 1: Secure cloud storage (encrypted)
- Copy 2: Physical USB drive (encrypted, locked safe)
- Copy 3: Password manager secure notes
-
Document recovery procedure:
# Key Recovery Procedure 1. Retrieve encrypted key from storage 2. Decrypt with GPG password (stored in 1Password) 3. Verify fingerprint matches inventory 4. Copy to ~/.evidence/keys/ 5. Set permissions: chmod 600 6. Test with verification
Verification
Verify Key Pair Match
Ensure private and public keys match:
# Extract public key from private key
openssl pkey -in private.pem -pubout -out public-from-private.pem
# Compare with existing public key
diff public.pem public-from-private.pem
# If identical, keys match (no output)Verify Bundle Signature
Using evidence CLI:
evidence verify evidence-bundle-*.tar.gz --public-key public.pemManual verification:
# Extract bundle
tar -xzf evidence-bundle-*.tar.gz -C /tmp/bundle
# Verify with public key (requires Ed25519 library)
# Complex - use evidence CLI instead
evidence verify evidence-bundle-*.tar.gzSecurity Best Practices
✅ DO
- Generate unique keys per environment
- Rotate keys annually (production)
- Use strong file permissions (600 for private keys)
- Back up keys encrypted
- Archive old keys for 3+ years
- Document all key operations
- Verify fingerprints out-of-band
- Use CI/CD secrets for keys, not files
❌ DON'T
- Never commit private keys to git
- Never share private keys (even within team)
- Never use same key across environments
- Never delete old keys immediately after rotation
- Never store keys unencrypted in cloud storage
- Never email private keys
- Never screenshot or copy private keys to clipboard tools
Troubleshooting
Private Key Not Found
Symptom:
✗ Bundle signing failed
Error: ENOENT: no such file or directory, open '~/.evidence/keys/private.pem'Solution:
# Check if file exists
ls -l ~/.evidence/keys/private.pem
# If not, generate new key pair
evidence init --generate-keys
# Or specify correct path in configPermission Denied
Symptom:
✗ Bundle signing failed
Error: EACCES: permission denied, open '/etc/evidence/keys/private.pem'Solution:
# Fix permissions
sudo chmod 600 /etc/evidence/keys/private.pem
sudo chown $USER:$USER /etc/evidence/keys/private.pemInvalid Key Format
Symptom:
✗ Bundle signing failed
Error: Invalid private key formatCauses:
- Key file corrupted
- Wrong file (public key instead of private)
- Not Ed25519 format
Solution:
# Verify key type
openssl pkey -in private.pem -text -noout
# Should show: ED25519 Private-Key
# If corrupted, restore from backup or generate newSignature Verification Failed
Symptom:
✗ Signature verification failed
Signature does not match checksums file.Causes:
- Bundle modified after signing
- Wrong public key used for verification
- Bundle created with different private key
Solution:
# Verify public key matches bundle
tar -xzf bundle.tar.gz
cat manifest.json | jq '.signer.key_id'
# Compare with your key inventory
# Use correct public key
evidence verify bundle.tar.gz --public-key public-2026-01.pemKey Lifecycle
Timeline
Day 0: Generate key pair
├── Save private key to secure location
├── Set file permissions (chmod 600)
├── Add to evidence.yaml configuration
└── Test with evidence collect
Day 1-7: Distribution
├── Share public key with auditors
├── Upload to evidence platform
├── Document in key inventory
└── Back up private key (encrypted)
Day 30-365: Active use
├── Monitor bundle signing
├── Verify periodic backups
└── Review key inventory quarterly
Day 365: Rotation
├── Generate new key pair
├── Update configuration
├── Test collection
├── Archive old key (don't delete)
└── Update public key distribution
Day 366-1095: Retention
├── Keep archived keys for verification
├── Update key inventory (archived status)
└── Maintain encrypted backups
Day 1095+: Disposal
├── Securely delete archived keys
├── Update inventory (disposed status)
└── Document disposal dateSee Also
- Bundle Format - Bundle signature specification
- evidence verify - Signature verification
- evidence collect - Bundle signing
- Credential Management - General security practices