> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# GPG Keys rotations

### More Info:

GitHub GPG keys are used to cryptographically sign code commits and should be rotated every 180 days.

### Risk Level

High

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are step‑by‑step instructions to rotate (replace) a GPG key used for commit signing in GitHub, using the GitHub web console. The process is:

        1. Generate a new GPG key locally
        2. Add the new key to your GitHub account (console)
        3. Update your local Git configuration to use the new key
        4. Remove/disable the old key in GitHub (and locally)

        ***

        ## 1. Generate a new GPG key (locally)

        On your workstation (not in GitHub):

        ```bash theme={null}
        gpg --full-generate-key
        ```

        Recommended answers:

        * Key type: `RSA and RSA` (or modern ECC if you know you need it)
        * Key size: `4096` (for RSA)
        * Expiration: set an appropriate expiry date (e.g., 1 year)
        * Name: your real name
        * Email: the **same email** you use on GitHub
        * Comment: something like `GitHub signing key`
        * Passphrase: strong passphrase

        List your keys:

        ```bash theme={null}
        gpg --list-secret-keys --keyid-format=long
        ```

        Identify the new key’s ID (e.g., `ABCD1234EFGH5678`).

        Export the public key in ASCII:

        ```bash theme={null}
        gpg --armor --export ABCD1234EFGH5678
        ```

        Copy the entire block including `-----BEGIN PGP PUBLIC KEY BLOCK-----` and `-----END PGP PUBLIC KEY BLOCK-----`.

        ***

        ## 2. Add the new GPG key in the GitHub web console

        1. In your browser, sign in to GitHub.
        2. Click your profile picture (top‑right) → **Settings**.
        3. In the left sidebar, select **SSH and GPG keys**.
        4. Under **GPG keys**, click **New GPG key**.
        5. **Title**: something meaningful, e.g., `Work laptop GPG key (2026-07)`.
        6. **Key**: paste the ASCII‑armored public key you copied.
        7. Click **Add GPG key**.
        8. Confirm with your password or 2FA if prompted.

        Now GitHub trusts this new key for verifying your signed commits.

        ***

        ## 3. Configure Git locally to use the new key

        Tell Git to sign commits by default and use the new key:

        ```bash theme={null}
        git config --global user.signingkey ABCD1234EFGH5678
        git config --global commit.gpgsign true
        ```

        (Replace `ABCD1234EFGH5678` with your real key ID.)

        If using GPG v2, you may also need:

        ```bash theme={null}
        git config --global gpg.program gpg
        ```

        Test signing a commit in any repo:

        ```bash theme={null}
        echo "test" > testfile
        git add testfile
        git commit -S -m "Test commit with new GPG key"
        git push
        ```

        On GitHub, open the commit and confirm it shows as **Verified** and linked to your account.

        ***

        ## 4. Remove or disable the old GPG key in GitHub

        Once you confirm new signed commits are verified:

        1. In GitHub, go to **Settings → SSH and GPG keys**.
        2. Under **GPG keys**, locate the old key.
        3. Click **Delete** next to the old key.
        4. Confirm deletion.

        This prevents the old key from being used to produce commits GitHub will trust going forward.

        ***

        ## 5. (Optional) Revoke / remove the old key locally

        To fully complete rotation:

        * Revoke old key (if you have or generate a revocation certificate):

          ```bash theme={null}
          gpg --output revoked-old-key.asc --gen-revoke OLDKEYID
          gpg --import revoked-old-key.asc
          ```

        * Or delete old secret key from your keyring (only if you are sure you don’t need it):

          ```bash theme={null}
          gpg --delete-secret-key OLDKEYID
          gpg --delete-key OLDKEYID
          ```

        ***

        If you share more about your current state (number of keys, whether they’re already in GitHub, etc.), I can narrow this to just the exact actions you need.
      </Accordion>

      <Accordion title="Using CLI">
        Below is a minimal, step‑by‑step rotation process for GitHub GPG keys using the GitHub CLI (`gh`).

        Assumptions:

        * You already have `gh` installed and authenticated (`gh auth login`).
        * You’re rotating a **personal** GPG key used to sign commits/tags that GitHub verifies.

        ***

        ## 1. List Existing GPG Keys in GitHub

        ```bash theme={null}
        gh gpg-key list
        ```

        Note the ID or email associated with the key you want to rotate.

        ***

        ## 2. Generate a New GPG Key Locally

        ```bash theme={null}
        gpg --full-generate-key
        ```

        Recommended options:

        * Key type: `RSA and RSA` (or Ed25519 if supported via newer GPG)
        * Key size: at least `4096` for RSA
        * Expiration: set a future expiration date (e.g. 1y)
        * Use the same email as your GitHub account

        List keys to confirm:

        ```bash theme={null}
        gpg --list-secret-keys --keyid-format=long
        ```

        Copy the new key ID, e.g. `ABCD1234EF567890`.

        ***

        ## 3. Export the New Public Key

        ```bash theme={null}
        gpg --armor --export ABCD1234EF567890 > new-gpg-key.asc
        ```

        ***

        ## 4. Upload the New GPG Key to GitHub via `gh`

        ```bash theme={null}
        gh gpg-key add new-gpg-key.asc --title "Rotated GPG key $(date +%Y-%m-%d)"
        ```

        Confirm it appears:

        ```bash theme={null}
        gh gpg-key list
        ```

        ***

        ## 5. Update Local Git to Use the New Key

        Set the new key as the signing key:

        ```bash theme={null}
        git config --global user.signingkey ABCD1234EF567890
        git config --global commit.gpgsign true
        ```

        Test signing:

        ```bash theme={null}
        echo "test" > /tmp/test-gpg.txt
        git commit -am "test gpg"  # or commit in a repo
        ```

        Push a signed commit and verify it shows as “Verified” in GitHub.

        ***

        ## 6. Remove the Old GPG Key from GitHub

        After verifying the new key works, remove the old one by ID:

        1. Get the GitHub key ID (not GPG key ID) from:

           ```bash theme={null}
           gh gpg-key list
           ```

        2. Delete the old key:

           ```bash theme={null}
           gh gpg-key delete <github_gpg_key_id>
           ```

        (You’ll be prompted to confirm.)

        ***

        ## 7. (Optional) Revoke Old Key Locally

        If the old key should no longer be trusted anywhere:

        ```bash theme={null}
        gpg --edit-key OLDKEYID
        # In the prompt:
        #   revkey
        #   save
        ```

        Then optionally delete it:

        ```bash theme={null}
        gpg --delete-secret-and-public-key OLDKEYID
        ```

        ***

        This completes GPG key rotation for GitHub using the GitHub CLI.
      </Accordion>

      <Accordion title="Using Python">
        Below is a minimal, practical rotation approach: detect old GPG keys in GitHub, generate a new key, upload it, and remove the old key using Python and the GitHub API.

        ***

        ## 1. Prerequisites

        1. **GitHub Personal Access Token (PAT)**
           * Create one (Classic) with scopes:
             * `read:gpg_key`
             * `write:gpg_key`
           * Store it as an environment variable, e.g. `GITHUB_TOKEN`.

        2. **Python packages**
           ```bash theme={null}
           pip install requests python-gnupg
           ```

        3. **GnuPG installed** on the machine (needed by python-gnupg):
           * Linux: `sudo apt-get install gnupg` (or equivalent)
           * macOS: `brew install gnupg`
           * Windows: install Gpg4win

        ***

        ## 2. Define Rotation Policy

        Example policy (adapt as needed):

        * Rotate any GPG key:
          * older than 90 days, or
          * expiring within 7 days.

        ***

        ## 3. Script: List Existing GPG Keys and Identify “Stale” Ones

        ```python theme={null}
        import os
        import requests
        from datetime import datetime, timezone

        GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
        GITHUB_API = "https://api.github.com"
        HEADERS = {
            "Authorization": f"Bearer {GITHUB_TOKEN}",
            "Accept": "application/vnd.github+json"
        }

        ROTATE_AGE_DAYS = 90
        EXPIRING_SOON_DAYS = 7

        def list_gpg_keys():
            resp = requests.get(f"{GITHUB_API}/user/gpg_keys", headers=HEADERS)
            resp.raise_for_status()
            return resp.json()

        def parse_github_time(ts):
            # e.g. "2021-03-31T17:49:39Z"
            return datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)

        def find_keys_to_rotate(keys):
            now = datetime.now(timezone.utc)
            to_rotate = []
            for k in keys:
                created_at = parse_github_time(k["created_at"])
                age_days = (now - created_at).days

                # GitHub API for gpg_keys doesn't expose expiration directly; you may rely on age only
                if age_days >= ROTATE_AGE_DAYS:
                    to_rotate.append(k)
            return to_rotate

        if __name__ == "__main__":
            keys = list_gpg_keys()
            stale = find_keys_to_rotate(keys)
            print("Existing keys:")
            for k in keys:
                print(f"- id={k['id']} key_id={k['key_id']} created_at={k['created_at']}")
            print("\nKeys to rotate:")
            for k in stale:
                print(f"- id={k['id']} key_id={k['key_id']} created_at={k['created_at']}")
        ```

        Adjust the criteria as you like.

        ***

        ## 4. Generate a New GPG Key Using Python

        This example generates a new key for a given user/email, with an explicit expiration.

        ```python theme={null}
        import gnupg
        from pathlib import Path

        GPG_HOME = Path.home() / ".gnupg-rotation"  # separate home to avoid polluting default
        GPG_HOME.mkdir(exist_ok=True)
        gpg = gnupg.GPG(gnupghome=str(GPG_HOME))

        def generate_gpg_key(name, email, passphrase=None, expire="1y"):
            input_data = gpg.gen_key_input(
                name_real=name,
                name_email=email,
                key_type="RSA",
                key_length=4096,
                expire_date=expire,
                passphrase=passphrase or ""
            )
            key = gpg.gen_key(input_data)
            if not key.fingerprint:
                raise RuntimeError("Failed to generate GPG key")
            return key.fingerprint

        def export_public_key( fingerprint ):
            ascii_armored_public_keys = gpg.export_keys(fingerprint)
            return ascii_armored_public_keys

        if __name__ == "__main__":
            fingerprint = generate_gpg_key("GitHub User", "user@example.com", passphrase=None)
            pub_key = export_public_key(fingerprint)
            print(pub_key)
        ```

        Note:

        * Use the same name/email as your GitHub user email.
        * Keep private key + passphrase secure (recommended: hardware token or OS keystore).

        ***

        ## 5. Upload the New Public GPG Key to GitHub

        ```python theme={null}
        import os
        import requests

        GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
        GITHUB_API = "https://api.github.com"
        HEADERS = {
            "Authorization": f"Bearer {GITHUB_TOKEN}",
            "Accept": "application/vnd.github+json"
        }

        def upload_gpg_key(armored_public_key):
            payload = {"armored_public_key": armored_public_key}
            resp = requests.post(f"{GITHUB_API}/user/gpg_keys", headers=HEADERS, json=payload)
            resp.raise_for_status()
            return resp.json()

        if __name__ == "__main__":
            # Suppose you've generated and exported the public key as `pub_key`
            from pathlib import Path
            pub_key = Path("new_gpg_pub.asc").read_text()  # or pass directly from export_public_key()
            res = upload_gpg_key(pub_key)
            print("Uploaded new GPG key:", res)
        ```

        You can integrate step 4 and 5 into a single workflow: generate, export, upload.

        ***

        ## 6. Remove Old (Rotated) GPG Keys from GitHub

        Once the new key is uploaded and you’ve validated it works (by signing and pushing a test commit), remove the old keys:

        ```python theme={null}
        import os
        import requests

        GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
        GITHUB_API = "https://api.github.com"
        HEADERS = {
            "Authorization": f"Bearer {GITHUB_TOKEN}",
            "Accept": "application/vnd.github+json"
        }

        def delete_gpg_key(key_id):
            resp = requests.delete(f"{GITHUB_API}/user/gpg_keys/{key_id}", headers=HEADERS)
            if resp.status_code not in (204, 404):
                resp.raise_for_status()

        if __name__ == "__main__":
            # Assume you already identified keys_to_rotate from step 3
            from previous_script import list_gpg_keys, find_keys_to_rotate  # or copy functions

            keys = list_gpg_keys()
            stale = find_keys_to_rotate(keys)
            for k in stale:
                print(f"Deleting key id={k['id']} key_id={k['key_id']}")
                delete_gpg_key(k["id"])
        ```

        ***

        ## 7. Hook This Into Your IAM / Security Process

        * Run the rotation script:
          * Periodically via CI (GitHub Actions, Jenkins, etc.).
          * Or as part of an internal identity lifecycle tool.
        * Store:
          * Mapping of GitHub username → GPG fingerprint.
          * Key creation and (optional) expiration dates for compliance/audit.

        ***

        ## 8. Optional: Sign Commits with the New Key in Git

        On developer machines (not via Python but for completeness):

        ```bash theme={null}
        git config --global user.signingkey <NEW_KEY_FINGERPRINT>
        git config --global commit.gpgsign true
        ```

        ***

        If you specify:

        * how you store/manage private keys (local, HSM, KMS, Vault),
        * and whether you want org-wide or per-user automation,

        I can adjust this to a full rotation workflow (including per-user key policy and CI integration).
      </Accordion>

      <Accordion title="Using Terraform">
        Terraform cannot manage or rotate GitHub GPG signing keys.

        The official `integrations/github` (hashicorp/github) provider does not expose any resource or argument for user GPG keys; they are per‑user credentials managed via the GitHub UI or API, not repository or org configuration. Because of that, there is no `github_*` Terraform resource that can invalidate, delete, or create GPG keys, so this finding cannot be remediated on `github-applicationintegration-scm-repository` via Terraform.

        To remediate:

        1. Each affected GitHub user must:
           * Generate a new GPG key locally.
           * Upload it under: GitHub → Settings → SSH and GPG keys → New GPG key.
           * Configure `git commit -S` with the new key.
           * Delete the old GPG key from the same page.

        2. Optionally, automate rotation outside Terraform via:
           * `gh api` or GitHub REST API for GPG keys.
           * A scheduled job (e.g., GitHub Actions, external CI) that:
             * Lists keys older than 180 days.
             * Creates/upload new keys and deletes old ones.

        There is no `terraform plan` change to verify, since no Terraform-managed resource can represent or rotate these GPG keys.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
