Началось все с того что я почитав статью Аппаратный ключ шифрования за 3$ — возможно ли это? решил запилить такую штуку. В итоге stlink приехал со второго раза. Первый потерялся в недрах почты.


После приезда я выяснил что там стоит чип cks и по второй статье залил прошивку. В системе ключ определился


[612524.102634] usb 4-5: new full-speed USB device number 26 using ohci-pci
[612524.264962] usb 4-5: New USB device found, idVendor=234b, idProduct=0000, bcdDevice= 2.00
[612524.264969] usb 4-5: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[612524.264973] usb 4-5: Product: Gnuk Token
[612524.264976] usb 4-5: Manufacturer: Free Software Initiative of Japan
[612524.264979] usb 4-5: SerialNumber: FSIJ-1.0.4-9A760301

Но после этого начались проблемы.


Проблема с доступом к ключу


gpg не видел ключ. Точнее видел его, но только под root. Последовало долгое изучение проблемы и гугления. В итоге выяснилось


  • У меня банально нет правила для пользовательского доступа к устройству
  • Доступ к карте может осуществляться более чем одним методом из gpg

Первая проблема решается через добавление файла /etc/udev/rules.d/60-gnuk.rules


SUBSYSTEMS=="usb", ATTRS{idVendor}=="234b", ATTRS{idProduct}=="0000",   ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg"

И перезагрузку udev


devadm control --reload-rules

Это больше касается тех у кого не debian. В нем и его производных этой проблемы нет, но у меня fedora. После этого ключ увиделся.


Вторая проблема может быть решена двумя способами


  • Отключить сервис pcscd
  • Настроить использование pcscd сервиса в gnupg

Отключить сервис можно через его удаление или же банально остановив сервис


systemctl stop pcscd
systemctl disable pcscd

Второй вариант настраиваем сервис gnupg на использование pcscd


touch ~/.gnupg/scdaemon.conf
echo -e "pcsc-driver /usr/lib64/libpcsclite.so.1\ndisable-ccid]\n" > ~/.gnupg/scdaemon.conf

В этом случае родной scdaemon из gnupg ходит в ключ через pcscd.


Отлично карта доступна можно же залить ключ как описано и вперед! А вот и нет. Ну вообще да оно работает как описано в первой статье. Но есть нюанс.


Проблема с выбором ключей на самом ключе


Формулировка конечно великолепна, но поясню суть проблемы. Авторы предыдущих статей упускают следующий момент


gpg --card-status
Reader ...........: 234B:0000:FSIJ-1.0.4-9A760301:0
Application ID ...: D276000124010200FFFE9A7603010000
Application type .: OpenPGP
Version ..........: 2.0
Manufacturer .....: unmanaged S/N range
Serial number ....: 9A760301
Name of cardholder: [не установлено]
Language prefs ...: [не установлено]
Salutation .......: 
URL of public key : [не установлено]
Login data .......: [не установлено]
Signature PIN ....: требуется
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

На самом деле ключ содержит 3 ключа. Один для подписи, который как раз и грузили. Но тут есть момент. При таком вызове


$ ssh -I /usr/lib/opensc-pkcs11.so martin@remotehost

  • У вас должен быть включен pcscd. Если он не включен не будет доступа к ключу.
  • При этом сначала идет обращение к Authentication key. А уже потом если облом, то идет обращение к Signature key. Что приводит к двойному запросу pin. Не очень то удобно.

Но тут опять есть два пути, но оба подразумевают добавление Authentication key


  • Использовать gpg-agent совместно с ssh
  • Использовать pcscd как указано

Но сначала стоит решить проблему с пустым Authentication key.


Мне помогла вот эта статья


Она рассказывает кстати где взять pem2openpgp он входит в состав monkeysphere, что отдельно прекрасно в Fedora пакет дропнули в 31 версии. Ладно из 30 версии пакет завелся.


Далее надо создать нормальный pgp ключ. Например так:


$ gpg --quick-gen-key "Niibe Yutaka <gniibe@fsij.org>"
About to create a key for:
    "Niibe Yutaka <gniibe@fsij.org>"

Continue? (Y/n) y

Пример честно сперт из официальной документации gnuk.


Далее сконвертировать ssh ключ и добавить его в gpg


pem2openpgp temporary_id < .ssh/my_fancy_key  | gpg2 --import
gpg: key 66091F2C70AF02A9: public key "temporary_id" imported
gpg: key 66091F2C70AF02A9: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

А далее добавить этот ключ к ранее созданному ключу.
Для этого смотрим список ключей


gpg2 -K --with-keygrip  
/home/bexelbie/.gnupg/pubring.kbx
--------------------------------
sec   rsa2048 2019-03-21 [SC] [expires: 2021-03-20]
      96F33EA7F4E0F7051D75FC208715AF32191DB135
      Keygrip = 90E08830BC1AAD225E657AD4FBE638B3D8E50C9E
uid           [ unknown] Niibe Yutaka <gniibe@fsij.org>
ssb   rsa2048 2019-03-21 [E] [expires: 2021-03-20]
      Keygrip = 5FA04ABEBFBC5089E50EDEB43198B4895BCA2136

sec   rsa2048 2019-03-23 [C]
      D4F6B35B52B96A092FB8F418A41A06197749FBA4
      Keygrip = 1F824257B107D9E3371B9A4957751D78FC8BB190
uid           [ unknown] temporary_id

Редактируем созданный ключ


gpg2 --expert --edit-key 96F33EA7F4E0F7051D75FC208715AF32191DB135
gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 13
Enter the keygrip: 1F824257B107D9E3371B9A4957751D78FC8BB190

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? s
Your selection? e
Your selection? a

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q
Please specify how long the key should be valid.
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y

sec  rsa2048/8715AF32191DB135
     created: 2019-03-21  expires: 2021-03-20  usage: SC  
     trust: unknown       validity: unknown
ssb  rsa2048/150F16909B9AA603
     created: 2019-03-21  expires: 2021-03-20  usage: E  
ssb  rsa2048/4A9EE7790817C411
     created: 2019-03-23  expires: never       usage: A  
[ unknown] (1). Niibe Yutaka <gniibe@fsij.org>

gpg> quit
Save changes? (y/N) y

Указываем в качестве добавляемого ключа импортированный ssh ключ. При добавлении выключаем подписывание и шифрование, а аутентификацию включаем. В итоге получаем ключ содержащий наш ssh ключ как пригодный для аутентификации.


Теперь осталось совсем немного. Перенести все это в аппаратный ключ. Снова запускаем редактирование ключа


gpg2 --expert --edit-key 96F33EA7F4E0F7051D75FC208715AF32191DB135

Вызываем запись ключа


gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1

Замечу, что если просто писать ssh ключ как есть то пункта 3 не будет.


После записи выбираем ключ 1


gpg> key 1
sec  ed25519/E267B052364F028D
     created: 2015-08-12  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb* cv25519/850AF040D619F240
     created: 2015-08-12  expires: never       usage: E
ssb  ed25519/5F910521FAA805B1
     created: 2015-08-12  expires: never       usage: A
[ultimate] (1). NIIBE Yutaka <gniibe@fsij.org>
[ultimate] (2)  NIIBE Yutaka <gniibe@debian.org>

При этом выделится ключ используемый для шифрования. Опять выполняем


gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2

Как видим выбор есть только как ключ шифрования. Если он вам не нужен, то пункт можно пропустить


Ну и наконец импортированный ssh ключ. Для начала убираем выделение с ключа 1


gpg> key 1
sec  ed25519/E267B052364F028D
     created: 2015-08-12  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/850AF040D619F240
     created: 2015-08-12  expires: never       usage: E
ssb  ed25519/5F910521FAA805B1
     created: 2015-08-12  expires: never       usage: A
[ultimate] (1). NIIBE Yutaka <gniibe@fsij.org>
[ultimate] (2)  NIIBE Yutaka <gniibe@debian.org>

Выбираем ключ 2


gpg> key 2
sec  ed25519/E267B052364F028D
     created: 2015-08-12  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  cv25519/850AF040D619F240
     created: 2015-08-12  expires: never       usage: E
*ssb  ed25519/5F910521FAA805B1
     created: 2015-08-12  expires: never       usage: A
[ultimate] (1). NIIBE Yutaka <gniibe@fsij.org>
[ultimate] (2)  NIIBE Yutaka <gniibe@debian.org>

Записываем ключ


gpg> keytocard
Please select where to store the key:
   (3) Authentication key
Your selection? 3

Далее если вы хотите сохранить ключи только на карте делаем


gpg> save

Иначе же, просто не сохраняем изменения.


gpg> quit
Save changes? (y/N) n
Quit without saving? (y/N) y

В этом случае ключи не удалятся из локального хранилища и можно сделать бекап.


В итоге получим добавленные на ключ три ключа. И ssh ключ будет занесен не как ключ подписи, а как ключ аутентификации


Reader ...........: 234B:0000:FSIJ-1.2.0-87193059:0
Application ID ...: D276000124010200FFFE871930590000
Version ..........: 2.0
Manufacturer .....: unmanaged S/N range
Serial number ....: 87193059
Name of cardholder: Yutaka Niibe
Language prefs ...: ja
Sex ..............: male
URL of public key : http://www.gniibe.org/gniibe-20150813.asc
Login data .......: gniibe
Signature PIN ....: not forced
Key attributes ...: ed25519 cv25519 ed25519
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 0
Signature key ....: 249C B377 1750 745D 5CDD  323C E267 B052 364F 028D
      created ....: 2015-08-12 07:10:48
Encryption key....: E228 AB42 0F73 3B1D 712D  E50C 850A F040 D619 F240
      created ....: 2015-08-12 07:10:48
Authentication key: E63F 31E6 F203 20B5 D796  D266 5F91 0521 FAA8 05B1
      created ....: 2015-08-12 07:16:14
General key info..: pub  ed25519/E267B052364F028D 2015-08-12 NIIBE Yutaka <gniibe@fsij.org>
sec>  ed25519/E267B052364F028D  created: 2015-08-12  expires: never
                                card-no: FFFE 87193059
ssb>  cv25519/850AF040D619F240  created: 2015-08-12  expires: never
                                card-no: FFFE 87193059
ssb>  ed25519/5F910521FAA805B1  created: 2015-08-12  expires: never
                                card-no: FFFE 87193059

Это конечно здорово, но как это все использовать? Тут есть два пути


  • Использовать ключ через gpg-agent
  • Использовать через pcscd

Использование ключа через gpg-agent


В этом случае gpg-agent прикидывается ssh-agent и транслирует ключи аутентификации из gpg в ssh. Плюсы в том, что он не требует на каждый вход вводить pin, ну и более логично вписывается в ssh.


Для включения режима в ~/.gnupg/gpg.conf добавляем


use-agent

Создаем файл ~/.gnupg/gpg-agent.conf следующего содержания


default-cache-ttl 600
max-cache-ttl 7200
enable-ssh-support

Это включает кеширование и поддержку ssh.


Добавляем в ~/.bashrc


export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent

Если хотим попробовать запускаем те же команды в локальном shell или просто открываем новый терминал/сессию. Если все сделали хорошо то


ssh-add -l
2048 SHA256:4v+TWsgZkr509VHQyJw9vnRcDb5HmcQwcdo5kMloi/s cardno:FFFE9A760301 (RSA)

Покажет наличие ключа на карте.


Далее запускаем как обычно ssh -v куда-нибудь и увидим


debug1: Will attempt key: cardno:FFFE9A760301 RSA SHA256:4v+TWsgZkr509VHQyJw9vnRcDb5HmcQwcdo5kMloi/s agent

Это говорит что ключ запрашивается с карты. Обратите внимание что пин будет запрашиваться не постоянно, а только когда истечет кеш в gpg-agent. Но если ключ вынут и вставлен по новой то кеш сбросится и при первом входе потребуется ввести его снова.


Использование ключа через pcks#11


Тут все просто. Первое проверяем что демон pcscd запущен


systemctl status pcscd.service
? pcscd.service - PC/SC Smart Card Daemon
     Loaded: loaded (/usr/lib/systemd/system/pcscd.service; indirect; vendor preset: disabled)
     Active: active (running) since Wed 2020-06-17 10:30:43 +05; 3s ago

Далее делаем как и приводилось до этого


ssh -I /usr/lib/opensc-pkcs11.so martin@remotehost

PIN при этом будет запрашиваться каждый раз, но зато один раз, а не два :)
И да на fedora команда меняется на такую


ssh -I /usr/lib64/opensc-pkcs11.so martin@remotehost

С моей точки зрения первый метод все же лучше, так-как предоставляет больше удобства и карта и так изменяется из gpg. Но если планируется так же организовать вход через токен, то надо использовать прослойку через pkcs.