git-crypt fails to recognize my key in my keychain.
Summary
I'm trying to unlock my repository but git-crypt fails because it believes there's no secret key. However, gpg -K shows my secret key is there.
The error I'm getting:
gpg: decryption failed: No secret key
git-crypt: GPG error: Failed to decrypt
Questions:
- How does git-crypt identify which key to use to unlock the repository?
- What commands can I use to verify that a gpg key is known to git-crypt?
Background
I'm trying to unlock my repository in our CI pipeline. The script below installs git-crypt and gpg, before configuring itself for an unattended lock. You can use it to reproduce this issue. You can also verify from the included logs of the script running that the keys were successfully imported and found on the gpg key chain before the unlock fails.
Script to reproduce
#!/usr/bin/env bash
# https://medium.com/@ahmed.kamel/git-crypt-unattended-unlock-with-gpg-passphrase-in-docker-f0aa39b85a
#
# Expected env vars:
# GPG_PUBLIC_KEY: the gpg public key
# GPG_SECRET_KEY: the gpg secret key
# GPG_PASSPHRASE: the gpg key's passphrase
# GPG_KEY_FINGERPRINT: the gpg key's fingerprint.
# GPG_KEYGRIP: the key's keygrip.
set -eo pipefail
export GPG_TTY=$(tty)
# 1. Install dependencies. (gpg/git-crypt)
WORKING_DIR=${PWD##*/}
sudo su
echo "🤖: Installing git-crypt"
cd ..
mkdir tmp
cd tmp
git clone https://github.com/AGWA/git-crypt.git
cd git-crypt
echo "🤖: making"
sudo make
echo "🤖: make install"
sudo make install PREFIX=/usr/local
cd ../../$WORKING_DIR
# Install gnupg and gnupg-agent, gnupg-agent is used to keep and
# release the passphrase to git-crypt when the time comes
echo "🤖: apt-get update"
sudo apt-get update
echo "🤖: apt-get install -y gnupg"
sudo apt-get install -y gnupg
echo "🤖: apt-get install -y gnupg-agent"
sudo apt-get install -y gnupg-agent
# 2. import the key into the gpg keyring
# https://discuss.circleci.com/t/gpg-keys-as-environment-variables/28641/4
# https://discuss.circleci.com/t/error-sending-to-agent-inappropriate-ioctl-for-device/17465/6
echo "🤖: Importing public key."
echo -e "${GPG_PUBLIC_KEY}" | gpg --import --no-tty --batch --yes
echo "🤖: Importing secret key."
echo -e "${GPG_SECRET_KEY}" | gpg --import --no-tty --batch --yes
echo "🤖: Trusting key"
FPR=`gpg --with-colons --fingerprint $GPG_KEY_FINGERPRINT | awk -F: '$1 == "fpr" {print$10; exit}'`
# replace trust level with ultimate
echo "FPR"
echo $FPR
gpg --with-colons --fingerprint $GPG_KEY_FINGERPRINT
gpg --export-ownertrust && echo $FPR:6: | gpg --import-ownertrust
# 3. Setup GPG for automated remove
# Adapted from: https://medium.com/@ahmed.kamel/git-crypt-unattended-unlock-with-gpg-passphrase-in-docker-f0aa39b85a
echo "Configuring git-crypt for automated unlock..."
gpgconf --kill gpg-agent
eval `gpg-agent --daemon --allow-preset-passphrase`
GPG_PASSPHRASE_HEX=`echo -n "$GPG_PASSPHRASE" | od -A n -t x1 | sed 's/ *//g'`
gpg-connect-agent "PRESET_PASSPHRASE $GPG_KEYGRIP -1 $GPG_PASSPHRASE_HEX" /bye
# 4. Unlock
echo "🤖: listing keys"
gpg --list-keys
gpg --list-secret-keys
gpg --export-ownertrust
echo "🤖: Unlocking"
git-crypt unlock
cc: #94, #182 - here's an example of how I'm doing an unattended unlock.
Script output
🤖: Installing git-crypt
Cloning into 'git-crypt'...
🤖: making
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o git-crypt.o git-crypt.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o commands.o commands.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o crypto.o crypto.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o gpg.o gpg.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o key.o key.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o util.o util.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o parse_options.o parse_options.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o coprocess.o coprocess.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o fhstream.o fhstream.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o crypto-openssl-10.o crypto-openssl-10.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -c -o crypto-openssl-11.o crypto-openssl-11.cpp
g++ -Wall -pedantic -Wno-long-long -O2 -std=c++11 -o git-crypt git-crypt.o commands.o crypto.o gpg.o key.o util.o parse_options.o coprocess.o fhstream.o crypto-openssl-10.o crypto-openssl-11.o -lcrypto
🤖: make install
install -d /usr/local/bin
install -m 755 git-crypt /usr/local/bin/
🤖: apt-get update
Hit:1 http://azure.archive.ubuntu.com/ubuntu focal InRelease
Get:2 http://azure.archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
Get:3 http://azure.archive.ubuntu.com/ubuntu focal-backports InRelease [101 kB]
Get:4 https://packages.microsoft.com/ubuntu/20.04/prod focal InRelease [10.5 kB]
Get:5 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Get:6 http://azure.archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages [1033 kB]
Hit:7 http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu focal InRelease
Get:8 http://azure.archive.ubuntu.com/ubuntu focal-updates/main Translation-en [230 kB]
Get:9 http://azure.archive.ubuntu.com/ubuntu focal-updates/main amd64 c-n-f Metadata [13.5 kB]
Get:10 http://azure.archive.ubuntu.com/ubuntu focal-updates/universe amd64 Packages [785 kB]
Get:11 http://azure.archive.ubuntu.com/ubuntu focal-updates/universe Translation-en [171 kB]
Get:12 http://azure.archive.ubuntu.com/ubuntu focal-updates/universe amd64 c-n-f Metadata [17.7 kB]
Get:13 http://azure.archive.ubuntu.com/ubuntu focal-updates/multiverse amd64 Packages [23.6 kB]
Get:14 http://azure.archive.ubuntu.com/ubuntu focal-updates/multiverse amd64 c-n-f Metadata [648 B]
Get:15 https://packages.microsoft.com/ubuntu/20.04/prod focal/main amd64 Packages [78.2 kB]
Get:16 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [708 kB]
Get:17 http://security.ubuntu.com/ubuntu focal-security/main Translation-en [142 kB]
Get:18 http://security.ubuntu.com/ubuntu focal-security/main amd64 c-n-f Metadata [7900 B]
Get:19 http://security.ubuntu.com/ubuntu focal-security/universe amd64 Packages [590 kB]
Get:20 http://security.ubuntu.com/ubuntu focal-security/universe Translation-en [95.7 kB]
Get:21 http://security.ubuntu.com/ubuntu focal-security/universe amd64 c-n-f Metadata [11.6 kB]
Fetched 4246 kB in 1s (4213 kB/s)
Reading package lists...
🤖: apt-get install -y gnupg
Reading package lists...
Building dependency tree...
Reading state information...
gnupg is already the newest version (2.2.19-3ubuntu2.1).
0 upgraded, 0 newly installed, 0 to remove and 46 not upgraded.
🤖: apt-get install -y gnupg-agent
Reading package lists...
Building dependency tree...
Reading state information...
The following NEW packages will be installed:
gnupg-agent
0 upgraded, 1 newly installed, 0 to remove and 46 not upgraded.
Need to get 5232 B of archives.
After this operation, 46.1 kB of additional disk space will be used.
Get:1 http://azure.archive.ubuntu.com/ubuntu focal-updates/universe amd64 gnupg-agent all 2.2.19-3ubuntu2.1 [5232 B]
Fetched 5232 B in 0s (307 kB/s)
Selecting previously unselected package gnupg-agent.
(Reading database ...
(Reading database ... 5%
(Reading database ... 10%
(Reading database ... 15%
(Reading database ... 20%
(Reading database ... 25%
(Reading database ... 30%
(Reading database ... 35%
(Reading database ... 40%
(Reading database ... 45%
(Reading database ... 50%
(Reading database ... 55%
(Reading database ... 60%
(Reading database ... 65%
(Reading database ... 70%
(Reading database ... 75%
(Reading database ... 80%
(Reading database ... 85%
(Reading database ... 90%
(Reading database ... 95%
(Reading database ... 100%
(Reading database ... 257522 files and directories currently installed.)
Preparing to unpack .../gnupg-agent_2.2.19-3ubuntu2.1_all.deb ...
Unpacking gnupg-agent (2.2.19-3ubuntu2.1) ...
Setting up gnupg-agent (2.2.19-3ubuntu2.1) ...
🤖: Importing public key.
gpg: directory '/home/runner/.gnupg' created
gpg: keybox '/home/runner/.gnupg/pubring.kbx' created
gpg: /home/runner/.gnupg/trustdb.gpg: trustdb created
gpg: key E87A5B406112EA99: public key "Firstname Lastname <[email protected]>" imported
gpg: Total number processed: 1
gpg: imported: 1
🤖: Importing secret key.
gpg: key E87A5B406112EA99: "Firstname Lastname <[email protected]>" not changed
gpg: key E87A5B406112EA99: secret key imported
gpg: Total number processed: 1
gpg: unchanged: 1
gpg: secret keys read: 1
🤖: Trusting key
gpg: secret keys imported: 1
FPR
***
tru::1:1624145135:0:3:1:5
pub:-:3072:1:E87A5B406112EA99:1612564012:1675636012::-:::scESC::::::23::0:
fpr:::::::::***:
uid:-::::1612564012::A4D9E30251A36A2A2BB69466349DAE929D524E96::Firstname Lastname <[email protected]>::::::::::0:
sub:-:3072:1:4E7AE6865CCFDA65:1612564012:1675636012:::::e::::::23:
fpr:::::::::318C696DC37EF712BB40C8234E7AE6865CCFDA65:
# List of assigned trustvalues, created Sat Jun 19 23:25:35 2021 UTC
# (Use "gpg --import-ownertrust" to restore them)
gpg: inserting ownertrust of 6
Configuring git-crypt for automated unlock...
OK
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2023-02-05
🤖: listing keys
/home/runner/.gnupg/pubring.kbx
-------------------------------
pub rsa3072 2021-02-05 [SC] [expires: 2023-02-05]
***
uid [ultimate] Firstname Lastname <[email protected]>
sub rsa3072 2021-02-05 [E] [expires: 2023-02-05]
/home/runner/.gnupg/pubring.kbx
-------------------------------
sec rsa3072 2021-02-05 [SC] [expires: 2023-02-05]
***
uid [ultimate] Firstname Lastname <[email protected]>
ssb rsa3072 2021-02-05 [E] [expires: 2023-02-05]
# List of assigned trustvalues, created Sat Jun 19 23:25:35 2021 UTC
# (Use "gpg --import-ownertrust" to restore them)
***:6:
🤖: Unlocking
gpg: decryption failed: No secret key
git-crypt: GPG error: Failed to decrypt
I'm having the same issue after re-importing my secret key.
@agconti I was working on something similar today, and I got it to work. A few ideas that might help:
- You might try adding
--verbosewhen you restart gpg-agent, that gave me a few extra hints that helped with debugging - Instead of using
gpg-connect-agent "PRESET_PASSPHRASE...I used gpg-preset-passphrase. Some extra benefits to using it are that you can pipe the passphrase in stdin, and that you don't have to convert it to hex. - I used
echo "GET_PASSPHRASE --no-ask $GPG_KEYGRIP a a a" | gpg-connect-agentto confirm that the passphrase was cached after presetting it. - You might double-check and make sure that you are using the correct keygrip when presetting the passphrase, if you have the wrong keygrip that would cause a 'No secret key' error.
@somesocks thanks for the tips, I'll try them! 💖