GPG(GNU PG)를 이용해 git 커밋에 서명하는 방법

들어가며

깃헙GitHub에서 커밋을 읽다보면 다음 화면처럼 Verified라는 표시를 보신 적이 있을 겁니다. 이 표시는 왜 등장했고, 어떤 의미가 있는 걸까요? 이 글에서는 Verified 표시의 의미를 설명하고, 내 커밋에 Verified 표시를 나타내기 위한 방법을 소개합니다.

Github 커밋에서 볼 수 있는 Verified 표시
44BITS 소식과 클라우드 뉴스를 전해드립니다. 지금 5,000명 이상의 구독자와 함께 하고 있습니다 📮

GPG(GNU Privacy Guard)란?

GPG는 GNU Privacy Guard의 약어이자, 1991년 필립 R. 짐머만Philip R. Zimmermann이 PGP(Pretty Good Privacy)라는 전자 우편 암호화 도구를 기반으로 만든 소프트웨어입니다. GPG는 여러가지 컨셉의 암호를 지원합니다. 이 글은 맥OSmacOS를 기준으로 설명하며, 맥 OS의 gpg 프로그램은 기본으로 RSA 알고리즘을 사용합니다.

GPG 설치

맥OS에서 gpg 프로그램을 설치하는 가장 간단한 방법은 홈브류Homebrew를 사용하는 것입니다.

$ brew install gpg

GPG 키 생성

gpg 프로그램을 설치했다면 먼저 키를 생성해야 합니다. gpg 프로그램의 --gen-key 옵션을 이용해 생성 도구를 실행하면 다음과 같은 인터랙티브 도구가 실행됩니다.

$ gpg --gen-key
gpg (GnuPG) 2.2.27; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Note: Use "gpg --full-generate-key" for a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name: Test RealName
Email address: test@email.com
You selected this USER-ID:
    "Test RealName <test@email.com>"

Change (N)ame, (E)mail, or (O)kay/(Q)uit? O

사용할 정보를 입력한 후 Okay의 첫 글자인 O 를 입력하면, 생성될 키에서 사용할 암호를 요구하는 프롬프트가 나옵니다. 여기서 입력하는 암호는 해당 키를 사용하는 동안 지속적으로 필요하므로 잊거나 잃지 않도록 주의해야 합니다.

암호 입력 화면

키가 생성된 직후, 생성된 키의 정보가 출력됩니다.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key E15D651143742DAD marked as ultimately trusted
gpg: directory '/Users/jeyraof/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/Users/jeyraof/.gnupg/openpgp-revocs.d/85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD.rev'
public and secret key created and signed.

pub   rsa3072 2021-02-08 [SC] [expires: 2023-02-08]
      85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
uid                      Test RealName <test@email.com>
sub   rsa3072 2021-02-08 [E] [expires: 2023-02-08]

마지막 네 줄을 살펴보면, 생성된 키의 이름은 85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD 이고, rsa3072 암호화 알고리즘을 사용하여 만들어졌음을 알 수 있습니다.*

* 방금 보여드린 예시에서는 --gen-key 명령어를 통해 기본 옵션을 적용하여 키를 생성했지만, 가급적 --full-generate-key 명령어를 통해 추가 정보를 입력하여 더욱 견고한 키를 만드실 것을 권장합니다.

GPG 키를 이용해서 커밋에 서명하기

GPG 키를 이용해서 Git 커밋에 서명(Sign)을 하기 위해서는, git에 몇몇 설정을 추가해야 합니다. 다음 설정 중 emailname, signingkey 부분을 수정해서 입력하세요.

$ git config --global user.email test@email.com
$ git config --global user.name "Test RealName"
$ git config --global user.signingkey 85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
$ git config --global commit.gpgsign true
$ git config --global gpg.program gpg

혹은 ~/.gitconfig 에 해당 내용을 직접 적어도 됩니다. (위 명령어를 사용해도 결국은 ~/.gitconfig 파일에 내용이 저장됩니다.)

[user]
    name = Test RealName
    email = test@email.com
    signingkey = 85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
[commit]
    gpgsign = true
[gpg]
    program = gpg

설명을 위해 임시로 /tmp/tmp-sign 디렉터리를 만들고, 깃 저장소로 초기화하고, test.db라는 빈 파일 하나를 생성합니다.

$ mkdir /tmp/tmp-sign
$ cd /tmp/tmp-sign
/tmp/tmp-sign
$ git init
Initialized empty Git repository in /tmp/tmp-sign/.git/
/tmp/tmp-sign master
$ git branch -M main
/tmp/tmp-sign main
$ touch test.db
/tmp/tmp-sign main*
$ git st
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   test.db

이제 커밋을 만들기 위해 git commit 명령어를 입력해봅시다.

/tmp/tmp-sign main*
$ git commit -m "commit with sign"

그러면 앞서 생성한 키를 이용하기 위해 암호를 묻는 화면이 나타납니다.

커밋 생성 전 암호를 묻는 화면

이 화면에서 암호를 틀리지 않게 입력했다면 커밋이 잘 생성될 겁니다.

/tmp/tmp-sign main*
git commit -m "commit with sign"
[main (root-commit) 2033b2a] commit with sign
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test.db

커밋이 GPG 키를 이용해 서명됐는지 확인하기

git log 커맨드의 --show-signature 옵션을 이용하면 해당 커밋에 서명된 키의 정보를 볼 수 있습니다.

$ git log --show-signature -1
commit 2033b2aae40faa7096de1864d63f17878e010a7c (HEAD -> main)
gpg: Signature made 월  2/ 8 19:11:56 2021 KST
gpg:                using RSA key 85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
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 2023-02-08
gpg: Good signature from "Test RealName <test@email.com>" [ultimate]
Author: Test RealName <test@email.com>
Date:   Mon Feb 8 19:11:56 2021 +0900

    commit with sign

암호를 묻는 화면이 나타나지 않고 Sign failed가 발생한다면?

GPG 키에 암호가 걸려 있을 때 환경변수 GPG_TTY 를 지정해 주지 않으면, 암호를 묻는 화면이 출력되지 않아 Sign failed 오류와 함께 커밋이 생성되지 않을 수 있습니다. 이때는 다음 명령을 통해 GPG_TTY 를 설정하면 문제가 해결됩니다.

$ export GPG_TTY=$(tty)

깃허브(GitHub)에 서명 적용하기

이제 깃허브 저장소에 올라간 커밋에 정상적으로 서명이 보이는지 확인해 보겠습니다. 이미 존재하는 저장소에서 작업을 해도 상관 없지만, 여기서는 새 저장소를 생성하는 단계부터 보여드리겠습니다. 새 저장소는 다음 주소에 만들었습니다.

이 저장소의 리모트 uri 는 다음과 같습니다. git@github.com:jeyraof/gpg-sign-example.git

해당 저장소를 원격 저장소 origin으로 등록합니다.

/tmp/tmp-sign main
$ git remote add origin git@github.com:jeyraof/gpg-sign-example.git

git remote -v 명령을 통해 원격 저장소가 잘 설정됐는지 확인할 수 있습니다.

/tmp/tmp-sign main
$ git remote -v
origin  git@github.com:jeyraof/gpg-sign-example.git (fetch)
origin  git@github.com:jeyraof/gpg-sign-example.git (push)

깃허브(GitHub)로 푸시

앞선 과정을 똑같이 따라하셨다면, 현재 저장소에는 생성된 GPG를 이용해 서명된 커밋이 존재합니다. git log --show-signature -1 명령을 통해 확인할 수 있습니다.

/tmp/tmp-sign main
$ git log --show-signature -1
commit 2033b2aae40faa7096de1864d63f17878e010a7c (HEAD -> main, origin/main)
gpg: Signature made 월  2/ 8 19:11:56 2021 KST
gpg:                using RSA key 85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
gpg: Good signature from "Test RealName <test@email.com>" [ultimate]
Author: Test RealName <test@email.com>
Date:   Mon Feb 8 19:11:56 2021 +0900

    commit with sign

이제 이 커밋을 원격 저장소로 푸시합니다.

/tmp/tmp-sign main
$ git push origin main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 731 bytes | 731.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:jeyraof/gpg-sign-example.git
 * [new branch]      main -> main

정상적으로 푸시가 되었습니다. 이제 깃허브에 가서 커밋을 확인합니다.

Unverified 상태의 커밋

커밋은 잘 올라갔지만, Unverified 상태로 나타납니다. 이 버튼을 누르면 상세한 정보를 확인할 수 있습니다.

커밋 서명 상태에 대한 자세한 설명

이제 Unverified 문제를 해결할 차례입니다.

깃허브 계정에 이메일 등록하기

앞선 과정에서 GPG 키를 생성할 때 사용한 이메일은 test@email.com입니다. Unverified 메시지는 이 test@email.com 이메일이 누구의 계정에 할당되었는지 또는 서명된 커밋signed commit을 푸시한 사람의 이메일이 정말로 test@email.com 인지 알 수 없는 상태일 때 나타납니다. 따라서 test@email.com 이메일을 깃허브 설정 페이지에서 등록해야 합니다.

깃허브에 이메일을 등록합니다
등록된 이메일 목록

깃허브 계정에 GPG 키 등록하기

이제 test@email.com 이메일을 사용할 GPG 키의 공개키를 깃허브에 등록할 차례입니다. SSH and GPG keys 설정 페이지에서 New GPG key 를 눌러 등록 페이지로 이동합니다.

등록 페이지에서는 BEGIN PGP PUBLIC KEY BLOCK 을 포함한 공개키를 요구합니다.

새 GPG 키를 등록합니다

생성했던 GPG 키를 다시 한 번 확인해 봅시다.

$ gpg --list-secret-keys
/Users/jeyraof.gnupg/pubring.kbx
---------------------------------
sec   rsa3072 2021-02-08 [SC] [expires: 2023-02-08]
      85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
uid           [ultimate] Test RealName <test@email.com>
ssb   rsa3072 2021-02-08 [E] [expires: 2023-02-08]

이 키에서 공개키를 추출해야 합니다. 추출 방법은 다음과 같습니다:

gpg --armor --export 85B5FA0A8A67AA1A20FA7D4BE15D651143742DAD
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
gCrQ7YWLfhewIewqujWAeWR/9j7gbg==
=Lu+o
-----END PGP PUBLIC KEY BLOCK-----

만약 사용 중인 GPG 키 중 해당 이메일에 할당된 키가 하나 밖에 없는 경우에는 다음과 같이 이메일을 입력하여 추출할 수도 있습니다. gpg --armor --export test@email.com

추출된 공개키를 ---BEGIN 부터 ---END 까지 복사해 깃허브의 GPG 키 등록 화면에 붙여넣습니다.

GPG 키를 깃허브(GitHub)에 등록합니다.

별 문제가 없었다면 다음과 같은 모습을 확인할 수 있습니다.

정상적으로 등록되면 다음과 같이 확인할 수 있습니다

이제 깃허브 저장소로 돌아가 커밋을 확인해 보면, Unverified 가 Verified 상태로 바뀐 것을 확인할 수 있습니다.

커밋이 Verified 된 것을 확인할 수 있습니다.

만약 이메일과 GPG 키를 등록했음에도 불구하고 Verified 상태로 바뀌지 않는 경우, 다음과 같이 커밋을 다시 작성해서 올려보시길 권합니다.

$ git commit --amend --reset-author
$ git push origin main --force

주의사항

해당 저장소는 실재하지 않는 이메일 test@email.com 을 이용해 작성하여 정상적으로 이메일 인증을 받을 수 없기 때문에, 실제 동작은 작성자의 실제 이메일 jaeyoung@monodiary.net 을 이용해 수행한 스크린샷을 포함하고 있습니다. 설정 방법 및 절차는 다르지 않으니 참고 바랍니다.

레퍼런스