Written by Jacek KwiecieĊ„
Android Developer
Published April 30, 2018

Securing data in your repository with git-crypt – an android example

Most of the projects are using some data that needs to be treated with extra care. Like client secrets, ids, API keys, or, for Android, necessary keystore file. This data is sometimes stored in the repository for convenience and sometimes kept out (gitignored) and passed between developers.

This post will present the way of encrypting selected data with git-crypt, allowing us to keep them safe, but also keep them in the repository.

Why bother?

Keeping all the files in the repository remove the hassle of storing them somewhere else and passing this knowledge between developers. It’s something done rather rarely and often requires digging between storages, when a new guy shows up in the team. But you can’t just have it all in the repository, it’s too dangerous!

Let’s take an example of an Android project. If the developer cared for security you won’t be able to take some non open source project and build it right away after you clone the repository. It will be probably missing keystore access information and it should miss the keystore file itself. There are several other informations that might have been kept away from the repository and are stored in some local-only files. Getting them in place requires onboarding from the developer that already has access and remembers the process, which can be the pain as you tend to forget things that you do for instance, once a year.

With git-crypt you’ll be able to say: Setup git-crypt. I’ll need your client-ID and email address and you’ll be ready to go.
This means, that having the access setup in place, all that new joiner will have to do it is simply git clone and nothing more.

How does it work?

Encryption isn’t anything new of course. git-crypt uses our beloved version control system to make the process almost transparent. Basically, you tell the git-crypt keep some files encrypted and when you push to the repository, they get encrypted on the fly. When you browse your remote git repository you can see the files, but the content will be entirely different. When you pull the repository, the process goes backwards. Selected files are decrypted and readable on your disk.

Once a new developer joins the project, he/she needs to setup the git-crypt and be added by someone already involved. I’ll describe the steps for both, a new joiner and an old member (I’ll call him admin for convenience). Instruction is for Mac users, but I’m sure it can be easily ported to any platform.

Get git-crypt

  1. Get a homebrew if you still don’t have it
  2. Install of encrypting selected data with git-crypt
  3. brew install git-crypt
    brew install gnupg

An admin – preparing your repo

  1. Enter your repo in the console
  2. Generate and commit a symmetric master key to automatically created .git-crypt folder.
  3. git-crypt init

  4. Add a copy of the master key, encrypted with your public GPG key (so that only you can decrypt it)
  5. git-crypt add-gpg-user --trusted your.email@domain.com

  6. Make sure that the files you want to encrypt are in the repository and are not gitignored
  7. Prepare the configuration file. Create .gitattributes file on the same level that files you want to encrypt
  8. Here is an example of the configuration for 2 files:

    # Files that are going to be encrypted
    release-keystore.jks filter=git-crypt diff=git-crypt
    # (I use gradle.properties to store credentials)
    gradle.properties filter=git-crypt diff=git-crypt
    # Making sure that .gitattributes is never encrypted. DON'T TOUCH THAT LINE AND ONE BELOW
    .gitattributes !filter !diff

  9. Add your changes to git. Don’t commit yet.
  10. git add .

  11. List files for encryption
  12. git-crypt status -e

    If you did a commit before it might fail and ask you to do git-crypt status -f first

  13. Commit and push!

  14. git commit
    git push

An admin – adding a new joiner

If you got the key-ID from the new joiner:

  1. Download new joiner public key
  2. gpg --keyserver hjttps://pgp.mit.edu --recv-key *new-joiner-key-ID*

  3. Add new joiner user
  4. git-crypt add-gpg-user --trusted newjoiner@email.com

If that fails you’ll need a ASCII version of public key from a new joiner.
Getting that is described in the new joiner section. This is how you add ASCII version of public key manually:

  1. Get a ASCII version of public key from new joiner and save it in some file
  2. Paste the whole thing you got from the previous command, with the —BEGIN…— and —END…— lines

  3. Import the key from file
  4. gpg --import /path/to/file

  5. Add new joiner user
  6. git-crypt add-gpg-user --trusted newjoiner@email.com

A new joiner

  1. Generate a GPG key pair
  2. gpg --gen-key
    Fill in the data you are be asked for.

  3. Get your hexadecimal key-ID
  4. gpg --list-keys

  5. Send your public key to the general server
  6. gpg --keyserver https://pgp.mit.edu/ --send-keys *your-key-ID*

  7. Send your key-ID and the email address you used for the key creation to the developer that will grant you rights

If you failed to send your public key to the general server the described method, below you’ll find a workaround. Just skip points 5 and 6 in a favor of the workaround.

Sending your public key to the general server manually

  1. Get your ASCII version of public key
  2. gpg --export --armor *your-key-ID*

  3. Submit ASCII version of public key manually on the web page.
  4. Paste the whole thing you got from the previous command, with the —BEGIN…— and —END…— lines

  5. Send your ASCII version of public key and the email address you used for the key creation to the developer that will grant you rights

Selecting more files for encryption later

There is a tight process you need to stick to while adding new files to encryption later. Unfortunately, if you have files that you wanna secure, on different levels of a project, you’ll have to create a separate .gitattributes file for each level

  1. Create .gitattributes file if necessary
  2. Add a line for selected file in .gitattributes file of the same level
  3. git add .
  4. git-crypt status -e or git-crypt status -f if you did the commit before
  5. git commit

Switching machines

If you happen to change your workstation and not wanna be treated as a new joiner (although it’s possible if there is still some other machine that will grant the access) you can export your keys and then import them on the new machine.

Here is how to export the keys:

gpg --export *your key-ID* > path/to/public/key/backup/file
gpg --export-secret-keys *your key-ID* > path/to/secret/key/backup/file

You can import these files later:

gpg --import path/to/public/key/backup/file
gpg --import path/to/secret/key/backup/file

Fail-safe

Having git-crypt doesn’t mean you’re free from making a backup somewhere. If your project happens to be in a situation that all the developers are gone, then after some time it’s getting new joiners, there will be nobody to grant the access to them. One of the ways would be exporting keys from one of the developers working on the project as the previous section described, keep them somewhere safe and then import to the new developer’s machine when he/she shows up.

Zipping up

At first sight, the setup might seem even more hassle that well-known procedures. It was to me. But then, keeping everything in repository was tempting, and if that becomes popular procedure it will be much easier to remember.

Written by Jacek KwiecieĊ„
Android Developer
Published April 30, 2018