Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Security

Erstellen digitaler Signaturen mit Swift

by
Difficulty:IntermediateLength:LongLanguages:

German (Deutsch) translation by Katharina Nevolina (you can also view the original English article)

Der Hauptzweck einer digitalen Signatur besteht darin, die Integrität einiger Informationen zu überprüfen. Angenommen, Sie hatten eine Datei, die über das Netzwerk übertragen wurde, und möchten überprüfen, ob die gesamte Datei korrekt übertragen wurde. In diesem Fall würden Sie eine Prüfsumme verwenden.

„Eine Prüfsumme ist ein kleines Datum, das aus einem Block digitaler Daten abgeleitet wird, um Fehler zu erkennen, die möglicherweise während der Übertragung oder Speicherung aufgetreten sind“ - Wikipedia

Wie leiten wir diese Prüfsumme ab? Die beste Option ist die Verwendung eines Hash. Eine Hash-Funktion nimmt eine variable Datenmenge auf und gibt eine Signatur fester Länge aus. Zum Beispiel könnten wir eine Datei zusammen mit ihrem Hash online veröffentlichen. Wenn jemand die Datei herunterlädt, kann er dieselbe Hash-Funktion für seine Version der Datei ausführen und das Ergebnis vergleichen. Wenn die Hashes identisch sind, entspricht die kopierte oder heruntergeladene Datei dem Original.

Ein Hash ist auch eine Einwegfunktion. Angesichts der resultierenden Ausgabe gibt es keine rechnerisch realisierbare Möglichkeit, diesen Hash umzukehren, um die ursprüngliche Eingabe zu ermitteln. SHA, Secure Hash Algorithm, ist ein bekannter Standard, der sich auf eine Gruppe von Hash-Funktionen bezieht, die diese Eigenschaft haben, und auf bestimmte andere, die sie für digitale Signaturen nützlich machen.

Über SHA

SHA hat seit seiner Erstveröffentlichung viele Iterationen durchlaufen. Es ist jetzt bekannt, dass die erste und zweite Iteration, SHA-0 und SHA-1, große Schwächen aufweisen. Sie sind nicht mehr für Sicherheitsimplementierungen zugelassen: Sie sollten im Allgemeinen nicht für Anwendungen verwendet werden, die auf Sicherheit beruhen. Die SHA-2-Familie umfasst jedoch Versionen mit den Namen SHA-256 und SHA-512, die als sicher gelten. "256" und "512" beziehen sich einfach auf die resultierende Anzahl erzeugter Bits. Für dieses Tutorial verwenden wir SHA-512.

Hinweis: Ein anderer älterer populärer Hash-Algorithmus war MD5. Es wurde auch festgestellt, dass es erhebliche Mängel aufweist.

Die Verwendung von SHA eignet sich hervorragend, um zu überprüfen, ob Daten versehentlich beschädigt wurden. Dies verhindert jedoch nicht, dass ein böswilliger Benutzer die Daten manipuliert. Da eine Hash-Ausgabe eine feste Größe hat, muss ein Angreifer lediglich herausfinden, welcher Algorithmus für die Ausgabegröße verwendet wurde, die Daten ändern und den Hash neu berechnen. Was wir brauchen, sind einige geheime Informationen, die dem Mix beim Hashing der Daten hinzugefügt werden, damit der Angreifer den Hash ohne Kenntnis des Geheimnisses nicht neu berechnen kann. Dies wird als Authentifizierungscode für Hash-Nachrichten (Hash Message Authentication Code (HMAC)) bezeichnet.

HMAC

HMAC kann eine Information oder Nachricht authentifizieren, um sicherzustellen, dass sie vom richtigen Absender stammt und dass die Informationen nicht geändert wurden. Ein häufiges Szenario ist, wenn Sie mit einem Server mit einer Back-End-API für Ihre App sprechen. Es kann wichtig sein, sich zu authentifizieren, um sicherzustellen, dass nur Ihre App mit der API kommunizieren darf. Die API verfügt über eine Zugriffssteuerung für eine bestimmte Ressource, z. B. einen Endpunkt /register_user. Der Client müsste seine Anforderung beim Endpunkt /register_user signieren, um sie erfolgreich verwenden zu können.

Beim Signieren einer Anforderung ist es üblich, ausgewählte Teile der Anforderung, z. B. POST-Parameter und URL, zu einer Zeichenfolge zusammenzufügen. Vereinbarte Elemente zu nehmen und sie in eine bestimmte Reihenfolge zu bringen, wird als Kanonisierung bezeichnet. In HMAC wird die verknüpfte Zeichenfolge zusammen mit dem geheimen Schlüssel gehasht, um die Signatur zu erzeugen. Anstatt es als Hash zu bezeichnen, verwenden wir den Begriff Signatur genauso wie die Signatur einer Person im wirklichen Leben, um Identität oder Integrität zu überprüfen. Die Signatur wird der Anfrage des Clients als Anforderungsheader wieder hinzugefügt (normalerweise auch als "Signatur" bezeichnet). Eine Signatur wird manchmal als Message Digest bezeichnet, aber die beiden Begriffe können austauschbar verwendet werden.

Auf der API-Seite wiederholt der Server den Vorgang des Verbindens der Zeichenfolgen und des Erstellens einer Signatur. Wenn die Signaturen übereinstimmen, beweist dies, dass die App das Geheimnis besitzen muss. Dies beweist die Identität der App. Da bestimmte Parameter der Anforderung auch Teil der zu signierenden Zeichenfolge waren, wird auch die Integrität der Anforderung garantiert. Es verhindert, dass ein Angreifer beispielsweise einen Man-in-the-Middle-Angriff ausführt und die Anforderungsparameter nach seinen Wünschen ändert.

In diesem Code verwendet die CCHmac-Funktion einen Parameter für den Typ der zu verwendenden Hash-Funktion sowie zwei Byte-Strings und deren Länge - die Nachricht und einen geheimen Schlüssel. Verwenden Sie für die beste Sicherheit mindestens einen 256-Bit-Schlüssel (32 Byte), der von einem kryptografisch sicheren Zufallszahlengenerator generiert wurde. Um zu überprüfen, ob auf der anderen Seite alles ordnungsgemäß funktioniert, führen Sie das Beispiel aus, geben Sie den geheimen Schlüssel und die Nachricht auf diesem Remote-Server ein und überprüfen Sie, ob die Ausgabe identisch ist.

Sie können der Anforderung und der Signaturzeichenfolge auch einen Zeitstempel-Header hinzufügen, um die Anforderung eindeutiger zu gestalten. Dies kann der API helfen, Wiederholungsangriffe auszusortieren. Beispielsweise könnte die API die Anforderung löschen, wenn der Zeitstempel 10 Minuten veraltet ist.

Es ist zwar gut, sich an sichere SHA-Versionen zu halten, es stellt sich jedoch heraus, dass viele der Schwachstellen der unsicheren SHA-Versionen nicht für HMAC gelten. Aus diesem Grund wird SHA1 möglicherweise im Produktionscode verwendet. Aus Sicht der Öffentlichkeitsarbeit kann es jedoch schlecht aussehen, wenn Sie erklären müssen, warum es kryptografisch in Ordnung ist, SHA1 in diesem Zusammenhang zu verwenden. Viele der Schwächen von SHA1 sind auf sogenannte Kollisionsangriffe zurückzuführen. Codeprüfer oder Sicherheitsforscher können erwarten, dass Ihr Code unabhängig vom Kontext kollisionssicher ist. Wenn Sie modularen Code schreiben, mit dem Sie die Signaturfunktion in Zukunft gegen eine andere austauschen können, vergessen Sie möglicherweise, die unsicheren Hash-Funktionen zu aktualisieren. Daher werden wir uns weiterhin an SHA-512 als Algorithmus unserer Wahl halten.

Die HMAC-CPU-Operationen sind schnell, aber ein Nachteil ist das Problem des Schlüsselaustauschs. Wie lassen wir uns gegenseitig wissen, was der geheime Schlüssel ist, ohne dass er abgefangen wird? Möglicherweise muss Ihre API beispielsweise mehrere Apps oder Plattformen dynamisch zu einer Whitelist hinzufügen oder daraus entfernen. In diesem Szenario müssten sich Apps registrieren, und das Geheimnis müsste nach erfolgreicher Registrierung an die App weitergegeben werden. Sie könnten den Schlüssel über HTTPS senden und SSL-Pinning verwenden, aber selbst dann besteht immer die Sorge, dass der Schlüssel während des Austauschs gestohlen wird. Die Lösung für das Problem des Schlüsselaustauschs besteht darin, einen Schlüssel zu generieren, der das Gerät überhaupt nicht verlassen muss. Dies kann mithilfe der Kryptografie mit öffentlichen Schlüsseln erreicht werden, und ein sehr beliebter und akzeptierter Standard ist RSA.

RSA

RSA steht für Rivest-Shamir-Adleman (die Autoren des Kryptosystems). Dabei wird die Schwierigkeit ausgenutzt, das Produkt zweier sehr großer Primzahlen zu faktorisieren. RSA kann zur Verschlüsselung oder Authentifizierung verwendet werden, obwohl wir es in diesem Beispiel nur zur Authentifizierung verwenden werden. RSA generiert zwei Schlüssel, einen öffentlichen und einen privaten, die wir mit der SecKeyGeneratePair-Funktion ausführen können. Bei der Authentifizierung wird der private Schlüssel zum Erstellen der Signatur verwendet, während der öffentliche Schlüssel die Signatur überprüft. Bei einem öffentlichen Schlüssel ist es rechnerisch nicht möglich, den privaten Schlüssel abzuleiten.

Das nächste Beispiel zeigt, was Apple und alle gängigen Spielekonsolenhersteller bei der Verteilung ihrer Software verwenden. Angenommen, Ihr Unternehmen erstellt und liefert regelmäßig eine Datei, die Benutzer in den Dateifreigabebereich Ihrer App in iTunes ziehen. Sie möchten sicherstellen, dass die von Ihnen gesendeten Dateien niemals manipuliert werden, bevor Sie in der App analysiert werden. Ihr Unternehmen wird den privaten Schlüssel, mit dem es die Dateien signiert, aufbewahren und schützen. Im Bundle der App befindet sich eine Kopie des öffentlichen Schlüssels, mit dem die Datei überprüft wird. Da der private Schlüssel niemals übertragen oder in die App aufgenommen wird, kann ein böswilliger Benutzer seine eigenen Versionen der Dateien nicht signieren (außer in das Unternehmen einzudringen und den privaten Schlüssel zu stehlen).

Wir werden SecKeyRawSign verwenden, um die Datei zu signieren. Es wäre langsam, den gesamten Inhalt der Datei mit RSA zu signieren, daher wird stattdessen der Hash der Datei signiert. Darüber hinaus sollten die an RSA übergebenen Daten aufgrund einiger Sicherheitslücken vor dem Signieren gehasht werden.

In diesem Code haben wir die Funktion CC_SHA512 verwendet, um SHA-512 erneut anzugeben. (RSA wird im Gegensatz zu HMAC unsicher, wenn die zugrunde liegende Hash-Funktion unsicher ist.) Wir verwenden auch 4096 als Schlüsselgröße, die durch den Parameter kSecAttrKeySizeInBits festgelegt wird. 2048 ist die empfohlene Mindestgröße. Dies soll verhindern, dass ein leistungsfähiges Netzwerk von Computersystemen den RSA-Schlüssel knackt (mit Knacken meine ich das Faktorisieren des RSA-Schlüssels - auch als Faktorisierung eines öffentlichen Moduls bekannt). Die RSA-Gruppe hat geschätzt, dass 2048-Bit-Schlüssel einige Zeit vor 2030 knackbar werden könnten. Wenn Sie möchten, dass Ihre Daten über diesen Zeitraum hinaus sicher sind, ist es eine gute Idee, eine höhere Schlüsselgröße wie 4096 zu wählen.

Die generierten Schlüssel liegen in Form von SecKey-Objekten vor. Ein Problem bei der Implementierung von SecKey durch Apple besteht darin, dass es nicht alle wesentlichen Informationen enthält, aus denen ein öffentlicher Schlüssel besteht, sodass es sich nicht um ein gültiges DER-codiertes X.509-Zertifikat handelt. Das Hinzufügen der fehlenden Informationen zum Format für eine iOS- oder OS X-App, selbst für serverseitige Plattformen wie PHP, erfordert einige Arbeit und erfordert die Arbeit in einem Format namens ASN.1. Glücklicherweise wurde dies in iOS 10 mit neuen SecKey-Funktionen zum Generieren, Exportieren und Importieren von Schlüsseln behoben.

Der folgende Code zeigt Ihnen die andere Seite der Kommunikation - die Klasse, die einen öffentlichen Schlüssel über SecKeyCreateWithData akzeptiert, um Dateien mithilfe der SecKeyRawVerify-Funktion zu überprüfen.

Sie können dies ausprobieren und mit einem einfachen Test wie dem folgenden überprüfen, ob es funktioniert:

RSA hat einen Nachteil: Die Schlüsselgenerierung ist langsam! Die Zeit zum Generieren der Schlüssel hängt von der Größe des Schlüssels ab. Auf neueren Geräten dauert ein 4096-Bit-Schlüssel nur wenige Sekunden. Wenn Sie diesen Code jedoch auf einem iPod Touch der 4. Generation ausführen, kann dies etwa eine Minute dauern. Dies ist in Ordnung, wenn Sie die Schlüssel nur einige Male auf einem Computer generieren. Was passiert jedoch, wenn auf einem mobilen Gerät häufig Schlüssel generiert werden müssen? Wir können die Schlüsselgröße nicht einfach verringern, da dies die Sicherheit herabsetzt.

Was ist die Lösung? Nun, die Elliptic Curve Cryptography (ECC) ist ein aufstrebender Ansatz - eine neue Reihe von Algorithmen, die auf elliptischen Kurven über endlichen Feldern basieren. ECC-Schlüssel sind viel kleiner und schneller zu generieren als RSA-Schlüssel. Ein Schlüssel mit nur 256 Bit bietet ein sehr hohes Maß an Sicherheit! Um ECC nutzen zu können, müssen wir nicht viel Code ändern. Wir können unsere Daten mit derselben SecKeyRawSign-Funktion signieren und dann die Parameter anpassen, um den Elliptic Curve Digital Signature Algorithm (ECDSA) zu verwenden.

Tipp: Weitere Ideen zur RSA-Implementierung finden Sie in der SwiftyRSA-Hilfsbibliothek, die sich sowohl auf die Verschlüsselung als auch auf das Signieren von Nachrichten konzentriert.

ECDSA

Stellen Sie sich das folgende Szenario vor: Mit einer Chat-App können Benutzer private Nachrichten aneinander senden. Sie möchten jedoch sicherstellen, dass ein Gegner die Nachricht auf dem Weg zum anderen Benutzer nicht geändert hat. Mal sehen, wie Sie ihre Kommunikation mit Kryptographie sichern können.

Zunächst generiert jeder Benutzer ein Schlüsselpaar aus öffentlichen und privaten Schlüsseln auf seinem Mobilgerät. Ihre privaten Schlüssel werden im Speicher gespeichert und verlassen das Gerät nie, während die öffentlichen Schlüssel untereinander übertragen werden. Nach wie vor wird der private Schlüssel zum Signieren der gesendeten Daten verwendet, während der öffentliche Schlüssel zur Überprüfung verwendet wird. Wenn ein Angreifer während der Übertragung einen öffentlichen Schlüssel erfasst, kann lediglich die Integrität der ursprünglichen Nachricht des Absenders überprüft werden. Ein Angreifer kann eine Nachricht nicht ändern, da er nicht über den privaten Schlüssel verfügt, der zum Rekonstruieren der Signatur erforderlich ist.

Die Verwendung von ECDSA unter iOS bietet noch einen weiteren Vorteil. Wir können die Tatsache nutzen, dass derzeit nur elliptische Kurvenschlüssel in der sicheren Enklave des Geräts gespeichert werden können. Alle anderen Schlüssel werden im Schlüsselbund gespeichert, der seine Elemente im Standardspeicherbereich des Geräts verschlüsselt. Auf Geräten mit einem Gerät befindet sich die sichere Enklave getrennt vom Prozessor, und der Schlüsselspeicher wird in Hardware ohne direkten Softwarezugriff implementiert. Die sichere Enklave kann einen privaten Schlüssel speichern und damit arbeiten, um eine Ausgabe zu erzeugen, die an Ihre App gesendet wird, ohne jemals den tatsächlichen privaten Schlüssel durch Laden in den Speicher freizulegen!

Ich werde Unterstützung für das Erstellen des privaten ECDSA-Schlüssels in der sicheren Enklave hinzufügen, indem ich die Option kSecAttrTokenIDSecureEnclave für den Parameter kSecAttrTokenID hinzufüge. Wir können dieses Beispiel mit einem User-Objekt beginnen, das bei der Initialisierung ein Schlüsselpaar generiert.

Als nächstes werden wir einige Hilfs- und Beispielfunktionen erstellen. In der Klasse kann ein Benutzer beispielsweise eine Konversation initiieren und eine Nachricht senden. Natürlich würden Sie dies in Ihrer App so konfigurieren, dass es Ihr spezifisches Netzwerk-Setup enthält.

Als nächstes werden wir die eigentliche Unterzeichnung und Überprüfung durchführen. Im Gegensatz zu RSA muss ECDSA vor der Unterzeichnung nicht gehasht werden. Wenn Sie jedoch eine Funktion haben möchten, bei der der Algorithmus einfach ausgetauscht werden kann, ohne viele Änderungen vorzunehmen, ist es vollkommen in Ordnung, die Daten vor dem Signieren weiter zu hashen.

Dies überprüft die Nachricht sowie die „Identifizierung“ eines bestimmten Benutzers, da nur dieser Benutzer über seinen privaten Schlüssel verfügt.

Dies bedeutet nicht, dass wir den Schlüssel mit dem Benutzer im wirklichen Leben verbinden. Das Problem, einen öffentlichen Schlüssel einem bestimmten Benutzer zuzuordnen, ist eine andere Domäne. Während die Lösungen nicht in den Rahmen dieses Lernprogramms fallen, können Benutzer mit beliebten sicheren Chat-Apps wie Signal und Telegramm einen Fingerabdruck oder eine Nummer über einen sekundären Kommunikationskanal überprüfen. Ebenso bietet Pidgin ein Frage- und Antwortschema an, bei dem Sie eine Frage stellen, die nur der Benutzer kennen sollte. Diese Lösungen eröffnen eine ganze Welt von Debatten darüber, was der beste Ansatz sein sollte.

Unsere kryptografische Lösung überprüft jedoch, ob die Nachricht nur von jemandem gesendet werden kann, der im Besitz eines bestimmten privaten Schlüssels ist.

Lassen Sie uns einen einfachen Test unseres Beispiels durchführen:

OAuth und SSO

Wenn Sie mit Diensten von Drittanbietern arbeiten, werden Sie häufig andere übergeordnete Begriffe bemerken, die für die Authentifizierung verwendet werden, z. B. OAuth und SSO. Während es in diesem Tutorial um das Erstellen einer Signatur geht, werde ich kurz erklären, was die anderen Begriffe bedeuten.

OAuth ist ein Protokoll zur Authentifizierung und Autorisierung. Es fungiert als Vermittler, um das Konto einer anderen Person für Dienste von Drittanbietern zu verwenden, und zielt darauf ab, das Problem der selektiven Autorisierung des Zugriffs auf Ihre Daten zu lösen. Wenn Sie sich über Facebook bei Service X anmelden, werden Sie auf einem Bildschirm beispielsweise gefragt, ob Service X auf Ihre Facebook-Fotos zugreifen darf. Dies wird erreicht, indem ein Token bereitgestellt wird, ohne das Kennwort des Benutzers preiszugeben.

Single Sign-On (SSO) beschreibt den Ablauf, in dem ein authentifizierter Benutzer dieselben Anmeldeinformationen verwenden kann, um auf mehrere Dienste zuzugreifen. Ein Beispiel hierfür ist die Funktionsweise Ihres Google Mail-Kontos bei der Anmeldung bei YouTube. Wenn Sie in Ihrem Unternehmen mehrere verschiedene Dienste hatten, möchten Sie möglicherweise nicht für alle verschiedenen Dienste separate Benutzerkonten erstellen.

Schlussfolgerung

In diesem Tutorial haben Sie gesehen, wie Sie Signaturen mit den gängigsten Standards erstellen. Nachdem wir alle Hauptkonzepte behandelt haben, lassen Sie uns noch einmal zusammenfassen!

  • Verwenden Sie HMAC, wenn Sie Geschwindigkeit benötigen und sicher sind, dass der geheime Schlüssel sicher ausgetauscht werden kann.
  • Wenn die Schlüssel über ein Netzwerk übertragen werden müssen, ist es besser, RSA oder ECDSA zu verwenden.
  • RSA ist immer noch der beliebteste Standard. Der Überprüfungsschritt ist ziemlich schnell. Verwenden Sie RSA, wenn der Rest Ihres Teams bereits mit dem Standard vertraut ist oder ihn verwendet.
  • Wenn Sie jedoch auf einem langsamen Gerät ständig Schlüssel generieren müssen, verwenden Sie ECDSA. Die ECDSA-Überprüfung ist zwar etwas langsamer als die RSA-Überprüfung, dies ist jedoch nicht mit den vielen Sekunden vergleichbar, die über RSA für die Schlüsselgenerierung eingespart wurden.

Das war's für digitale Signaturen in Swift. Wenn Sie Fragen haben, schreiben Sie mir eine Nachricht in den Kommentaren und lesen Sie in der Zwischenzeit einige unserer anderen Tutorials zu Datensicherheit und App-Entwicklung in Swift!


Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.