How to Sign Windows Binaries Using AWS KMS & AWS Signer (Step-by-Step Guide)
Code signing is a non-negotiable requirement for distributing Windows executables. Whether you're publishing an installer, distributing internal enterprise tools, or deploying signed binaries for CI/CD, Windows requires Authenticode signatures to establish trust and prevent tampering.
But traditional code-signing workflows come with a major challenge: your private key must remain secure — yet legacy signing tools require storing a PFX file on build servers.
This is where AWS KMS + AWS Signer gives you a modern, cloud-secure way to sign Windows binaries without ever exposing your private key.
In this detailed guide, you’ll learn:
- How AWS Signer works with KMS
- How to prepare a signing certificate
- How to create a signing profile
- How to sign a Windows binary (EXE/MSI) end-to-end
- How to verify the signature on Windows
- CI/CD examples for GitHub Actions & CodeBuild
Let’s dive in.
Understanding Windows Code Signing in AWS
To sign a Windows binary, you need:
- A private key – used to generate the digital signature
- A signing certificate – identifies your organization
- A timestamp – preserves validity after certificate expiration
- A tool that produces an Authenticode signature
AWS solves this with two services:
| Need | AWS Service |
|---|---|
| Secure, non-exportable private key | AWS KMS |
| Managed code signing workflow | AWS Signer |
AWS Signer creates a signing job, signs the binary, and returns a fully signed Windows executable—no crypto engineering needed.
Architecture: How AWS Signer Uses KMS
Here’s the simplified flow:
Developer / CI
|
| Upload unsigned.exe to S3
v
AWS Signer ----> KMS private key (non-exportable)
|
| Produces Authenticode signature
v
Signed.exe → Output S3 bucket
You never access the private key — only AWS Signer calls KMS internally.
Prerequisites
Before signing Windows executables, you need:
1. A Windows Code-Signing Certificate
Options:
- Import an External Code-Signing Certificate (from DigiCert, Sectigo, etc.)
- Issue certificates internally via ACM Private CA
- Use existing certificates in your organization
2. Create a Signing Profile in AWS Signer
A signing profile defines:
- What platform you are signing for
- What signing certificate to use
- What hash algorithms are allowed
3. S3 Buckets
You need:
- Input bucket → store unsigned binaries
- Output bucket → store signed binaries
Step-by-Step: Signing Windows Binaries with AWS Signer
Step 1 — Create or Import a Signing Certificate
If you are using a certificate from DigiCert / Sectigo:
aws signer put-signing-profile \
--profile-name WinCodeSignProfile \
--signing-material certificateArn=arn:aws:acm:us-east-1:111122223333:certificate/abcd1234 \
--platform-id "AWSWindowsCodeSigning"
If using ACM PCA, issue a certificate using the public key AWS Signer manages.
Step 2 — Create a Signing Profile
aws signer put-signing-profile \
--profile-name WindowsCodeSign \
--platform-id AWSWindowsCodeSigning \
--signing-material certificateArn=arn:aws:acm:us-east-1:111122223333:certificate/abcd-1234 \
--signature-validity period=365,type=DAYS
Confirm the profile exists:
aws signer list-signing-profiles
Step 3 — Upload the Unsigned Binary to S3
Example:
aws s3 cp MyApp.exe s3://my-code-signing-input/MyApp.exe
Step 4 — Start a Signing Job
aws signer start-signing-job \
--profile-name WindowsCodeSign \
--source s3={bucketName=my-code-signing-input,key=MyApp.exe} \
--destination s3={bucketName=my-code-signing-output,prefix=signed/}
This returns a JSON response:
{
"jobId": "123abcde-456f-7890-1122-334455667788"
}
Step 5 — Check Signing Job Status
aws signer describe-signing-job --job-id 123abcde-456f-7890-1122-334455667788
Look for:
"status": "Succeeded"
Step 6 — Download the Signed Windows Binary
aws s3 cp s3://my-code-signing-output/signed/MyApp.exe ./MyApp-signed.exe
Verifying the Signature on Windows
Using signtool.exe
signtool verify /pa /v MyApp-signed.exe
Expected output:
Successfully verified: MyApp-signed.exe
Signer Certificate: CN=YourCompany
Timestamp: Valid
Using PowerShell
Get-AuthenticodeSignature MyApp-signed.exe
Output example:
Status: Valid
SignerCertificate: CN=YourCompany
CI/CD Integration Examples
GitHub Actions Example
name: Sign Binary
on: [push]
jobs:
sign:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Upload unsigned binary
run: aws s3 cp build/MyApp.exe s3://my-code-signing-input/
- name: Start signing job
id: sign
run: |
JOB=$(aws signer start-signing-job \
--profile-name WindowsCodeSign \
--source s3={bucketName=my-code-signing-input,key=MyApp.exe} \
--destination s3={bucketName=my-code-signing-output,prefix=signed/})
echo "jobid=$(echo $JOB | jq -r '.jobId')" >> $GITHUB_OUTPUT
- name: Wait for signing job to finish
run: |
aws signer wait successful-signing-job \
--job-id ${{ steps.sign.outputs.jobid }}
- name: Download signed binary
run: aws s3 cp s3://my-code-signing-output/signed/MyApp.exe MyApp-signed.exe
AWS CodeBuild Example
buildspec.yml
version: 0.2
phases:
build:
commands:
- aws s3 cp MyApp.exe s3://my-code-signing-input/
- >
JOBID=$(aws signer start-signing-job
--profile-name WindowsCodeSign
--source s3={bucketName=my-code-signing-input,key=MyApp.exe}
--destination s3={bucketName=my-code-signing-output,prefix=signed/}
--query jobId --output text)
- aws signer wait successful-signing-job --job-id $JOBID
- aws s3 cp s3://my-code-signing-output/signed/MyApp.exe ./MyApp-signed.exe
artifacts:
files:
- MyApp-signed.exe
Troubleshooting Common Issues
| Issue | Cause | Fix |
|---|---|---|
| Certificate not trusted | Certificate not from a trusted CA | Use DigiCert/Sectigo or install internal CA root |
| Signature missing timestamp | TSA not configured | Use AWS Signer—they add RFC3161 timestamps |
| signing-profile not found | Wrong profile name | Run aws signer list-signing-profiles |
| AccessDenied | IAM lacks permission | Add signer:* & kms:Sign permissions |
IAM Policies Required
Assign these roles to CI/CD or developer IAM roles:
Signer permissions
{
"Effect": "Allow",
"Action": [
"signer:StartSigningJob",
"signer:GetSigningProfile",
"signer:DescribeSigningJob"
],
"Resource": "*"
}
KMS permissions
{
"Effect": "Allow",
"Action": [
"kms:Sign",
"kms:GetPublicKey"
],
"Resource": "arn:aws:kms:us-east-1:111122223333:key/*"
}
S3 permissions
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-code-signing-*/*"
}
Best Practices for Secure Code Signing
- Use separate profiles for dev, staging, production
- Never store certificates or PFX locally
- Use KMS key rotation (manual for asymmetric keys)
- Restrict
signer:StartSigningJobto CI/CD roles - Enable CloudTrail for auditing signing operations
Closing Thoughts
AWS Signer + KMS gives you a secure, scalable, and fully automated way to sign Windows binaries without ever exposing your private key, avoiding PFX distribution risks and strengthening your supply-chain security posture.
This workflow supports:
- Automated CI/CD signing
- Enterprise compliance
- Cryptographic protection against tampering
- Scalable multi-team usage
Categories
Latest Resources
- How to Sign Windows Binaries Using AWS KMS & AWS Signer (Step-by-Step Guide)
- Checking Unsigned Drivers in Windows 11/10: Quick Troubleshooting Guide
- Exporting Your Code Signing Certificate as a PFX File in Chrome
- Exporting Your Code Signing Certificate as a PFX File in Internet Explorer
- How do I export my Code Signing Certificate from Firefox?
- How do I export my Code Signing Certificate from Safari?
- How do I sign a VBA Macro in MS Office with a timestamp?
- How do I use a Kernel Mode Driver Signing Certificate to sign driver files?
- What is the order procedure for a Code Signing Certificate?
- How Long Does It Take To Get a Code Signing Certificate?
Customers Reviews
FIPS-140 Level 2 USB or Existing HSM
Stored on an External Physical Device
3 to 5 Business Days