GPG Key Setup for Maven Central Publishing
This guide explains how to configure GPG keys for publishing artifacts to Maven Central via GitHub Actions.
Prerequisites
Section titled “Prerequisites”- GPG installed:
brew install gnupg(macOS) orapt install gnupg(Linux) - Sonatype account at https://central.sonatype.com
Step 1: Generate Your GPG Key Pair
Section titled “Step 1: Generate Your GPG Key Pair”# Generate a new GPG keygpg --full-gen-keyRecommended configuration:
- Key type: RSA and RSA (default)
- Key size:
4096bits (minimum required by Maven Central) - Validity:
0= no expiration (or your preferred duration) - Name: Your real name
- Email: Your GitHub-associated email
- Passphrase: A strong password (store it securely)
Step 2: Create a Signing Subkey (Recommended for CI/CD)
Section titled “Step 2: Create a Signing Subkey (Recommended for CI/CD)”Using subkeys is more secure because they can be revoked independently from your master key.
# List your keys to find the key IDgpg --list-secret-keys
# Output example:# sec rsa4096 2026-02-11 [SC]# ABCD1234EFGH5678IJKL9012MNOP3456QR# uid [ultimate] Your Name <your@email.com># ssb rsa4096 2026-02-11 [E]
# Your key ID is the last 16 characters after "sec rsa4096"# Example: ABCD1234EFGH5678IJKL9012MNOP3456QRCreate a dedicated signing subkey:
gpg --edit-key YOUR_KEY_ID
# At the GPG prompt:gpg> addkey# Select: (4) RSA (sign only)# Size: 4096# Duration: 2y (2 years, or your preference)# Confirm: y# Passphrase: enter or use same as master key
gpg> savegpg> quitStep 3: Export Your ASCII Armored Private Key
Section titled “Step 3: Export Your ASCII Armored Private Key”Option A: Subkey only (RECOMMENDED for CI/CD)
# Export the PRIVATE KEY (subkey) in ASCII formatgpg --export-secret-keys --armor YOUR_SUBKEY_ID > private-subkey.asc
# Verify it starts with:# -----BEGIN PGP PRIVATE KEY BLOCK-----
# Display the content (to copy):cat private-subkey.asc
# Copy the ENTIRE content including BEGIN and END linesOption B: Full key (if you need the master key as well)
gpg --export-secret-keys --armor YOUR_KEY_ID > private-key.ascStep 4: Get Your Subkey ID
Section titled “Step 4: Get Your Subkey ID”# List secret keys to identify the signing subkeygpg --list-secret-keys
# Output example:# sec rsa4096 2026-02-11 [SC]# ABCD1234EFGH5678IJKL9012MNOP3456QR# uid [ultimate] Your Name <your@email.com># ssb rsa4096 2026-02-11 [E] ← Encryption subkey# ssb rsa4096 2026-02-11 [S] ← THIS is the signing subkey# 1234567890ABCDEF1234567890ABCDEF12345678
# The subkey ID is the last 16 characters# Example: 1234567890ABCDEF1234567890ABCDEF12345678Step 5: Publish Your Public Key to a Key Server
Section titled “Step 5: Publish Your Public Key to a Key Server”# Send to keyserver (choose one)gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
# Alternative keyservers:# gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID# gpg --keyserver pgp.mit.edu --send-keys YOUR_KEY_IDStep 6: Configure GitHub Secrets
Section titled “Step 6: Configure GitHub Secrets”Go to GitHub → Repository → Settings → Secrets and variables → Actions and create these 4 secrets:
| Secret Name | Value |
|---|---|
SIGNING_IN_MEMORY_KEY | ENTIRE content of private-subkey.asc (including BEGIN and END lines) |
SIGNING_IN_MEMORY_KEY_PASSWORD | The passphrase you set when creating the key |
MAVEN_CENTRAL_USERNAME | Your Sonatype username |
MAVEN_CENTRAL_PASSWORD | User Token from Sonatype (NOT your password) |
Step 7: Create User Token in Sonatype
Section titled “Step 7: Create User Token in Sonatype”- Go to https://central.sonatype.com
- Login with your Sonatype account
- Click on your avatar → Profile
- Find User Token or Generate Token
- Create a token and copy:
- Username →
MAVEN_CENTRAL_USERNAME - Password →
MAVEN_CENTRAL_PASSWORD
- Username →
Important: Never use a never-expiring token. User tokens should be rotated periodically.
Step 8: Verify GPG Key Format
Section titled “Step 8: Verify GPG Key Format”Ensure your SIGNING_IN_MEMORY_KEY secret contains:
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQHYBF... (base64 encoded key data)...-----END PGP PRIVATE KEY BLOCK-----NOT binary format (which would look like random binary characters).
Testing Locally
Section titled “Testing Locally”# Export variables and test publishexport ORG_GRADLE_PROJECT_signingInMemory_KEY="$(cat private-subkey.asc)"export ORG_GRADLE_PROJECT_signingInMemory_KEY_PASSWORD="your_passphrase"export ORG_GRADLE_PROJECT_mavenCentralUsername="your_sonatype_username"export ORG_GRADLE_PROJECT_mavenCentralPassword="your_sonatype_token"
# Test (dry-run mode if available)./gradlew publishToMavenCentral --dry-run
# Or actual publish./gradlew publishToMavenCentralTroubleshooting
Section titled “Troubleshooting””gpg: signing failed: Inappropriate ioctl for device”
Section titled “”gpg: signing failed: Inappropriate ioctl for device””This happens in CI environments without a TTY. The configuration in this project uses in-memory keys, which should work around this issue.
”No secret key” error
Section titled “”No secret key” error”Verify that:
- The key is exported with
--armorflag (ASCII format) - The secret includes
-----BEGIN PGP PRIVATE KEY BLOCK----- - The correct key ID is used (if using subkey)
Key expired
Section titled “Key expired”To extend expiration:
gpg --edit-key YOUR_KEY_IDgpg> listgpg> key 1 # Select the signing subkey (second key)gpg> expire# Set new expirationgpg> savegpg> quit
# Re-publish to keyservergpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_IDSecurity Best Practices
Section titled “Security Best Practices”- NEVER commit any
.ascfiles or private keys to version control - ALWAYS use User Tokens in Sonatype, not your account password
- Subkeys expire - set calendar reminders to renew them before expiration
- Backup your master key securely (encrypted USB, hardware wallet, etc.)
- Consider using hardware security keys (YubiKey) for maximum security
- Rotate tokens periodically, especially if you suspect any compromise