Kryptologie Material Steganographie Monoalphabetisch Polyalphabetisch One-Time-Pad Kerckhoffs-Prinzip Public-Key RSA Feistel-Chiffren IDEA AES Langzahlarithmetik Gpg4win GnuPG Keysigning-Party Einweg-Funktion Schlüsseltausch Hash digitale Signatur Authentifizierung Zertifikate Chipkarten
Pfad: Startseite / Fächer / Informatik / Kryptologie / digitale Signatur
Autor: mk
17.09.2010 10:52
4987
digitale Signatur

Aufgabe

Kommt folgende Nachricht von Klaus Merkert? Wurde die Nachricht verändert?

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hallo liebe Lkler,

dieser Text kommt von mk.
Glaubt ihr das?

Viele Gruesse

mk
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (MingW32)

iD8DBQFDTQXKWik4TmXUdgARAtsMAJ9EKdHKlBYDtxbmclYwrXlR0WGzPgCeKxE8
M+A1EVxzrLV2B4TFmViAB8Y=
=eYfm
-----END PGP SIGNATURE-----

Aufgabe

Erzeuge eine Datei mmk.txt, die obige Nachricht enthält. Überprüfe die Signatur mit GnuPG. Welchen Schlüssel brauchst du dafür? mk-public-key.asc

gpg --verify mmk.txt

Verändere die Nachricht und kontrolliere erneut die Signatur.

Veranschaulichung der digitalen Signatur

Stephan Liell und Leon Schröder haben zur RSA-Signatur ein Comic dazu erstellt. Vorsicht, die Versionen sind je etwa 1,7 MB groß!

Praktisches Vorgehen mit WinPT

Signieren

Signatur prüfen

Beispiel mit krypto.py

Die Vorgänge beim Signieren und bei der Überprüfung der Signatur sollen realistisch durchgeführt werden. In einem ersten Schritt lassen wir uns von krypto.py ein Krypto-System mit genügend großem n erzeugen.

>>> (e,d,n) = ks129()
>>> e
4321
>>> d
105540278152442239156964372775953839871497419353759152866682265133093329419309672608340617518650663632935328045919317310113761185
>>> n
114381625757888867669235779976146612010218296721242362562561842935706935245733897830597123563958705058989075147599290026879543541

Jetzt wählen wir uns eine Original-Nachricht original, die unterschrieben werden soll.

>>> original = 'Überweise 100 €'
>>> original
'Überweise 100 €'

Die Original-Nachricht ist ein String, der zunächst in eine Codierungs-abhängige Byte-Folge eoriginal (encoded original) verwandelt wird.

>>> eoriginal = bytes(original,'utf8')
>>> eoriginal
b'\xc3\x9cberweise 100 \xe2\x82\xac'
>>> list(eoriginal)
[195, 156, 98, 101, 114, 119, 101, 105, 115, 101, 32, 49, 48, 48, 32, 226, 130, 172]

Aus der Byte-Folge eoriginal wird nun ein Hash des gewählten Verfahrens erzeugt. sha1 besteht aus 20 Bytes. Mit Hilfe der Python-Bibliothek hashlib lassen wir uns den Hash errechnen.

>>> import hashlib
>>> ho = hashlib.sha1(eoriginal) # ho = Hash Original
>>> ho.digest()
b'B#\xe7\x86\x8fG\xe4lx\xbda\x02M\xbc\xb9W\xa4[\xc3\xc9'
>>> L = list(ho.digest())
>>> L
[66, 35, 231, 134, 143, 71, 228, 108, 120, 189, 97, 2, 77, 188, 185, 87, 164, 91, 195, 201]
>>> len(L)
20

Zur Verschlüsselung muss aus der Bytefolge eine Zahl zho (Zahl-Hash-Original) erzeugt werden.

>>> zho = bton(ho.digest())
>>> zho
377594085682662273889667993265979539981762020297

Diese Zahl zho wird nun - und das ist vielleicht überraschend - mit dem geheimen Schlüssel (d,n) zu zhoc (Zahl-Hash-Original-ciffriert) verschlüsselt.

>>> zhoc = modpower(zho,d,n)
>>> zhoc
23356665507477810529268387333879606134859732585806800518553927498712741880026078527916394717542583907180086581967855821632025288

In der konkreten Anwendung müsste zur Versendung diese Zahl zhoc (Zahl-Hash-Original-ciffriert) in eine Bytefolge verwandelt werden. Diese Bytefolge wird dann zur Sicherheit oft base64-codiert. Der Empfänger muss dann daraus wieder die Zahl zhoc machen. Diese Schritte wollen wir uns der Klarheit wegen sparen.

Wie überprüft der Empfänger nun die Signatur?

Er erhält die unverfälschte - oder auch nicht - Bytefolge eempfangen. Die kann er mit dem Wissen um die Codierung in den String empfangen decodieren. Damit kann er das Original - oder seine Fälschung - lesen.

>>> # keine Fälschung
>>> eempfangen = eoriginal
>>> empfangen = str(eempfangen,'utf8')
>>> empfangen
'Überweise 100 €'

Nun wird die Signatur überprüft. Dazu muss die Zahl zee (Zahl-encoded-empfangen), die sich aus dem Hash von eempfangen ergibt, mit der Zahl zhod (Zahl-Hash-Original-deciffriert), die sich aus der Entschlüsselung der Signatur ergibt, verglichen werden.

Ganz wichtig: Jeder kann die Signatur überprüfen, da jeder den öffentlichen Schlüssel (e,d) kennt bzw. dazu Zugang hat.

>>> he = hashlib.sha1(eempfangen)
>>> he.digest()
b'B#\xe7\x86\x8fG\xe4lx\xbda\x02M\xbc\xb9W\xa4[\xc3\xc9'
>>> zee = bton(he.digest())
>>> zee
377594085682662273889667993265979539981762020297
>>> zhod = modpower(zhoc,e,n)
>>> zhod
377594085682662273889667993265979539981762020297

Der Empfänger berechnet also einen eigenen Hash und vergleicht ihn mit dem deciffrierten Hash.

>>> zee == zhod
True

Wer anderes als der Besitzer des geheimen Schlüssels kann dafür sorgen, dass dieses Verfahren aufgeht?

Aufgabe 1

Verändere das Original zu

'Überweise 1000€'

Dabei wurde nur eine Leerstelle zu einer Null. (ord - Python-Dokumentation)

>>> bin(ord(' '))
'0b100000'
>>> bin(ord('0'))
'0b110000'

Die Nachricht bleibt also genauso lang und wurde nur um 1 Bit verändert.

Die empfangene Byte-Folge wird also jetzt

>>> eempfangen = bytes('Überweise 1000€','utf8')
>>> eempfangen
b'\xc3\x9cberweise 1000\xe2\x82\xac'

sein. Überprüfe damit die Signatur.

Schlüssel signieren

In der Konsole würde man einen Schlüssel direkt mit GnuPG so zertifizieren: 1. Mit gpg --list-keys Angaben der User-ID den betreffenden Schlüssel mit der Key-ID Key-ID ermitteln. 2. Mit gpg --sign (exportierbar) oder --lsign (nur lokal) Key-ID den Zertifizierungsablauf starten. Besitzt man mehrere Hauptsignierschlüssel, kann ein bestimmter Schlüssel mit gpg -u Key-ID in der Konsole oder über die Option default-key Key-ID in der gpg.conf festgelegt werden.

Links