README
¶
PGP KMS
A Go command-line tool that integrates AWS Key Management Service (KMS) with PGP cryptography, enabling you to use AWS KMS asymmetric keys for PGP signing operations and export KMS public keys as PGP-compatible key blocks.
This project is inspired by https://github.com/hf/kmspgp, but tries to mimic the gpg command line interface and implements clearsign.
Features
- Export KMS public keys as PGP public key blocks
- Sign data using AWS KMS asymmetric keys with PGP formatting
- Clear text signing for human-readable signed messages
- Flexible input/output - works with files or stdin/stdout
- ASCII armoring support for text-safe output
Installation
Prerequisites
- Go 1.24.0 or later
- AWS credentials configured (via AWS CLI, environment variables, or IAM roles)
- AWS KMS asymmetric key (RSA or ECDSA) with
SIGN_VERIFYusage
Build from source
git clone https://github.com/vinnterab/pgpkms.git
cd pgpkms
go build -o pgpkms
AWS KMS Key Requirements
Your AWS KMS key must be:
- Asymmetric (not symmetric)
- Key usage:
SIGN_VERIFY - Key spec: RSA or ECDSA (e.g.,
RSA_2048,ECC_NIST_P256)
Usage
Export PGP Public Key
Export a KMS public key as a PGP public key block:
# Export with name and email
pgpkms --export -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 \
--export-name "John Doe" \
--export-email "john@example.com"
# Export with ASCII armor
pgpkms --export -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 \
--export-name "John Doe" \
--export-email "john@example.com" \
--armor
Sign Files
Sign a file using a KMS key:
# Sign a file (creates input.txt.asc)
pgpkms --sign -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 input.txt
# Sign with custom output file
pgpkms --sign -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 \
input.txt -o signature.sig
# Sign data from stdin to stdout
echo "Hello, World!" | pgpkms --sign -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012
# Sign data from an already-open file descriptor
pgpkms --enable-special-filenames --sign \
-u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 \
-&3
When --enable-special-filenames is set, positional input filenames may use GPG-style special filename syntax. -&N reads input from the already-open file descriptor N.
Clear Text Signing
Create human-readable signed messages:
# Clear sign a file
pgpkms --clear-sign -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 message.txt
# Clear sign from stdin
echo "This is a test message" | pgpkms --clear-sign -u arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012
Command Line Options
| Flag | Short | Description |
|---|---|---|
--sign |
-s |
Sign a file using KMS key |
--detach-sign |
-b |
Make a detached signature |
--detach |
Alias for --detach-sign |
|
--clear-sign |
Create a clear text signature | |
--clearsign |
Alias for --clear-sign |
|
--export |
Export KMS public key as PGP key block | |
--export-name |
Name for the exported PGP key | |
--export-email |
Email for the exported PGP key | |
--export-comment |
Comment for the exported PGP key | |
--armor |
-a |
Use ASCII armored format for output |
--armour |
Alias for --armor |
|
--local-user |
-u |
Key ID, ARN, alias, UID, fingerprint, or key ID hex |
--output |
-o |
Output file (default: input file + .asc) |
--digest-algo |
Hash algorithm: sha1, sha256, sha384, sha512 (default: sha256) | |
--list-secret-keys |
-K |
List signing keys in GPG-compatible format |
--with-colons |
Print key listings delimited by colons | |
--version |
Display version information (Formatted to be GPG compatible) | |
--status-fd |
Write status info to a file descriptor | |
--logger-fd |
Write log info to a file descriptor | |
--exit-on-status-write-error |
Exit if writing to status-fd fails | |
--enable-progress-filter |
Enable progress indicator reporting | |
--enable-special-filenames |
Enable GPG special filenames such as -&N for positional input |
|
--charset |
No-op. Accepted for GPG compatibility | |
--batch |
No-op. Accepted for GPG compatibility | |
--no-tty |
No-op. Accepted for GPG compatibility | |
--no-greeting |
No-op. Accepted for GPG compatibility | |
--no-sk-comments |
No-op. Accepted for GPG compatibility | |
--homedir |
No-op. Accepted for GPG compatibility | |
--lc-ctype |
No-op. Accepted for GPG compatibility |
Examples
Complete Workflow
-
Create a KMS key (if you don't have one):
aws kms create-key \ --description "PGP signing key" \ --key-usage SIGN_VERIFY \ --key-spec RSA_2048 -
Export the public key:
pgpkms --export -u arn:aws:kms:us-east-1:123456789012:key/your-key-id \ --export-name "Your Name" \ --export-email "your.email@example.com" \ --armor > publickey.asc -
Sign a document:
pgpkms --sign -u arn:aws:kms:us-east-1:123456789012:key/your-key-id document.txt -
Verify the signature (using standard PGP tools):
gpg --import publickey.asc gpg --verify document.txt.asc
Integration with CI/CD
Use pgpkms in CI/CD pipelines for secure artifact signing:
# Sign release artifacts
pgpkms --sign -u $KMS_KEY_ARN release.tar.gz
# Create detached signature
pgpkms --sign -u $KMS_KEY_ARN release.tar.gz -o release.tar.gz.sig
OSTree Commit Signing
pgpkms can replace gpg for signing OSTree commits, allowing you to use AWS KMS-backed keys instead of local GPG keyrings.
Setup: Tag your KMS key with PGPName and PGPEmail tags. These tags are used to build the PGP UID when listing keys (--list-secret-keys) and when resolving keys by UID via -u. This lets OSTree (and other tools) look up keys the same way they would with GPG.
aws kms tag-resource --key-id <key-id> \
--tags TagKey=PGPName,TagValue="OSTree Signing Key" \
TagKey=PGPEmail,TagValue="ostree@example.com"
Usage with OSTree: Symlink or alias pgpkms as gpg in your build environment, then use OSTree's standard signing workflow:
ln -s /path/to/pgpkms /usr/local/bin/gpg
ostree commit --sign-type=gpg --gpg-sign="OSTree Signing Key" ...
OSTree passes --status-fd, --batch, --no-tty, and other GPG flags that pgpkms accepts (see no-op flags above).
Replacing gpg in toolchains
The pgpkms cli is a clone of the gpg cli. This means that tools calling gpg can instead call pgpkms without any changes. A way to achieve that is symlink pgpkms to for instance /usr/local/bin/gpg in a build container/image.
Importing Existing GPG Keys into KMS
If you have an existing GPG key and import it into AWS KMS, the PGP fingerprint and key ID computed by pgpkms will not match the original key by default. This is because the PGP v4 fingerprint includes the key's creation timestamp, and KMS reports the import date rather than the original creation date.
To preserve the original fingerprint, tag your KMS key with PGPCreationTime set to the original key's creation time in RFC 3339 format.
1. Get the original creation time from GPG
# Replace KEYID with your key's fingerprint or ID
gpg --list-keys --with-colons KEYID | awk -F: '/^pub/{print $6}'
This prints a Unix timestamp (e.g., 1583064000). Convert it to RFC 3339:
date -u -d @1583064000 +%Y-%m-%dT%H:%M:%SZ
# Output: 2020-03-01T12:00:00Z
Or as a one-liner:
date -u -d @$(gpg --list-keys --with-colons KEYID | awk -F: '/^pub/{print $6}') +%Y-%m-%dT%H:%M:%SZ
On macOS, use date -u -r TIMESTAMP +%Y-%m-%dT%H:%M:%SZ instead.
2. Tag the KMS key
aws kms tag-resource --key-id <key-id> \
--tags TagKey=PGPCreationTime,TagValue="2020-03-01T12:00:00Z"
3. Verify the fingerprint
# List keys from KMS
./pgpkms --list-secret-keys
# Compare against the original
gpg --list-keys
The fingerprints should now match.
Note: If the
PGPCreationTimetag is missing,pgpkmsfalls back to the KMS creation date — existing keys that were created (not imported) in KMS are unaffected.
AWS Authentication
pgpkms uses the standard AWS SDK credential chain:
- Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - AWS credentials file (
~/.aws/credentials) - IAM roles (for EC2 instances)
- AWS CLI profiles
Required IAM Permissions
Your AWS credentials need the following KMS permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:GetPublicKey",
"kms:DescribeKey",
"kms:Sign"
],
"Resource": "arn:aws:kms:*:*:key/*"
}
]
}
Architecture
The project is organized into clean, focused packages:
main.go- Entry point and AWS configurationcmd/- Command-line interface and argument parsingkms/- AWS KMS integration and key managementpgp/- PGP key and signature generation
Development
Running Tests
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests verbosely
go test -v ./...
Code Quality
This project uses GolangCI-Lint for linting Go code. It helps maintain clean, idiomatic, and error-free code by running multiple linters efficiently. You can install GolangCI-Lint by following the official instructions here.
# Format code
go fmt ./...
# Lint code
go vet ./...
# Build
go build -o pgpkms
# golangci-lint
golangci-lint run ./...
Security Considerations
- Key Management: KMS keys are stored securely in AWS and never leave the service
- Audit Trail: All KMS operations are logged in AWS CloudTrail
- Access Control: Use IAM policies to control who can use which keys
- Key Rotation: Consider using KMS automatic key rotation for enhanced security
Limitations
- Only supports asymmetric KMS keys with
SIGN_VERIFYusage - Cannot generate new KMS keys (use AWS CLI or Console)
- PGP signatures are not timestamped (KMS limitation)
Contributing
Contributions are welcome! Please ensure:
- Code is properly formatted (
go fmt) - All tests pass (
go test ./...) - Code passes static analysis (
go vet) - New features include appropriate tests
License
This project is licensed under the MIT License - see the LICENSE file for details.
Troubleshooting
Common Issues
"Key not found" errors:
- Verify the KMS key ARN is correct
- Ensure your AWS credentials have access to the key
- Check that the key exists in the correct AWS region
"Invalid key usage" errors:
- Ensure the KMS key has
SIGN_VERIFYusage - Verify the key is asymmetric (not symmetric)
Permission errors:
- Check IAM permissions for
kms:GetPublicKey,kms:DescribeKey, andkms:Sign - Verify the key policy allows your principal to use the key
Output file errors:
- Ensure the output directory is writable
- Check that the output file doesn't already exist (pgpkms won't overwrite)
Documentation
¶
There is no documentation for this package.