# GPG подпись

<!-- https://www.youtube.com/watch?v=2CwsoGw2coc -->
<!-- https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key -->

GPG ключи по сравнению с SSH ключами имеют более развитый функционал.
- настройка специализации ключа (шифрование, подпись, авторизация, создание подключей);
- отзыв ранее выпущенных ключей;
- настройка срока действия ключей с возможностью продления в будущем;
- привязка ключей к имени пользователя и почте;
- возможность загрузки публичных ключей на сервера ключей;
- возможность хранения секретных ключей на аппаратном ключе который поддерживает стандарт OpenPGP Card (например Yubikey)

Под Windows утилита gpg идет вместе с git. Так что можно работать сразу после установки git.

Все свои файлы gpg хранит в `~/.gnupg/`

Полезно добавить в конфиг:
```
cat << --- >> ~/.gnupg/gpg.conf
keyid-format LONG
with-fingerprint
no-greeting
armor
---
```

Полезные команды:
```
gpg -k --keyid-format=long                      # Посмотреть публичные ключи
gpg -K --keyid-format=long                      # Посмотреть приватные ключи
gpg --full-generate-key                         # Генерация пары ключей
gpg -a --export <key-id>                        # Экспорт публичного ключа
gpg -a --export-secret-keys <key-id>            # Экспорт секретного ключа
gpg --import <file>                             # Импорт ключа
gpg --delete-key <key-id>                       # Удаление публичного ключа
gpg --delete-secrete-key <key-id>               # Удаление секретного ключа
git config --global user.signingkey <key-id>
```

Обозначения типов ключей:
```
sec - secret primary key (секретный основной ключ)
pub - public primary key (публичный основной ключ)
ssb - secret sub-key (секретный сабключ)
sub - public sub-key (публичный сабключ)
> - ключ есть на карте
# - нет секретного ключа, только заглушка для сабключа
```

## Генерация ключей

Создаем основной (primary) ключ:
```
gpg --expert --full-generate-key
```

Выбираем один из вариантов:
```
RSA (sign only)                     # создание только основного ключа c правами [SC]
RSA (set your own capabilities)     # создание только основного ключа c настраиваемыми правами
```
Этот ключ нужен для генерации других сабключей.

## Генерация сабключей

Для генерации сабключей заходим в консоль gpg:
```
gpg --expert --edit-key [id]
> addkey
...
> save
```
`[id]` - здесь и далее в качестве id можно использовать id ключа, имя или почту пользователя.  
`?` - список все комманд  
`addkey` - добавить новый сабключ  
`change-usage` - поменять флаги usage  
`save` - не забываем сохранить результаты перед выходом

## Экспорт ключей

Прежде удалять мастер ключ или переносить сабключи на карту, необходимо сделать бекап (экспорт) всей нашей работы.

```
gpg -a --export-secret-key [id] > master.key            # Экспорт мастер ключа (вместе с сабключами)
gpg -a --export-secret-subkeys [id] > subkeys.key       # Экспорт сабключей (не обязательно)
gpg -a --export [id] > public.key                       # Экспорт общего публичного ключа
```

## Сохранение на аппаратный ключ
<!-- https://fido.ftsafe.com/openpgp/ -->
<!-- https://rgoulter.com/blog/posts/programming/2022-06-10-a-visual-explanation-of-gpg-subkeys.html -->

```
gpg --card-status               # посмотреть информацию по карте
gpg --edit-key [id]             # входим в режим редактирования
> key 1                         # выбираем первый сабключ
> keytocard                     # переносим его на карту
> key 1                         # снимаем старый выбор
> key 2                         # выбираем следующий сабключ
> keytocard                     # переносим его на карту
> key 2                         # снимаем старый выбор
> key 3                         # выбираем следующий сабключ
> keytocard                     # переносим его на карту
> key 3                         # снимаем старый выбор
> save                          # сохраняем (локальные ключи сотрутся!)
```

## Удаление секретных ключей

Удаляем секретные ключи:
```
gpg --delete-secret-keys [master-key-id]
```
Для того чтобы gpg понял что секретные ключи находятся на карте выполняем:
```
gpg --card-status
```

Для использования карты на новом компьютере достаточно импортировать public ключ и запустить `gpg --card-status`.

## Загрузка public ключа на сервер ключей

Существует огромное кол-во серверов ключей, для примера будем использовать:

https://keys.openpgp.org/

Ключи лучше загружать через web-интерфес чтобы получить ссылку для верификации почты.

После того как public ключ загружен на сервер, можно добавить URL ключа в настройки карты. Тогда будет возможно настраивать новый компьютер совсем просто:
```
gpg --card-edit			# Заходим в настройки карты
> fetch                 # Скачиваем public ключ
> quit                  # Выходим
gpg --edit-key [id]     # Заходим в редактирование
> trust                 # Редактируем trust (по умолчанию она сброшена в "unknown")
> 5                     # Савим "ultimate" (Это наш собственный ключ, доверяем.)
> save                  # Сохраняем
```
И сразу можно подписывать коммиты :)

## Настройка подписи коммитов в git

Подписать последний коммит `git commit --amend --no-edit -S`

Также не лишним будет добавить удобные элиасы в .ginconfig:
```
sign = commit --amend --no-edit -S
unsign = commit --amend --no-edit
signoff = commit --amend --no-edit -s
```

Для подписи коммитов из WSL полезно добавить элиас который использует git из windows
```
git config --global alias.sign '!git -c gpg.program="/mnt/c/Program Files/Git/usr/bin/gpg.exe" commit --amend --no-edit -S'
```

## Настройка чистой системы для подписи аппаратным ключом

Если есть аппаратный ключ и нужно настроить подпись коммитов на чистой системе,
то можно просто импортировать публичные ключи с карты.
Предполагается что `.gitconfig` настроен правильно.

Предварительно можно очистить все настройки `gpg` удалив папку `~/.gpgconf`

Если удалить не получается то надо убить процессы:
```
gpgconf --kill all
```

Импортируем ключи с карты:
```
gpg --card-edit
> fetch
> quit
```

Опционально устанавливаем ключу максимальный уровень доверия:
```
gpg --edit-key [id]
> trust
5
> quit
```

## Продление ключей

Короткий вариант продлить сразу все ключи:
```
gpg --import ~/master.key                                       # временно импортируем мастер ключ
gpg --quick-set-expire [master-key-fingerprint] 1y '*'          # продляем на 1 год с текущей даты
gpg --quick-set-expire [master-key-fingerprint] 2025-12-31 '*'  # или указываем конкретную дату
gpg --delete-secret-keys [master-key-id]                        # удаляем секретные ключи
gpg --keyserver keys.openpgp.org --send-keys [master-key-id]    # обновляем на сервере
gpg -a --export [master-key-id] > public.key                    # экспорт публичных ключей
# обновляем public.key на всех серверах git
```

Или вручную по одному:
```
gpg --import ~/master.key       # временно импортируем мастер ключ
gpg --edit-key [master-key-id]
> key 1                         # выбираем первый сабключ
> expire                        # редактируем срок действия
> 1y                            # 1 год например
> key 1                         # снимаем старый выбор
> key 2                         # выбираем следующий сабключ
> expire                        # редактируем срок действия
> 1y                            # 1 год например
> key 2                         # снимаем старый выбор
> key 3                         # выбираем следующий сабключ
> expire                        # редактируем срок действия
> 1y                            # 1 год например
> key 3                         # снимаем старый выбор
> save                          # сохраняем (локальные ключи сотрутся!)
gpg --delete-secret-keys [master-key-id]                        # удаляем секретные ключи
gpg --keyserver keys.openpgp.org --send-keys [master-key-id]    # обновляем на сервере
gpg -a --export [master-key-id] > public.key                    # экспорт публичных ключей
# обновляем public.key на всех серверах git
```
Обновлять приватные ключи на аппаратных ключах (Yubikey) не нужно, т.к. там храниться только криптографическая часть, без метаданных.