Signieren und verschlüsseln mit GnuPG und PHP

Autogramm gefällig?
Kommentare

Abseits des Rummels um RFID in Ausweisen, kompletter Kommunikationsüberwachung und anderen Auswüchsen gläsernen Verbrauchertums etabliert sich langsam aber sicher eine Kultur derjenigen, die ihre Privatsphäre als Luxus betrachten und diesen Luxus durchaus zu schützen bereit sind. Wichtige Instrumente sind dabei Verschlüsselungstechniken wie GnuPG, die mit der entsprechenden Extension nun auch in PHP direkt nutzbar sind.

Am Anfang war das Wort. Und damit auch jemand, der es hören sollte. Ob zusätzlich schon jemand da war, den es eigentlich nichts anging, der es aber trotzdem unbedingt hören wollte, ist nicht überliefert. Ein paar tausend Jahre später hingegen ist es erschreckender Alltag geworden, dass Kommunikation und Informationswege zu den bestüberwachtesten Dingen des Lebens gehören, in Deutschland wie auch anderswo auf der Welt. Doch wie sich dagegen schützen? Telefongespräche lassen sich theoretisch inzwischen via Voice over IP abwickeln, dessen Verbindung man mittels SSH tunneln und so vor unbefugten Zuhörern schützen kann. Im WWW kann man sich mit ein wenig Aufwand durchaus auch relativ unbeobachtet bewegen. Qualifizierte Informationen im Internet, die zum Download angeboten werden, sichern sicherheitsbewusste Menschen mit einer Prüfsumme (einer so genannten Signatur) gegen Manipulationen.

Nur E-Mail dümpelt in vielen Fällen im Klartext durchs Netz, dabei ist das Problem des unbefugten Datenkonsums und der durchaus dabei möglichen Veränderung des Inhalts durch Dritte schon seit Jahren bekannt. Und es existiert seit 1991 eine durchaus praktische Lösung dafür, genannt Pretty Good Privacy, kurz PGP. Darunter fasst man das ursprünglich von Phil Zimmermann entwickelte Programm zur Verschlüsselung von Daten, das den zu übermittelnden Content mithilfe von Schlüsselpaaren aus öffentlichem und privatem Key vor unbefugten Lesern schützen soll und gleichzeitig eine Möglichkeit zur Signierung von im Klartext übertragenen Daten anbietet. Aus dem Wunsch geboren, Bürgerbewegungen und auch den Bürgern der Vereinigten Staaten die Möglichkeit zu geben, von Geheimdiensten unbeobachtet Nachrichten auszutauschen, entwickelte sich über die Jahre nicht nur ein unter kommerzieller Lizenz laufendes Programm, sondern auch ein offener Standard – OpenPGP, dessen erste Implementierung GnuPG darstellt. GnuPG (oder auch GPG) steht dabei für Gnu Privacy Guard und verwendet im Unterschied zu PGP nur patentfreie Algorithmen zur Verschlüsselung. Selbstverständlich ist der Einsatz sowohl auf Unixoiden, als auch auf Macintosh- und Windows-Rechnern möglich.

Signieren und Verschlüsseln

Das Kernelement von PGP und GnuPG sind Schlüssel. Und zwar jeweils ein zusammengehörendes Pärchen, nämlich der private Schlüssel, den man selbst möglichst sicher aufbewahrt, und der öffentliche Schlüssel, der Dritten übermittelt werden muss, um zum einen Signaturen auf Echtheit zu prüfen und um zum anderen E-Mails korrekt zu verschlüsseln. Natürlich muss dazu auch in irgendeiner Weise sichergestellt werden, dass man beispielsweise nicht einfach nur irgendeinen Schlüssel hat, der zufällig zu einer Signatur passt, sondern es sollte durchaus ein Key sein, der der signierenden Person auch wirklich glaubhaft zugeordnet werden kann. Zu diesem Zweck hat sich im Laufe der Zeit ein so genanntes „Web of Trust“ herausgebildet, bei dem bereits authentifizierte Schlüsselinhaber anderen Schlüsselinhabern bestätigen, dass der angegebene Key wirklich zu der Person gehört, die sie zu sein vorgibt.

Dazu werden zum Beispiel gerne so genannte „Keysigning Parties“ ins Leben gerufen, auf denen die Authentifizierungswilligen mit Pass oder Ausweis sowie ihrem Schlüssel unterwegs sind und gegenseitig nach Prüfung der Personendaten ihre Keys austauschen. Im Nachhinein werden dann die ertauschten öffentlichen Schlüssel vom Prüfenden mit dem eigenen privaten Schlüssel signiert und so als vertrauenswürdig deklariert. Diese Signierung kann auch von bekannteren Zertifizierungsstellen vorgenommen werden, in Deutschland kann man diesen Service beispielsweise bei Cacert, der Zeitschrift c’t und vom Deutschen Forschungsnetz kostenlos in Anspruch nehmen.

Hat man sich solcherart erst einmal als vertrauenswürdig dargestellt, kann der nächste Schritt folgen: das Signieren von E-Mails und auch das Verschlüsseln derselben. Dies kann man sich in etwa wie folgt vorstellen: Beim Signieren wird der zu übertragende Text primär vor dem Angriffsszenario „Veränderung des Inhalts“ geschützt. Hierzu verwendet der Autor seinen privaten Schlüssel und signiert damit die E-Mail. Die erzeugte Signatur wird an den eigentlichen Text angefügt und der Text selbst als signiert gekennzeichnet. Der Empfänger der Nachricht kann den entsprechenden Inhalt problemlos lesen, auch wenn er nicht im Besitz des öffentlichen Schlüssels des Absenders ist. Will er jedoch prüfen, ob der dargestellte Inhalt auch dem Inhalt entspricht, den der Absender verschickt hat, muss er die Nachricht mittels des öffentlichen Keys des Absenders verifizieren. Das Verschlüsseln einer E-Mail erfolgt ähnlich: Hier wird der zu versendende Text mit dem öffentlichen Key des Empfängers verschlüsselt.

Das daraus entstehende Textfragment ist ausschließlich mit dem privaten Schlüssel des Empfängers wieder zurück in etwas Lesbares zu verwandeln. Somit wird nicht nur sichergestellt, dass der Text während der Übertragung nicht von Dritten verändert wird, sondern zusätzlich ein sicherer Schutz gegen unbefugtes Mitlesen hinzugefügt. Hierbei kommt eine so genannte Hybride Verschlüsselung zum Einsatz, bei der der eigentliche Inhalt einer Nachricht symmetrisch verschlüsselt wird und nur der zufällig erzeugte, symmetrische Schlüssel asymmetrischer Verschlüsselung vermittels des öffentlichen Schlüssels des Empfängers unterliegt. (Klingt verwirrend? Vereinfacht gesagt gibt es zwei grundsätzliche Arten, etwas zu verschlüsseln: symmetrisch und asymmetrisch. Beide Varianten haben Vor- und Nachteile, die bei der so genannten Hybriden Verschlüsselung zum bestmöglichen und möglichst effizienten Verhalten zusammengeführt werden.) Dieser Schlüssel wird der zu übermittelnden Nachricht angefügt. In der Praxis entsteht dann eine E-Mail, deren Body als lesbares Element den Hinweis auf die Verschlüsselung selbst sowie die verwendete GnuPG-Version enthält. Der Rest ist komplett verschlüsselt.

ext/gnupg

Ist das Interesse an derart geschützter Kommunikation erst einmal geweckt, stellt sich die Frage, wie sich Derartiges zum Beispiel für Webmail in PHP realisieren lassen könnte. Ein Blick ins PECL-Archiv offeriert schnelle Hilfe: Es existiert eine GnuPG-Extension, die laut Aussage des Entwicklers entgegen dem Vermerk in der Dokumentation mit Version 1.0 den Status stable erreicht hat – derzeit kann man bereits Version 1.2 herunterladen. Die Installation erfolgt wie bei PECL-distributierten Extensions üblich über den Standardweg, der im Optimalfall zum Erfolg führen sollte. Ist die Extension erfolgreich installiert und eingebunden, kann es direkt losgehen. Wichtig bei der weiteren Arbeit ist das Vorhandensein eines Schlüsselrings, den wir initial erzeugen sollten. In unserem Beispiellisting 1, das den in Listing 2 dargestellten Key verwendet, wird diese grundlegende Prozedur gezeigt.

Listing 1: Umgang mit dem Schlüsselring

seterrormode(gnupg::ERROR_EXCEPTION);

 // Key Datei einlesen ...
    $keydata = file_get_contents($GNUPGHOME.'/demo.key');

 // ... und zum Keyring hinzufuegen
    $rc = $gpg->import($keydata);

 // ... und Fingerprint ausgeben
    echo "Fingerprint: {$rc['fingerprint']} ";


 // Keyring Iterator ueber alle Keys erzeugen ...
    $iterator = new gnupg_keylistiterator('');

 // ... und durchgehen
    foreach($iterator as $fingerprint) {
     // Daten des Schluessels mittels fingerprint abrufen
        $keyinfo = $gpg->keyinfo($fingerprint);

     // ... und ebenfalls ausgeben
        foreach ($keyinfo[0]['uids'] as $uid => $data) {
            echo "Eintrag $uid: n";
            foreach ($data as $key => $value) {
                echo "$key: $value n";
            }
        }

     // Den Schluessel kann man natuerlich auch wieder exportieren...
        $keytext = $gpg->export($fingerprint);

        echo "Export Schlüssel:
$keytext

"; } ?>

Listing 2: Der Beispielschlüssel

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.5 (GNU/Linux)

mQGiBEUtZ50RBACGhQ95dvX7LTS6YcVEI8e83m+IgcsvIkxpqQfOOe4GKSmC3XAS
rPQwM6ravvoXkgHX+Hei1myyitmlZ0Dosq2VpQLRovwJpa9gyLNo/tauqc66iiNj
8LA/nxR9aFj70HDO4JCXRU9i2Sw7HJeEdATMQoxBqAnH5N0ugfMkr0/eEwCgxBsL
7D9WOdEKQUJ7EiFHaLylqpsD/iaF5PZJlk9iVLfGpF0Ldm5sogACvAp3kVMHmxol
438VSXRZ37EHJJ53ffdpKqwU/8KHGzLQHugLZBHbzDJCn2yByk76NXN5s1WWp5zX
iq56fspnX09Vg+9wSIpYod+SGIMivyPwBuDr8qQDvbZPJw1kTcuCkk0gyLc4qRrP
Wne4A/4s+zbReuR00R1Vg/FiHEDMKa4kZwsdo5NRhZDxaSXM7TcCwhj1QYwp/Mn3
Skj1mQTL7F5NUgxUQR8nSj48b/l6x6dRhwh2fDdgmFm7nNtyBL0Ui3L7oE0+7IlB
Xi9RLGAtJNoKTdMc7q2LhjUSapO01JpKyWDlPcnqkhjxhZ1yurROUEhQTWFnYXpp
biBTaWduYXR1cmUgYW5kIENyeXB0IChDcnlwdCB1bmQgU2lnbmF0dXIgRGVtbykg
PENyeXB0QHBocG1hZ2F6aW4uZGU+iGAEExECACAFAkUtZ50CGwMGCwkIBwMCBBUC
CAMEFgIDAQIeAQIXgAAKCRAGZREMYogf8hedAKCNyPUB1LwvvHREQNx0gR/+yhpg
kwCgwpRS9f+TnxN9hKRPlvcG3woroEy5Ag0ERS1nthAIANpHUiz8OYqGwfW74oy6
PJDhBHpXSGTWSDMfpPEsXpvd9tkuHH3zDJBUx1RBrZmorCxuUBVf+Og/nDZLCAmG
HrTjjFVx9Ef9WuYfCDt5CHpgfPT9T5Z+GIOZx9Bi00MC0sYV99C9mb1PnCaAqhiF
/Gp7WE6zVPDvdgd8TYulqSqPbuCy25DGjxgifB0X2AjcLpE12N1Z+wx7s93N6/c3
+tP+i22lxrqgdmD4g6sWz55iMzuhVud0zGQGWp8UcX3OIXkpIFwAe0Xwza+0O/BY
rDy/PHVvCfMWtdBqMRRoDbOVKKLeWHbpgPgGXaVSVfUR2DJKLUWSFNtbbFqbh2HL
578AAwcH/RA11hOpsrbpCsBf6sLOp7/7Was/iKWy2EXvTeAIkHgVpbG3hRh9a0LY
rMiMP2/p9fg+S7RpvQHWAXna+7xJL02Y54jmbFA0vN4v7Pq0/LNKgIv0mxgOiWah
KlQvob3LJCbu2iRGSKTLtyosrdb5Zqfy0gUI5j7epEyTQfutvrrJQUVxEkdKyEXu
4MQSKJlUMuOAx2/WW8yUJ3vj4jfl6JYCuPJYBRbQ3kqwkRsHEBE4DHbCguRQ0ZPU
dnbXcFdhije/NycQMD17VC0EmjDg81WcKaC+kAaKrW519ItKbTBUctocqhg3XE2e
M9bycKAgvpiaqwaOn9p1+aRnj59o/tqISQQYEQIACQUCRS1ntgIbDAAKCRAGZREM
Yogf8s/FAKCNYUn3gF37NouZPZN/RbANeh/hWgCgjKVM64QkPGxwygWOQpGpqQtY
zKc=
=G7c9
-----END PGP PUBLIC KEY BLOCK-----

Bei der Implementierung ist zu beachten, dass der Schlüsselring des aktuellen Benutzers verwendet wird (in vielen Fällen entspräche dies wohl dem httpd-User). Es ist also in unserem Interesse, für die berechtigten und am System authentifizierten Benutzer als erstes einen jeweils eigenen Schlüsselring anzulegen und dessen Lokation dem System mitzuteilen. Tun wir dies nicht, wird die Extension versuchen, auf einen Schlüsselring unter der Defaultadresse ~/.gnupg/. zuzugreifen. Um einen Keyring in unserer Applikation verfügbar zu machen, importieren wir als erstes einen im Schlüsselverzeichnis liegenden Key. Wollen wir wissen, ob wir den richtigen Key importiert haben, könnten wir zum Beispiel den Fingerprint des Schlüssels überprüfen, indem wir uns diesen ausgeben lassen oder aber mit einem gegebenen Fingerprint vergleichen. Die GnuPG-Extension stellt hierfür bei erfolgreichem Import ein Array mit den entsprechenden Informationen als Rückgabewert der Import-Methode bereit.

Wir können natürlich auch auf die keyinfo-Methode zurückgreifen, auch hiermit ist es möglich, die zum Schlüssel gehörenden Informationen abzufragen und auszugeben. Um die Informationen zu allen geladenen und in den Keyring integrierten Schlüsseln zu betrachten, steht uns mit dem gnupg_keylistiterator ein passendes Werkzeug zur Verfügung, mit dessen Hilfe sich über die im Schlüsselbund vorhandenen Keys iterieren lässt. Die Kombination aus gnupg_keylistiterator und der keyinfo-Methode bildet damit ein hervorragendes Gespann, um sich über den aktuellen Zustand des eigenen Keyrings zu informieren. Einen im aktuellen Schlüsselbund befindlichen Schlüssel können wir selbstverständlich auch mittels der export-Methode exportieren, die den Fingerprint des betreffenden Keys als Argument benötigt, um ihn innerhalb des Schlüsselbundes zu identifizieren.

Mit dem Wissen um die Erstellung des zum aktuellen User gehörenden Schlüsselbundes haben wir nun die Grundvoraussetzung für unsere weiteren Aktivitäten geschaffen und können uns der Frage zuwenden, wie sich denn zum Beispiel eine E-Mail unter Verwendung der Extension signieren lässt. Wir erinnern uns, dass eine signierte E-Mail in zwei Mailbody-Bereiche aufgeteilt ist, den eigentlichen Text, der mit —–BEGIN PGP SIGNED MESSAGE—– eingeleitet wird, und die eigentliche Signatur, die von —–BEGIN PGP SIGNATURE—– und —–END PGP SIGNATURE—– umschlossen wird. Wir benötigen die gleiche Struktur, um eine valide E-Mail zu erzeugen, die der Mailclient des Empfängers korrekt interpretieren kann. Im ersten Teil von Listing 3 signieren wir einen Text, im zweiten Teil emulieren wir die empfängerseitige Verarbeitung. Wenn wir den entsprechenden Code zum Signieren des Textes näher betrachten, stellen wir fest, dass GnuPG offensichtlich mehrere Möglichkeiten implementiert, einen String zu signieren respektive die Rückgabe unterschiedlich gestaltet. Wir unterscheiden hier zwischen den Modi Clear, Normal und Detach. Für die von uns angestrebte Form des signierten Mailtextes werden wir den Clear-Text-Modus verwenden, auch und gerade, weil es hier für uns kaum noch etwas zu tun gibt, nachdem unsere Mitteilung erst einmal die Signierung durchlaufen hat.

Prinzipiell muss die Rückgabe nur noch als Parameter von mail() deklariert werden und schon kann der Versand erfolgen. Die Gegenrichtung erfolgt ähnlich einfach, wie der Verify-Teil des Listings zeigt. An dieser Stelle kommen auch die anfangs vorgenommenen Lokalisierungen der GnuPG-Statuscodes ins Spiel, denn natürlich wollen wir unseren Benutzern eine Aussage über die Gültigkeit einer Signatur in ihrer eigenen Sprache übermitteln.

Listing 3: Signieren
 'unbekannt',
        gnupg::VALIDITY_UNDEFINED => 'undefiniert',
        gnupg::VALIDITY_NEVER     => 'nie',
        gnupg::VALIDITY_MARGINAL  => 'marginal',
        gnupg::VALIDITY_FULL      => 'voll',
        gnupg::VALIDITY_ULTIMATE  => 'ultimativ',
    );

 // Environment einstellen
    $GNUPGHOME = dirname(__FILE__).'/../gpg';
    putenv('GNUPGHOME='.$GNUPGHOME);

 // GnuPG Objekt erzeugen
    $gpg = new gnupg();

 // Daten des Signatur-Schluessels
    $fingerprint = '228E3AEC1CB09629FDBAABAF0665110C62881FF2';
    $passphrase  = 'demo';

 // Der zu signierende Text
    $plaintext = 'Hallo Welt - Bitte signieren';

 // Fehler bitte als Warning anzeigen...
    $gpg->seterrormode(gnupg::ERROR_WARNING);

 // Signatur key anbinden
    if (!$gpg->addsignkey($fingerprint, $passphrase)) {
        die('Fehler beim Key einlesen');
    }

 // ASCII Armor Ausgabe
    $gpg->setarmor(1);

 // Clear text Modus (Text inkl. Signatur)
    $gpg->setsignmode(gnupg::SIG_MODE_CLEAR);
    $clear = $gpg->sign($plaintext);
    echo "Clear: 
" . $clear . "

n";

// Nur signierter Text (Normal mode) $gpg->setsignmode(gnupg::SIG_MODE_NORMAL); $signed = $gpg->sign($plaintext); echo "Normal:

" . $signed . "

n";

// Nur Signatur (Detach mode) $gpg->setsignmode(gnupg::SIG_MODE_DETACH); $signature = $gpg->sign($plaintext); echo "Detach:

" . $signature . "

n";

// Objekt neu erzeugen $gpg = NULL; $gpg = new gnupg();

$plaintext1 = ''; $plaintext2 = '';

// Verify #1 - Clear text (text+signatur) $ret = $gpg->verify($clear, false, $plaintext1); echo "Fingerprint #1: " . $ret[0]['fingerprint'] . " n"; echo "Gültigkeit #1: " . $validity[ $ret[0]['validity'] ] . " n"; echo "Text: $plaintext1 n";

echo " ";

// Verify #2 - Signierter Text und Signatur (??) $ret = $gpg->verify($signed, $signature, $plaintext2); echo "Fingerprint #2: " . $ret[0]['fingerprint'] . " n"; echo "Gültigkeit #2: " . $validity[ $ret[0]['validity'] ] . " n"; echo "Text: $plaintext2 n"; var_dump($ret);

?>

Bleibt noch die Frage, worin sich nun das Verschlüsseln einer Nachricht programmiertechnisch vom Signieren unterscheidet. Vergleichen wir Listing 3 mit Listing 4, stellen wir fest, dass wir auch hier mit unserem Schlüsselring arbeiten, nur dass wir in diesem Fall statt zu den Methoden setsignmode() und sign() zu den Methoden addSignKey(), addEncryptKey() und encryptsign() greifen. Deutlich unterscheidet sich hingegen das Ergebnis – wir erhalten statt einer Signatur eben die komplett verschlüsselte Mitteilung, die wir wie beim Signieren direkt in den Mailbody integrieren können. Entsprechend simpel ist das Vorgehen bei der Rückumwandlung von verschlüsseltem zu klar lesbarem Text: Einfach den eigenen privaten Key mittels Fingerprint und Passphrase laden und mittels decryptverify() auf den empfangenen Text anwenden. Bilden der vom Absender verwendete öffentliche Key des Empfängers sowie der private Schlüssel des Empfängers ein Paar, so wird der Empfänger, also unser Benutzer, die Nachricht lesen können. Zusätzlich geben wir ihm einige Informationen zur Gültigkeit. Dies ist wichtig, da Schlüssel auch von ihren Eigentümern zurückgezogen werden können (zum Beispiel, weil sie kompromittiert wurden oder der Eigentümer das dazugehörige Passwort, normalerweise als Mantra bezeichnet, vergessen hat) oder von allein ungültig werden, wenn sie von vornherein nur für temporäre Nutzung ausgelegt waren.

Listing 4: Verschlüsseln
 'unbekannt',
        gnupg::VALIDITY_UNDEFINED => 'undefiniert',
        gnupg::VALIDITY_NEVER     => 'nie',
        gnupg::VALIDITY_MARGINAL  => 'marginal',
        gnupg::VALIDITY_FULL      => 'voll',
        gnupg::VALIDITY_ULTIMATE  => 'ultimativ',
    );


 // GnuPG Objekt erzeugen
    $gpg = new gnupg();

 // Daten des Schluessels
    $fingerprint = '228E3AEC1CB09629FDBAABAF0665110C62881FF2';
    $passphrase  = 'demo';

 // Der zu verschluesselnde Text
    $plaintext = 'Hallo Welt - Bitte verschlüsseln';

 // Verschluesseln und Signieren vorbereiten
    $gpg->addSignKey($fingerprint,$passphrase);
    $gpg->addEncryptKey($fingerprint);

 // .. und ausfuehren
    $crypted = $gpg->encryptsign($plaintext);

    echo "Verschlüsselt: 
$crypted

";

// Entschluesseln vorberbeiten $gpg->addDecryptKey($fingerprint,$passphrase);

$clear='';

// Entschluesseln mit verifizierung $rc = $gpg->decryptverify($crypted,$clear);

echo "Klartext: "; echo "Fingerprint #2: ".$rc[0]['fingerprint']." n"; echo "Gültigkeit #2: ".$validity[ $rc[0]['validity'] ]." n"; echo "Text: $clear n";

?>

Fazit

Die GnuPG-Extension stellt sich bei pfleglicher Behandlung als durchaus praxistaugliches Werkzeug dar, um E-Mails zu verschlüsseln oder zu signieren, respektive, um verschlüsselte und signierte E-Mails zu verarbeiten. Das Handling gestaltet sich recht einfach und durch das übersichtliche Set an Funktionen ist es relativ einfach, sich mit der Extension vertraut zu machen. Die Einbindung der entsprechenden Funktionalitäten in bereits vorhandenen Code ist, beachtet man Kleinigkeiten wie den ausschließlichen Einsatz von HTTPS als Kommunikationsprotokoll zwischen Server und Client sowie die sinnvolle Absicherung privater Schlüssel auf dem Server, als unkompliziert zu bezeichnen.

Anzumerken sei noch, dass die Extension ebenfalls unter PHP 4 ab Version 4.3.x lauffähig ist und auch prozedurale Verarbeitung unterstützt. In Anbetracht der semantischen Ähnlichkeit zwischen beiden Versionen haben wir jedoch darauf verzichtet, diese Option ebenfalls ausführlich vorzustellen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -