# What is git-crypt

git-crypt enables transparent encryption and decryption of files in a git repository. Files which you choose to protect are encrypted when committed, and decrypted when checked out. git-crypt lets you freely share a repository containing a mix of public and private content. git-crypt gracefully degrades, so developers without the secret key can still clone and commit to a repository with encrypted files. This lets you store your secret material (such as keys or passwords) in the same repository as your code, without requiring you to lock down your entire repository.

This post explain how to use it.

# Generate a GPG key

To crypt and decrypt content using git-crypt, we need to a gpg key. So let generate a key.

# Create a gpg config file

create a file gpg-config with the following content

%echo Generating a basic OpenPGP key
Key-Type: default
Subkey-Type: default
Name-Real: My Name
Name-Email: myemail@domain.sub
# Do a commit here, so that we can later print "done" :-)
%commit
%echo done

You can change My Name with your real name and myemail@domain.sub by your email.

# Generate the key

run the following command, we will prompted to enter the key paraphrase.

$ gpg --batch --gen-key gpg-config
gpg: Generating a basic OpenPGP key
gpg: key D5B2C360EA7A893F marked as ultimately trusted
gpg: revocation certificate stored as '/home/******/.gnupg/openpgp-revocs.d/05AC305410E66882237F4991D5B2C360EA7A893F.rev'
gpg: done

You should have a new key

$ gpg --list-secret-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: next trustdb check due at 2022-05-31
/home/******/.gnupg/pubring.kbx
---------------------------------
...
pub   rsa3072 2020-06-01 [SC]
      F6241BADF89F0BA297359D7F5A2954A9F2CAA211
uid           [ultimate] My Name <myemail@domain.sub>
sub   rsa3072 2020-06-01 [E]

# Install git-crypt

# Installing on Mac OS X

Using the brew package manager, simply run:

$ brew install git-crypt

# Installing on ubuntu

Using the apt or apt-get package manager, simply run:

$ sudo apt install git-crypt

# Use git-crypt

To demonstrate the usage of git-crypt we will

  • create a new local git repository
  • configure git-crypt
  • add a secret
  • push modification
  • clone a second version
    • show that the secret is encrypted
    • decrypt the secret

# Initialize a git repo with git-crypt

Let init a new git repository

$ mkdir /tmp/copy1 && cd /tmp/copy1
$ git init

Add an empty .gitattributes file and commit it.

$ touch .gitattributes
$ git add .gitattributes
$ git commit -m "first commit" 

We have a secret file to add, we don't want this secret to be readable by everyone having access to this repository, but we want to share and historise this secret with a group of special users. To do that we can use git-crypt.

Here steps to activate git-crypt into this repository

git-crypt init

Tell git-crypt to use the gpg key created above to encrypt secret.

if you changed myemail@domain.sub with your own email, so don't forget to use it here.

$ git-crypt add-gpg-user --trusted myemail@domain.sub
[master f7051b5] Add 1 git-crypt collaborator
 2 files changed, 4 insertions(+)
 create mode 100644 .git-crypt/.gitattributes
 create mode 100644 .git-crypt/keys/default/0/F6241BADF89F0BA297359D7F5A2954A9F2CAA211.gpg

We can see that the git-crypt add-gpg-user command has made an automatic commit.

$ git log --oneline 
547458e (HEAD -> master) Add 1 git-crypt collaborator
c8b6e50 first commit

You should have this tree into the current folder

$ tree -a .
.
├── .git
...
...
│   ├── git-crypt
│   │   └── keys
│   │       └── default
...
...
├── .gitattributes
└── .git-crypt
    ├── .gitattributes
    └── keys
        └── default
            └── 0
                └── F6241BADF89F0BA297359D7F5A2954A9F2CAA211.gpg

Tell git-crypt to decrypt encrypted files.

At this step there are nothing to decrypt, but we need to unlock to be able to add clear version of secrets.

git-crypt unlock

Create a new secret file.

$ echo "secret" > mysecret.txt

Tell git-crypt that mysecret.txt have to be encrypted

echo "mysecret.txt filter=git-crypt diff=git-crypt" >> .gitattributes

Check that git-crypt take on consideration the content of .gitattributes

$ git-crypt status
    encrypted: mysecret.txt
not encrypted: .git-crypt/.gitattributes
not encrypted: .git-crypt/keys/default/0/F6241BADF89F0BA297359D7F5A2954A9F2CAA211.gpg
not encrypted: .gitattributes

Check the content of the mysecret.txt file

$ cat mysecret.txt 
secret

It still readable this because of the git-crypt unlock.

Let switch the repository to the encrypted version.

$ git-crypt lock

Check the content of the mysecret.txt file

$ cat mysecret.txt 
## some binary output, the encrypted version

To develop and use the repository we have to unlock the repository.

$ git-crypt unlock

Add and commit those changes

git add .gitattributes mysecret.txt
git commit -m "add my first secret"

# Add a second special user to the git repo with git-crypt

To share this git repository, and the secret part with an other user.

  1. You can share your gpg key.

To do that you can export the key

$ git-crypt unlock
$ git-crypt export-key /tmp/mykeyfile

Share the /tmp/mykeyfile file with the other user. Using the following command it can decrypt secret.

$ git-crypt unlock /tmp/mykeyfile

But this is a poor method and it's bad practices to use the same key for all users. Let try a method with one key by user.

create a file gpg-config2 with the following content

%echo Generating a basic OpenPGP key
Key-Type: default
Subkey-Type: default
Name-Real: Second User
Name-Email: seconduser@domain.sub
# Do a commit here, so that we can later print "done" :-)
%commit
%echo done

and generate a new gpg key.

$ gpg --batch --gen-key gpg-config2 
gpg: Generating a basic OpenPGP key
gpg: key 4D304D45B2F73BF4 marked as ultimately trusted
gpg: revocation certificate stored as '/home/******/.gnupg/openpgp-revocs.d/3E4A037337BE1CB34A3D6CF44D304D45B2F73BF4.rev'
gpg: done

Export the public key of the gpg key pair.

$ gpg --output /tmp/seconduser.gpg --armor --export seconduser@domain.sub
$ cat /tmp/seconduser.gpg
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQGNBF7U0AYBDADM1KmXY/zc3Y0fJifWKzsCVSsaJ2hMNQ1Fh2iZ0TY33xU/z6Fs
...
...
2A3NEtBDWMbPEqVJJzkh
=FKXx
-----END PGP PUBLIC KEY BLOCK-----

Share the seconduser.gpg with the first user (the crypt admin).

To simulate what will happen into the first user side, we will remove the seconduser@domain.sub from our gpg key store using the following command.

# delete the `seconduser@domain.sub` secret
$ gpg --list-secret | grep -B1 "seconduser@domain.sub" | grep -v uid | tr -d "[:blank:]" | xargs -I{} gpg --batch --delete-secret-keys {}

# delete the `seconduser@domain.sub` key
$ gpg --list-keys | grep -B1 "seconduser@domain.sub" | grep -v uid | tr -d "[:blank:]" | xargs -I{} gpg --batch --delete-key {}

$ gpg --list-keys | grep "seconduser@domain.sub"
# empty result

The first user have to import the public key of the second user

$ gpg --import /tmp/seconduser.gpg
gpg: key 4D304D45B2F73BF4: public key "Second User <seconduser@domain.sub>" imported
gpg: key 8800324F3C0519E8: public key "Second User <seconduser@domain.sub>" imported
gpg: Total number processed: 2
gpg:               imported: 2
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: depth: 1  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
gpg: next trustdb check due at 2022-05-31

The new key should be listed into GPG keys. But this key is marked as unknown

$ gpg --list-keys 
/home/******/.gnupg/pubring.kbx
---------------------------------
pub   rsa3072 2020-06-01 [SC]
      F6241BADF89F0BA297359D7F5A2954A9F2CAA211
uid           [ultimate] My Name <myemail@domain.sub>
sub   rsa3072 2020-06-01 [E]

pub   rsa3072 2020-06-01 [SC]
      9C465BFA9B3DE508E036EF888800324F3C0519E8
uid           [ unknown] Second User <seconduser@domain.sub>
sub   rsa3072 2020-06-01 [E]

To make this key trusted the first user have to

$ gpg --edit-key seconduser@domain.sub
# At the gpg> prompt
# Enter sign
# Enter save (which should exit the prompt)

you should have full instead of unknown

$ gpg --list-keys 
/home/melhabib/.gnupg/pubring.kbx
---------------------------------
pub   rsa3072 2020-06-01 [SC]
      F6241BADF89F0BA297359D7F5A2954A9F2CAA211
uid           [ultimate] My Name <myemail@domain.sub>
sub   rsa3072 2020-06-01 [E]

pub   rsa3072 2020-06-01 [SC]
      9C465BFA9B3DE508E036EF888800324F3C0519E8
uid           [  full  ] Second User <seconduser@domain.sub>
sub   rsa3072 2020-06-01 [E]
$ cd /tmp/copy1
$ git-crypt unlock
$ git-crypt add-gpg-user --trusted seconduser@domain.sub
[master c4346de] Add 1 git-crypt collaborator
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 .git-crypt/keys/default/0/9C465BFA9B3DE508E036EF888800324F3C0519E8.gpg
  • The first user have to Push auto-generated commit up to Git host
  • The second user have to
    • pull down the repository from Git host
    • git-crypt unlock And files will be decrypted

# Clean GPG

If you don't want to keep test GPG key you can delete them using the following commands.

$ gpg --list-secret | grep -B1 "myemail@domain.sub" | grep -v uid | tr -d "[:blank:]" | xargs -I{} gpg --batch --delete-secret-keys {}
$ gpg --list-keys | grep -B1 "myemail@domain.sub" | grep -v uid | tr -d "[:blank:]" | xargs -I{} gpg --batch --delete-key {}

$ gpg --list-secret | grep -B1 "seconduser@domain.sub" | grep -v uid | tr -d "[:blank:]" | xargs -I{} gpg --batch --delete-secret-keys {}
$ gpg --list-keys | grep -B1 "seconduser@domain.sub" | grep -v uid | tr -d "[:blank:]" | xargs -I{} gpg --batch --delete-key {}