News

Das Neuste aus der Welt von Adfinis SyGroup

Multi-Factor Authentication

17. July 2016

Heutzutage sind Hacker-Attacken keine Seltenheit mehr. Um so wichtiger ist es, Daten gut zu schützen ohne den Aufwand für Datenzugriff massiv zu erhöhen.

Security sollte für alle (Private und Firmen) ein Thema sein. Ein grosses Problem ist, dass oft nur ein (1) Passwort oder Zertifikat/Key verwendet wird. Wenn diese Information gestohlen wird, kann sich der Dieb an sämtlichen Systemen authentisieren. Genau hier kommt eine Multi-Factor, oft auch Two-Factor genannte, Authentifizierung ins Spiel. Das Ziel ist es, mehrere Faktoren zu haben, die möglichst nie zusammen aufbewahrt werden. Ist einer der Faktoren dabei zusätzlich noch dynamisch (One-Time Passwort oder Challenge-Response Verfahren) sollte dies auch sogenannte Replay-Attacken verhindern.

Grundsätzlich kann eine Multi-Factor Authentication auch mit Passwort und Zertifikat realisiert werden. Bei SSH z.B. mit Passwort und RSA Key, im Web mit Passwort und x509 Zertifikaten. Im Nachfolgenden wird aber auf One-Time Passwörter und Challenge-Response Verfahren eingegangen, welche eine zusätzliche Hardware-Komponente voraussetzen. Die Idee dabei ist, dass ein Passwort (den Faktor Wissen) und eine Hardware (den Faktor Besitzen) im Spiel sind was den einfachen Diebstahl verhindert.

FIDO U2F

FIDO U2F ist ein sehr junges Protokoll, welches auf public Key Kryptographie aufbaut. Ein nativer Support im Browser (aktuell nur in Chrome / Chromium und per Addon in Firefox) ist Voraussetzung. Theoretisch kann es auch für SSH und andere Dienste verwendet werden. Auch hier ist die Voraussetzung, dass die Programme dies nativ unterstützen. Entsprechende Patches für den OpenSSH Client sind bereits verfügbar.
Nach erfolgreicher Authentifizierung per Passwort oder Zertifikat, schickt der Server dem Client eine einmalige Challenge worauf der Client dem Server eine, mit dem eigenen privaten Key signierte Response, zurück schickt.

OATH

Bei OATH gibt es zwei Protokolle, der Time-based One-time Password Algorithm TOTP (RFC6238) und der HMAC-based One-time Password Algorithm HOTP (RFC4226).

Zuerst wird ein Shared Secret zwischen Server und Client ausgetauscht. Oft wird dies durch einen QR-Code realisiert.

TOTP

Bei TOTP wird die Uhrzeit als Basis genutzt. Die Voraussetzung ist, dass bei Server und Client die Uhrzeit synchronisiert ist.
Der Authentication Code wird mit folgender Funktion generiert: HMAC(sharedSecret, timestamp) und ist für 30 Sekunden gültig.

HOTP

HOTP funktioniert gleich wie TOTP, nutzt aber an Stelle der Uhrzeit einen Counter, welcher Server- und Client-Seitig immer synchron sein müssen.
Der Authentication Code wird durch folgende Funktion generiert: HMAC(sharedSecret, counter) und ist für das nächste Login gültig (Zeitunabhängig).

One-Time Passwort (OTP)

One-Time Passwörter (OTP) sind ein einfaches Verfahren von Yubico, welches im Hintergrund zusätzliche Infrastruktur benötigt. Nach erfolgreicher Authentifizierung per Passwort oder Zertifikat, verlangt der Server noch ein OTP. Wird dieses gesendet, muss es der Server an einen Validation-Server weiterleiten für die Überprüfung der Einmaligkeit.

Drei OTPs sehen vom selben Yubikey zum Beispiel wie folgt aus:
<b>rjvrllbbckhe</b>flnbdhnbdgluhedfneecjrluudknuecu
<b>rjvrllbbckhe</b>nrvblgfvkcbdhbtnjegvdicnhcutundg
<b>rjvrllbbckhe</b>lvrdjdrjccrvkrlbiijictlullglfglj

Die ersten 12 Zeichen der Sequenz sind dabei der Publicname und die restliche Sequenz wird anhand eines AES-Keys und einem Counter generiert. Der AES-Key muss dabei dem Yubikey und dem Validation-Server (spezifisch dem yubikey-ksm) bekannt sein.
Bei jedem OTP wird der Counter mitgegeben, damit der Server seinen Counter synchronisieren kann.

Validation-Server

Die Validation-Server Software von Yubico yubikey-val und yubikey-ksm kann unkompliziert selber gehostet werden. Man kann aber auch die YubicoCloud, welche von Yubico selber gehostet und angeboten wird, verwenden.
Bei kritischer Infrastruktur ist das selber hosten Sicherheitstechnisch sicher sinnvoller. Dabei geht es weniger um den Datenschutz, sondern eher um die Verfügbarkeit. Wenn, als Beispiel, die Internet Anbindung unterbrochen wird und für die Reparatur der Firewall ein Login über Two-Factor Authentifizierung mit OTP nötig wäre, ist man in einem deadlock und hat keine Verbindung zum Internet mehr.

Finja – Ihr freundlicher Suchninja

5. June 2016

Stellen Sie sich vor, Sie erhalten ein Login auf einem Unixrechner, der auf einer esoterischen Hardware läuft. Auf diesem Rechner finden Sie ein 10-mloc C++/C/Java-Projekt, mit vielen einsatzspezifischen Sprachen und Dateiformaten.
Sie dürfen den Quellcode nicht auf Ihren Rechner kopieren, da dieser dem Kunden gehört. Sie dürfen den Quellcode anpassen und auf dem Rechner kompilieren. Sie dürfen nichts installieren.

Wie soll man sich hier zurechtfinden? Sie können grep versuchen, aber Sie werden an Alterschwäche sterben, bevor Sie alle Stellen gefunden haben, wo das Symbol, für das Sie sich interessieren, vorkommt. Vielleicht haben Sie Glück und GNU-Idutils ist installiert, aber natürlich kommt das Symbol auch in Dateien vor die Idutils nicht kennt und nicht indexiert.

Aber glücklicherweise finden Sie auf dem Rechner Python 2.6 und Sie können die Hilfe eines Ninja in Anspruch nehmen.

Und ja, solche Dinge kommen bei uns vor.

Finja

Finja

Finja ist ein Indexer der nur Python 2.6+ (3.4+) benötigt. Anders als viele der grossartigen Alternativen zu Finja, ist Finja generisch. Es weiss nicht, was es indexiert. Finja erreicht gute Qualität durch mehrere Durchgänge in denen der Text auf unterschiedliche Art in Tokens zerlegt wird. Deshalb ist es langsamer und der Index ist grösser als bei spezialisierten Indexern, aber es indexiert Ihren Kram und lässt keine unbekannten Dateien aus.

Finja Benutzen

Ich rufe finja -i im Verzeichnis in dem ich diesen Artikel schreibe auf.

$> finja -i
Makefile: indexed 87/147 (0.592) new: 38 UTF-8
finja-blog.html: indexed 4370/10492 (0.417) new: 1126 UTF-8
finja-blog.rst: indexed 487/1517 (0.321) new: 73 UTF-8
Indexing done

Im Makefile hat es 87 einmalige Tokens gefunden. Tatsächlich hat es in allen Durchläufen 147 Token gefunden, im ersten Durchlauf benutzt es zum Beispiel Leerzeichen als Trennzeichen, in einem zweiten Durchlauf benutzt es Satzzeichen der natürlichen Sprache als Trennzeichen und im dritten Durchlauf benutzt Finja Trennzeichen die in gängigen Programmiersprachen vorkommen. Damit zeigt 0.592 den Zusatzaufwand durch die Durchläufe an (1.0 wäre perfekt, tiefer Kennzahlen sind schlechter). Es hat 38 neue Tokens gefunden und UTF-8 als Kodierung der Datei erkannt. Jawohl, Finja kann die Kodierung erkennen. Um die Effizienz zu steigern, nimmt Finja einfach an, dass die Datei in UTF-8 kodiert ist. Erst wenn es zu einem Dekodierungsfehler kommt, wird die Kodierung detektiert.

Jetzt möchte ich ein paar Ninjas finden.

$> finja ninja
.:
finja-blog.html:    7:<title>Finja - Your friendly finding ninja</title>
finja-blog.html:  696:<div class="document"
finja-blog.html:  697:<h1 class="title">Finja - Your friendly finding ninja</h1>
finja-blog.rst:    1:Finja - Your friendly finding ninja
finja-blog.rst:   16:a ninja.

Die Resultate aus der HTML-Datei interessieren mich nicht.

$> finja -p html ninja
.:
finja-blog.rst:    1:Finja - Your friendly finding ninja
finja-blog.rst:   16:a ninja.

Wir finden also im Order ‘.’ Ninja zwei mal auf Linie 1 und 16. Aber halt, dass ist nicht aktuell.

$> finja -u
finja-blog.rst: indexed 939/2897 (0.324) new: 111 UTF-8

Mit finja -u werden nur Dateien die sich verändert haben neu indexiert.

$> finja -p html ninja
.:
finja-blog.rst:    1:Finja - Your friendly finding ninja
finja-blog.rst:   16:a ninja.
finja-blog.rst:   61:   $> finja ninja
finja-blog.rst:   63:   finja-blog.html:    7:<title>Finja - Your friendly
finja-blog.rst:   65:   id="finja-your-friendly-finding-ninja">
finja-blog.rst:   66:   finja-blog.html:  697:<h1 class="title">Finja - Your
finja-blog.rst:   67:   finja-blog.rst:    1:Finja - Your friendly finding ninja
finja-blog.rst:   68:   finja-blog.rst:   16:a ninja.
finja-blog.rst:   74:   $> finja -p html ninja
finja-blog.rst:   76:   finja-blog.rst:    1:Finja - Your friendly finding ninja
finja-blog.rst:   77:   finja-blog.rst:   16:a ninja.

“Yo dawg, I hear you like searching ninjas, we put ninjas in your search, so you can search ninjas while you search.” Hmm, das Resultat ist etwas Rekursiv, aber wir haben bewiesen, dass der Index aktualisiert wurde.

Wir können auch in Unterverzeichnissen suchen:

$> cd dir/
$> finja -u -p html punctuation
..:
finja-blog.rst:   51:second pass it uses punctuation marks from natural

SQLite kann Ihr Freund sein

Natürlich haben wir mit einem 10 mloc Projekt schnell Leistungsprobleme gefunden. Es stellt sich heraus, dass der Anfrageoptimierer von SQLite dumm ist, dumm ist das richtige Wort, den er ist nicht schlecht, aber nicht so ausgeklügelt wie wir es uns von PostgreSQL gewohnt sind. Standardmässig wird keine Histogrammanalyse gemacht und deshalb werden die Tabellen in einer schlechten Reihenfolge vereinigt. Jedoch wenn man SQLite mit SQLITE_ENABLE_STAT4 kompiliert, was Histogrammanalyse aktiviert, werden die Tabellen immer noch in einer schlechten Reihenfolge vereinigt. Ich mache SQLite keinen Vorwurf, es ist eben nicht PostgreSQL. Wir haben die Anfragen also in Finja optimiert.

SELECT
    COUNT(id) count
FROM
    finja
WHERE
    token_id = ?

Wir überprüfen zu erst wie oft ein Token im Index vorkommt, also sozusagen eine manuelle Histogrammanalyse. Dann vereinigen wir den Index mit dem seltensten Token zuerst.

def search_term_cardinality(term_id):
    db         = get_db(create = False)
    con        = db[0]

    curs = con.cursor()
    res = curs.execute(_token_cardinality, [term_id]).fetchall()
    return res[0][0]

def order_search_terms(search):
    res = sorted(search, key=search_term_cardinality)
    return res

Dies basiert natürlich auf der Annahme, dass die Tokens über mehrere Dateien und Zeilen verteilt sind. Wenn es eine Datei gibt in der sich das Token auf der selben Zeile eine Million mal wiederholt, werden wir eine falsche Entscheidung treffen. Aber in den meisten Fällen ist es viel besser als SQLite entscheiden zu lassen.

Schlusswort

Bitte installieren Sie Finja, nutzen es und falls Sie ein Problem finden eröffnen Sie doch bitte ein Ticket auf Github.

Besten Dank!

UCS Ansible Modules

24. March 2016

Ein Univention Corporate Server (UCS) eignet sich nicht besonders, um per Ansible konfiguriert zu werden, da viele Tasks entweder über das Web GUI oder über spezielle Bash Befehle gemacht werden müssen. Beispiele dafür sind Benutzer und Gruppen anlegen, modifizieren oder löschen.
Da wir von der Adfinis SyGroup trotzdem UCS automatisiert installieren und konfigurieren wollen, haben wir für diverse Tasks neue Ansible Module geschrieben. Dazu gehören aktuell folgende:

  • udm_group
  • udm_user
  • udm_dns_zone
  • udm_dns_record
  • udm_share

Diese Module sind ab Ansible Version 2.2 Bestandteil von Ansible Modules Extras und können entsprechend mit Ansible genutzt werden, wie andere Module auch.

Falls in Zukunft weitere Ansible Module erstellt werden (und noch nicht Teil von Ansible sind), können diese pro Projekt hinzugefügt werden.
Nachfolgend wird kurz erklärt, wie solche zusätzliche Ansible Module installiert werden können und danach noch die oben genannten Module kurz präsentiert.

Installation

Zusätzliche Ansible Module werden entweder per Projekt oder in den Ansible Source Code installiert. Um zusätzliche Module per Projekt zu installieren, müssen diese in den Ordner “library” unterhalb des Top-Directories des Projekts kopiert werden. Dies sieht dann etwa wie folgt aus:

$ ls
|- ansible.cfg
|- group_vars/
|  |- all/
|- inventory
|- library/
|  |- README.md
|  |- ucr.py
|  |- udm_dns_record.py
|  |- udm_dns_zone.py
|  |- udm_group.py
|  |- udm_share.py
|  |- udm_user.py
|- README.md
|- site.yml

Werden die Module im Ansible Source Code installiert, muss der gesamte Ansible Source Code geklont werden:

$ git clone https://github.com/ansible/ansible.git
$ cd ansible/
$ git submodule update --init --recursive

Danach kann Ansible mithilfe von pip installiert werden:

$ virtualenv -p /usr/bin/python2 venv
$ . venv/bin/activate
$ pip install -e ansible/

Abschliessend müssen die zusätzlichen Ansible Module noch in den Ordner ansible/lib/ansible/modules/extras/ oder einen Subordner davon kopiert werden. Die Univention Module gehören z.B. noch in den Subordner univention.

udm_group

Um eine Gruppe anzulegen, welche den Namen employee und als LDAP DN cn=employee,cn=groups,ou=company,dc=example,dc=org hat, ist folgender Ansible Task notwendig:

- udm_group: name=employee
             description=Employee
             ou=company
             subpath=‘cn=groups‘

Wird nur das Attribut name angegeben, wird die Gruppe mit der DN cn=<name>,cn=groups,<LDAP Base DN> angelegt.

udm_user

Ein User Objekt umfasst sehr viele mögliche Attribute, nachfolgend wird somit nur ein minimales Beispiel gezeigt. Alle zur Verfügung stehenden Attribute sind direkt im Ansible Modul dokumentiert.

Soll ein User Hans Muster mit dem Benutzernamen hans.muster und dem Passwort secure_password erstellt werden, ist folgender Task nötig:

- udm_user: name=hans.muster
            firstname=Hans
            lastname=Muster
            password=secure_password

Das Angeben des genauen LDAP Pfades ist wie bei udm_group auch möglich. Wird nichts weiteres angegeben, wird der User mit der LDAP DN uid=hans.muster,cn=users,dc=example,dc=com erstellt.

udm_dns_zone

DNS Zonen haben nicht sehr viele mögliche Attribute. Etwas speziell ist, dass in der Zone die Interfaces, NS und MX Records definiert werden.
Die Interfaces können mit BIND 9 Views verglichen werden. Diese definieren, von wo aus die entsprechenden DNS Queries beantwortet werden.
Die NS und MX Records werden bei UCS speziell gehandhabt und werden deshalb nicht mit udm_dns_record, sondern per udm_dns_zone, konfiguriert.

Beispielsweise wird die Forward Zone example.com mit dem zuständigen Nameserver ucs.example.com, welcher DNS Queries auf der IP Adresse 192.168.1.1 beantwortet, wie folgt angelegt:

- udm_dns_zone: zone=example.com
                type=forward_zone
                nameserver=['ucs.example.com']
                interfaces=['192.168.1.1']

udm_dns_record

Einzelne DNS Records können mit udm_dns_record erstellt werden. Mögliche Einträge sind:

  • host_record (A und AAAA Records)
  • alias (CNAME Records)
  • ptr_record
  • srv_record
  • txt_record

Soll zur Zone example.com der Eintrag www.example.com. IN A 192.168.1.1 hinzugefügt werden, ist folgender Task nötig:

- udm_dns_zone: name=www
                zone=example.com
                type=host_record
                data=['a': '192.168.1.1']

udm_share

Mit dem Modul udm_share können Samba und NFS Shares gehandhabt werden. Ein Share Objekt beinhaltet eine Vielzahl von Attributen, diese sind im Ansible Modul dokumentiert.

Damit das Share homes auf dem Ansible Target System angelegt wird, ist folgender Task nötig:

- udm_share: name=homes
             host='{{ ansible_fqdn }}'
             path=/home
             owner=root
             group=root
             directorymode='00755'
             samba_name=homes

Links

Systemd: start und stop dependencies

18. February 2016

Das Problem

Auf einem Debian-Server (jessie, systemd 215) läuft eine Web-Applikation, die einen Samba-Share (cifs) via openvpn (IPv6) benötigt. Mit der naiven Konfiguration funktioniert manuelles Starten und Stoppen problemlos, beim Reboot geht’s aber nicht richtig.

Naive Konfiguration:

  • Debian Packages: apache2 cifs-utils openvpn
  • cifs mount in /etc/fstab konfiguriert
  • openvpn Konfiguration in /etc/openvpn/client.conf

Wenn man alles einzeln startet, funktioniert es reibungslos:

  • systemctl start openvpn@client.service
  • mount /var/www/data
  • systemctl start apache2.service

Auch das Stoppen geht manuell problemlos:

  • systemctl stop apache2.service
  • umount /var/www/data
  • systemctl stop openvpn@client.service

Aber bei einem reboot klappt weder Hochfahren…

  • Beim Start wird der mount versucht, bevor das VPN verfügbar ist und schlägt natürlich fehl.
  • Apache läuft zwar, aber die Web-Applikation hat ein Problem, sobald sie auf Files im cifs mount zugreifen will.

… noch Runterfahren:

  • Das VPN ist unterbrochen, bevor der umount abgeschlossen ist.
  • Das System hängt 2 Minuten bis der Timeout für den umount abgelaufen ist. Sehr lästig.

Ansatz #1

Die Lösung sollte mit systemd units nicht allzu schwierig sein.

Da sich in /etc/fstab keine Abhängigkeiten explizit formulieren lassen, wird für den mount eine eigene Unit-Datei erstellt. Wir starten mit der automatisch erzeugten Unit-Datei:

systemctl cat var-www-data.mount > /etc/systemd/system/var-www-data.mount

Und löschen die Einträge SourcePath und Documentation, so dass nur noch das Minimum da steht:

#/etc/systemd/system/var-www-data.mount
[Unit]
Before=remote-fs.target

[Mount]
What=//v6.smb.example.com/WwwDataShare
Where=/var/www/data
Type=cifs
Options=ro,guest,iocharset=utf8

Dann wird der Eintrag aus /etc/fstab gelöscht und die Unit aktiviert mit:

systemctl enable var-www-data.mount; systemctl daemon-reload

/var/www/data müsste jetzt sauber ein- und wieder ausgehängt werden können.

Jetzt können wir die Abhängigkeit zu openvpn formulieren:

#/etc/systemd/system/var-www-data.mount
[Unit]
Before=remote-fs.target
After=openvpn@client.service
Requires=openvpn@client.service

systemctl daemon-reload

Funktioniert leider immer noch nicht. Nach diversen Analysen mit journalctl -f war klar, dass

  • die openvpn Unit beim Start behauptet, sie sei “fertig”, obwohl die IPv6 Routen noch nicht korrekt gesetzt sind.
  • der umount von /var/www/data beim Stoppen behauptet, er sei fertig, obwohl es noch ausstehende Netzwerk-Kommunikation hat.

Lösung

Zuerst braucht es ein Script, welches die Vorbedingungen beim Starten (ping) und Stoppen (umount) prüft:

/etc/openvpn/checks_updown

#!/bin/bash
LOGGER_RED="systemd-cat -t $0 -p err"
LOGGER_BOLD="systemd-cat -t $0 -p notice"
LOGGER_NORM="systemd-cat -t $0 -p info"

case "$1" in
  ping) 
    while true
    do
      if ping6 -q -c 3 v6.smb.example.com > /dev/null 2>&1 
      then
        echo ping6 ok | $LOGGER_BOLD
        exit 0
      else
        echo "." | $LOGGER_NORM
        sleep 4
      fi
    done
    ;;
  umount) 
    while true
    do
      if mount | grep "//v6.smb.example.com/" >/dev/null 2>&1
      then
        echo ";" | $LOGGER_NORM
        sleep 4
      else
        echo umount ok | $LOGGER_BOLD
        exit 0
      fi
    done
    ;;
  *) 
    echo "invalid call: $1" | tee | $LOGGER_RED
    exit 1
    ;;
esac

Für dieses Script bauen wir eine eigene Unit:

# /etc/systemd/system/vpnbarrier.service
[Unit]
Requires=openvpn@client.service
After=openvpn@client.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/openvpn/checks_updown ping
ExecStop=/etc/openvpn/checks_updown umount

[Install]
RequiredBy=var-www-data.mount

Das RemainAfterExit=yes bewirkt (zusammen mit oneshot), dass ExecStop erst beim Stop dieser Unit ausgeführt wird. Ohne diese Einstellung würde ExecStop unmittelbar nach ExecStart, also noch während des Hochfahrens, ausgeführt.

Abhängigkeiten in der mount-Unit erweitern:

# /etc/systemd/system/var-www-data.mount
[Unit]
Before=apache2.service
Requires=vpnbarrier.service
After=vpnbarrier.service

[Mount]
...

[Install]
RequiredBy=apache2.service

systemctl enable vpnbarrier.service; systemctl daemon-reload

Erst jetzt funktioniert alles wie es muss:

  • systemctl stop openvpn@client.service: Zuerst wird apache2 gestoppt, dann /var/www/data abgehängt und erst dann das VPN gestoppt.
  • systemctl start apache2.service: Zuerst wird das VPN gestartet, dann der cifs-mount und schliesslich apache2.
  • Ein reboot dauert knapp 20 Sekunden.

Bemerkungen

  • Die Dokumentation von systemd ist recht gut, zum Beispiel bei digitalocean. Der Schwerpunkt ist aber meistens auf dem Hochfahren und man findet kaum etwas zum Runterfahren.
  • Eine etwas andere Diskussion hat gezeigt, dass es auch andere gibt, denen etwas fehlt.
  • Die wichtigste Einsicht: Immer sowohl Requires als auch After benutzen, nur so kriegt man auch das Runterfahren in den Griff.
  • Root Cause: Ist das Problem ein generisches systemd-Problem oder ein Bug im openvpn-Paket? Und beim cifs umount?
  • Könnte man das Problem noch etwas eleganter Lösen, z.B. indem man auf eine separate Unit vpnbarrier.service verzichtet und den “ping”- bzw “umount”-Check durch Einträge in *.conf.d-Verzeichnisse geeignet einbaut?
  • Ab und zu war es nicht ausreichend, systemd daemon-reload zu machen, erst nach einem Reboot hat alles wie erwartet funktioniert.

GnuPG und SmartCards

18. January 2016

Damit GnuPG (Manpage) oder PKCS#11 Keys nicht mehr durch einen Virus oder ähnliches gestohlen werden können, können diese auf ein Hardware Security Modul (HSM) oder eine SmartCard ausgelagert werden. Dieser Blog Artikel soll zeigen, wie ein GnuPG Key nach “best current practice” erstellt und danach in einer SmartCard (z.B. Yubikey oder Nitrokey) gespeichert wird. Gleiches wäre auch mit einem PKCS#11 Key möglich, auf diese wird hier jedoch nicht weiter eingegangen, das Verfahren sollte aber ähnlich sein.
In diesem Beispiel wird ein GnuPG Key erstellt, welcher drei Subkeys (Encryption, Signing und Authentication) hat. Danach werden nur die Subkeys in die SmartCard geladen. Der eigentliche Masterkey wird in eine Datei gespeichert, welche schlussendlich offline an einem möglichst sicheren Ort aufbewahrt werden sollte. Der Vorteil von diesem Setup ist, dass ohne Masterkey die Keys nicht mehr abgeändert werden können (Ablaufdatum, UIDs, Subkeys, …).

GnuPG Key erstellen

$ gpg2 --expert --full-gen-key
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
Your selection? 8

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

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

Your selection? s

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

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

Your selection? e

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

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

Your selection? q
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 3y
Key expires at Sam 15 Feb 2020 09:27:17 CET
Is this correct? (y/N) y

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

Real name: Pallando
Email address: pallando@example.org
Comment:
You selected this USER-ID:
    "Pallando <pallando@example.org>"

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

pub   rsa4096/0x0DA3852689C0BC38 2017-02-15 [C] [expires: 2020-02-15]
      01691D7408369C26A5B7D9390DA3852689C0BC38
      Key fingerprint = 0169 1D74 0836 9C26 A5B7  D939 0DA3 8526 89C0 BC38
uid                              Pallando <pallando@example.org>

GnuPG Key mit den Subkeys erweitern

Im obigen Key gibt es nun eine UID und einen Subkey. Dieser wird jetzt mit den anderen Subkeys erweitert.

$ gpg2 --expert --edit-key 0x0DA3852689C0BC38

sec  rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
[ultimate] (1). Pallando <pallando@example.org>

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? 8

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? e

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

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

Your selection? q
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 3y
Key expires at Son 24 Mär 2019 09:49:46 CET
Is this correct? (y/N) y
Really create? (y/N) y

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? 8

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

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

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

Your selection? q
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 3y
Key expires at Son 24 Mär 2019 09:49:46 CET
Is this correct? (y/N) y
Really create? (y/N) y

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? 8

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

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

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

Your selection? e

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

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

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
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 3y
Key expires at Son 24 Mär 2019 09:49:46 CET
Is this correct? (y/N) y
Really create? (y/N) y

sec  rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

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

Nun hat der Key drei Subkeys, jeweils einer für Signing, Encryption und Authentication. Die Capabilities haben folgende Bedeutung:

  • S: Good for signatures
  • E: Good for encryption
  • C: Also good to certify keys
  • A: Good for authentication

Nun sollte der gesamte Key mit dem Command gpg2 --armor --export-secret-keys 0x0DA3852689C0BC38 > 0x0DA3852689C0BC38.asc in ein File exportiert werden, welches danach an einem sicheren Ort aufbewahrt und abgelegt wird.

Mit dem Command gpg2 --armor --export-secret-subkeys 0x0DA3852689C0BC38 > 0x0DA3852689C0BC38_subs.asc exportieren wir die Subkeys und anschliessend wird der gesamte Key aus dem Keyring gpg2 --delete-secret-key 0x0DA3852689C0BC38 (dies muss mit zweimal “y” bestätigt werden) gelöscht.
Nun können die Subkeys (NUR die Subkeys) wieder importiert werden gpg2 --import 0x0DA3852689C0BC38_subs.asc. Somit sollte der Keyring wie folgt aussehen:

$ gpg2 --list-secret-keys
sec#  rsa4096/0x0DA3852689C0BC38 2017-02-15 [C] [expires: 2020-02-15]
      01691D7408369C26A5B7D9390DA3852689C0BC38
uid                   [ultimate] Pallando <pallando@example.org>
ssb   rsa4096/0x0B8635FD20045068 2017-02-15 [S] [expires: 2020-02-15]
ssb   rsa4096/0x8BF4DDB73F29DDAA 2017-02-15 [E] [expires: 2020-02-15]
ssb   rsa4096/0xC163F1DC0667E483 2017-02-15 [A] [expires: 2020-02-15]

Das # nach dem sec in der ersten Zeile des Outputs gibt an, dass der Masterkey nicht, sondern nur die Subkeys, vorhanden ist.

SmartCard konfigurieren

Dieses Beispiel wird anhand eines Yubikeys gemacht, ist aber genau so gültig bei anderen SmartCards, wie z.B. Nitrokeys.
Falls die SmartCard im voraus noch geresetet werden muss, kann dies folgendermassen gemacht werden (nur für SmartCards Version >2!):

$ gpg-connect-agent --hex
# reset your PIN
> scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
D[0000]  69 82                                              i.
OK
> scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
D[0000]  69 82                                              i.
OK
> scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
D[0000]  69 82                                              i.
OK
> scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
D[0000]  69 83                                              i.
OK
# reset your Admin PIN
> scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
D[0000]  69 82                                              i.
OK
> scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
D[0000]  69 82                                              i.
OK
> scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
D[0000]  69 82                                              i.
OK
> scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
D[0000]  69 83                                              i.

Danach wird die SmartCard konfiguriert:

$ gpg2 --card-edit 

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240102010006042451460000
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: 04245146
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

gpg/card> admin
Admin commands are allowed

gpg/card> passwd
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 3
Admin PIN prompt (default admin PIN is 12345678)
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 1
PIN prompt (default PIN is 123456)
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? q

gpg/card> name
Cardholder's surname: 
Cardholder's given name: Pallando

gpg/card> lang
Language preferences: de

gpg/card> sex
Sex ((M)ale, (F)emale or space): m

gpg/card> q

Danach sollte die Ausgabe von gpg2 --card-status in etwa so aussehen:

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240102010006042451460000
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: 04245146
Name of cardholder: Pallando
Language prefs ...: de
Sex ..............: male
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

GnuPG Keys in der SmartCard speichern

$ gpg2 --edit-key 0x0DA3852689C0BC38
Secret key is available.

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

gpg> key 1

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

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

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

gpg> key 1

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

gpg> key 2

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb* rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

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

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb* rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

gpg> key 2

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb  rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

gpg> key 3

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb* rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

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

sec# rsa4096/0x0DA3852689C0BC38
     created: 2017-02-15  expires: 2020-02-15  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x0B8635FD20045068
     created: 2017-02-15  expires: 2020-02-15  usage: S
ssb  rsa4096/0x8BF4DDB73F29DDAA
     created: 2017-02-15  expires: 2020-02-15  usage: E
ssb* rsa4096/0xC163F1DC0667E483
     created: 2017-02-15  expires: 2020-02-15  usage: A
[ultimate] (1). Pallando <pallando@example.org>

gpg> save

Danach sollte gpg2 --card-status in etwa so aussehen:

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240102010006042451460000
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: 04245146
Name of cardholder: Pallando
Language prefs ...: de
Sex ..............: male
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: DF3B 91AD 59A5 BF46 916D  9A32 0B86 35FD 2004 5068
      created ....: 2017-02-15 08:52:56
Encryption key....: 5777 58DA AE13 D7AC 9528  287C 8BF4 DDB7 3F29 DDAA
      created ....: 2017-02-15 08:34:39
Authentication key: EAEF A30B E182 129A CB7A  5D9F C163 F1DC 0667 E483
      created ....: 2017-02-15 08:58:07
General key info..: sub  rsa4096/0x8BF4DDB73F29DDAA 2017-02-15 Pallando <pallando@example.org>
sec#  rsa4096/0x0DA3852689C0BC38  created: 2017-02-15  expires: 2020-02-15
ssb>  rsa4096/0x0B8635FD20045068  created: 2017-02-15  expires: 2020-02-15
                                  card-no: 0006 04245146
ssb>  rsa4096/0x8BF4DDB73F29DDAA  created: 2017-02-15  expires: 2020-02-15
                                  card-no: 0006 04245146
ssb>  rsa4096/0xC163F1DC0667E483  created: 2017-02-15  expires: 2020-02-15
                                  card-no: 0006 04245146

und die Ausgabe von $ gpg2 --list-secret-keys sollte in etwa so aussehen:

sec#  rsa4096/0x0DA3852689C0BC38 2017-02-15 [C] [expires: 2020-02-15]
      01691D7408369C26A5B7D9390DA3852689C0BC38
uid                   [ultimate] Pallando <pallando@example.org>
ssb>  rsa4096/0x0B8635FD20045068 2017-02-15 [S] [expires: 2020-02-15]
ssb>  rsa4096/0x8BF4DDB73F29DDAA 2017-02-15 [E] [expires: 2020-02-15]
ssb>  rsa4096/0xC163F1DC0667E483 2017-02-15 [A] [expires: 2020-02-15]

Das > nach ssb steht dafür, dass der Key ein Stubkey (sprich nicht selber bekannt, sondern auf der SmartCard) ist.

Nun ist der GnuPG Key nur noch über die SmartCard verfügbar. Dabei werden die Daten jeweils an die SmartCard gesendet, diese entschlüsselt oder signiert sie und sendet diese wieder zurück.