7.5 iCloud
iCloud ist die Apple-Variante des Cloud-Computings, das seit einigen Jahren in nahezu jedem Gespräch unter IT-Fachleuten erwähnt wird. Cloud-Computing oder einfach nur die Cloud, wie die coolen Leute sagen, ist der große Hype unserer Zeit. Wer schon länger im IT-Geschäft ist, wird bereits so manchen Hype erlebt haben. Die »Demokratisierung« der IT durch Windows in den 1990er-Jahren war noch relativ harmlos. Schlimm wurde es zur Jahrtausendwende. Wer am neuen Markt sein Geld in heute nicht mehr existierende Internet-Firmen investiert hatte, der wird wissen, was ein IT-Hype ist. Leider hat der Neue Markt viele Nicht-ITler auf IT aufmerksam gemacht, sodass seitdem jede auch noch so unscheinbare Neuerung in der IT mit großem Hallo hochgejubelt wird, als sei es die Erfindung der Weltformel.
Wer erinnert sich nicht an die Zeit, in der XML bekannt wurde? Jeder, der das Wort Computer fehlerfrei buchstabieren konnte und mit Anzug und Krawatte bewaffnet war, sah in XML die Lösung aller Probleme der Menschheit. »Leute, es ist nur eine Auszeichnungssprache!« wollte man diesen armen Menschen zurufen. Vergebens.
Dann kam das Web 2.0 über uns. Eine nette Idee, die aber von den einschlägigen Protagonisten ebenfalls so umfassend in Beschlag genommen wurde, dass sich jedem ITler beim Hören des Begriffs Web 2.0 die Nackenhaare hochstellen.
Abbildung 7.35 Der Web-Teil der iCloud
Und genauso ist es mit der Cloud. Cloud-Computing ist heute in den allermeisten Fällen im professionellen IT-Umfeld nicht mehr als ein falsch verstandenes Synonym für Outsourcing. Man mietet als Firma Kapazitäten in Form von Rechenzentren, Diensten oder Anwendungen und lagert dorthin eigene IT-Funktionalität aus. Eigentlich kommt die Idee des Cloud-Computings aus den Anforderungen großer Internet-Firmen wie Amazon oder Google. Diese Firmen haben prinzipbedingt mit großen Lastspitzen zu kämpfen, je nachdem, welches Ereignis die Benutzer dazu bringt, auf die Web-Präsenzen zuzugreifen.
Außer bei globalen Ereignissen, wie z. B. den Terroranschlägen vom 11. September 2001, bei denen so gut wie alle Nachrichtenseiten rund um dem Globus unter der Last der Anfragen zusammengebrochen sind, haben globale Anbieter wie Amazon oder Google in der Regel mit lokal begrenzten Lastspitzen zu kämpfen. Um solche Lastspitzen ohne Ausfälle oder Einschränkungen in der Erreichbarkeit und Funktionalität meistern zu können, wurde die Idee des Cloud-Computings geboren. Dahinter verbirgt sich zunächst nicht mehr als das dynamische, bedarfsgerechte Verwalten und Verteilen von IT-Ressourcen über ein Netzwerk – in der Regel das Internet. Es gibt einen großen Pool verteilter Ressourcen, und je nachdem, wo gerade aufgrund einer Lastspitze Ressourcen benötigt werden, weist man dort entsprechende Ressourcen aus dem Pool zu.
Clouds gibt es in verschiedenen Geschmacksrichtungen. Sie werden im Zusammenhang mit Cloud vielleicht über die Begriffe IaaS (Infrastructure as a Service), SaaS (Software as a Service) und PaaS (Platform as a Service) gestolpert sein. Dahinter verbirgt sich, dass Sie Cloud-Ressourcen als nackte Infrastruktur haben (IaaS) haben können, als Plattform für eigene Anwendungen (PaaS) oder als fertige Anwendungen in der Cloud (SaaS). Das ist für Leute, die IT-Outsourcing kennen, nichts Neues. Neu daran sind nur die mittlerweile große Verbreitung und die dynamische Verwaltung von Ressourcen.
Wenn Sie Google Docs oder Microsoft Office 365 kennen, dann kennen Sie bereits Clouds aus dem Bereich SaaS. Die bekannteste Cloud aus dem Bereich IaaS ist wohl die Elastic Compute Cloud von Amazon, Amazon EC2. PaaS-Vertreter sind die Google App Engine und Microsoft Azure. Als Apple-Benutzer haben Sie vielleicht mit Mobile Me gearbeitet. Die Online-Versionen der Mail- und Kalender-App sind typische Vertreter von SaaS; die iDisk und die Anbindung von iPhoto und iWeb fallen eher in den Bereich PaaS.
Ein weiterer, sich rasch verbreitender Anwendungsfall für Cloud-Computing ist der Speicher im Netz. Diese Idee, die die beiden Wolfgangs vom legendären Computerclub bereits Anfang der 1990er-Jahre hatten, hat sich für Apple-Benutzer in Form der iCloud manifestiert. Diese ist zwar, vom Browser aus bedient, eine SaaS, denn auch in der iCloud gibt es die Online-Pendants zur Mail-, Kalender-, Kontakte- und iPhone-Suchen-App. Für App-Entwickler ist aber insbesondere der PaaS-Teil interessant, der einen Synchronisations- und Speicherdienst anbietet, über den Apps und Mac-Programme Daten in der iCloud ablegen und über mehrere Geräte synchronisieren können.
Sie können Dokumente aus einer App direkt in der iCloud ablegen und die iCloud auf diese Weise als Speicherplatz verwenden. In den Dokumenten können Sie Suchoperationen ausführen und die Dokumente über mehrere Endgeräte synchronisieren.
Darüber hinaus beherrscht die iCloud Key-Value-Storing. Sie können kleine Datenpakete direkt aus Objekten heraus in die Cloud legen. Die Idee dahinter ist einleuchtend. Stellen Sie sich beispielsweise ein Spiel vor, das den aktuellen Spielstand über Key-Value-Storing in die Cloud legt, sodass Sie das Spiel auf iPad, iPhone und Mac spielen können und immer an der letzten Stelle weitermachen, egal an welchem Gerät Sie gerade sind.
7.5.1 Denkanstöße

Bevor es ans Programmieren geht, müssen wir an dieser Stelle noch kurz den ermahnenden Zeigefinger heben. Das Killer-Argument für die Cloud ist stets Verfügbarkeit. Durch die Verteilung auf unglaublich viele Rechner, Netzwerke, Komponenten und Rechenzentren ist der Ausfall einer Cloud so gut wie ausgeschlossen. Bedauerliche Einzelfälle gibt es natürlich immer wieder. Auch die großen Anbieter hatten bereits einen oder mehrere Ausfälle ihrer Cloud-Infrastruktur zu verzeichnen – zum Teil mit tagelangen Einschränkungen der Erreichbarkeit, zum Teil auch mit größeren Datenverlusten:
- Amazon: Ausfall von mehreren Tagen und Datenverlust durch Fehler in einem Rechenzentrum
- Google: Versehentliche Löschung der Inhalte von über 150.000 Postfächern des Dienstes Googlemail durch einen Software-Fehler
- Microsoft: Ein Hausmeister-Skript, das verdächtige Konten löschen sollte, war übereifrig und hat direkt über 17.000 »normale« Konten mit gelöscht.
- Microsoft und Amazon: teilweiser Ausfall der Cloud-Dienste durch Blitzschlag in Irland
- Persönliche Erfahrung eines der beiden Autoren: versehentliches Löschen des iTunes-Match-Dienstes inklusive der gesamten darin abgelegten Musik-Bibliothek durch einen Apple-Mitarbeiter
Ausfallsicherheit und die sichere Ablage von Daten in der Cloud sind daher in der Theorie nette Ideen, aber wie bei allen Ideen, an deren Umsetzung Menschen beteiligt sind, sieht die Praxis weniger rosig aus. Dies sollten Sie stets im Hinterkopf behalten, wenn Sie Ihre App an die iCloud anbinden. Es gibt unendlich viele Anwendungsfälle, in denen die Verwendung von Cloud-Diensten sinnvoll sein kann, genauso gibt es unendlich viele Anwendungsfälle, in denen die Verwendung von Cloud-Diensten nicht sinnvoll ist. [http://goo.gl/5K5Uc] Entscheiden Sie weise!
Neben der Verfügbarkeit von Rechenleistung und gespeicherten Daten kommen bei der Verwendung von Cloud-Diensten noch zwei weitere Aspekte ins Spiel, nämlich Datenschutz und Datensicherheit. Die Natur der Cloud beinhaltet, dass Sie nie genau wissen, wo sich die Rechner befinden, die sich Ihnen gegenüber als iCloud ausgeben. Die Geräte können in Irland stehen (Europa), aber auch in den USA oder sonstwo, wo Apple gerade Ressourcen für die iCloud verfügbar hat.
Da die iCloud ihrerseits zurzeit auf den Clouds von Amazon und Microsoft läuft [http://goo.gl/8O8Rk] und Apple überdies ein US-amerikanisches Unternehmen ist, müssen Sie beim Speichern personenbezogener Daten in der iCloud Acht geben. Die USA sind zwar dem Safe-Harbor-Abkommen beigetreten, Datenschutz wird in den USA aber trotzdem auf einem wesentlich anderen Niveau betrachtet, als es in Geltungsbereich des Bundesdatenschutzgesetzes der Fall ist. Je nachdem, welche Daten Ihre App verarbeitet, können Sie durch die Verwendung der iCloud-Funktionalität daher unter Umständen einen Verstoß gegen das Bundesdatenschutzgesetz begehen. Im Zweifelsfall sollten Sie vorher juristischen Rat einholen.
Laut Apple sind die in der iCloud abgelegten Daten mit AES128 verschlüsselt. [http://support.apple.com/kb/HT4865] Bei korrekter Implementierung und bei der Verwendung eines starken Passworts bietet dieser Algorithmus ausreichende Sicherheit. Offen bleibt aber die Frage, mit welchem Schlüssel die Daten verschlüsselt werden und ob nur der Benutzer oder auch Apple sie entschlüsseln kann. Fraglich ist auch, ob Apple seine eigene Dokumentation befolgt und AES im CBC-Modus ohne Initialisierungsvektor verwendet. [http://goo.gl/qLQpR]
Die iCloud begegnet Ihnen als App-Programmierer an zwei Stellen. Die erste Stelle ist die explizite Verwendung der iCloud in Ihrer eigenen App. Die zweite Stelle ist die iCloud-Backup-Funktion von iOS. Selbst wenn Sie Ihrer App keine iCloud-Anbindung spendieren, wird iOS dennoch alle Dateien Ihrer App in der iCloud sichern, wenn der Benutzer das iCloud-Backup in den Systemeinstellungen aktiviert hat.
7.5.2 iCloud in der Praxis

Damit eine App mit der iCloud arbeiten kann, benötigt sie eine entsprechend konfigurierte App ID. Melden Sie sich dafür im iOS Developer Center an, und öffnen Sie das Provisioning Portal. Unter dem Navigationspunkt App IDs können Sie die neue App ID anlegen. Wählen Sie rechts oben den Button New App ID, und geben Sie im sich öffnenden Dialogfeld die Beschreibung und den Bundle Identifier an (siehe Abbildung 7.36).
Abbildung 7.36 Erzeugen einer neuen App ID
Nach Beenden des Dialogs erscheint die neue App ID in der Übersicht der App IDs unten auf der Seite (siehe Abbildung 7.37).
Abbildung 7.37 Die Übersicht über die neue App ID
Die mit grünen Icons versehenen Features sind standardmäßig aktiviert, das sind In-App Purchase und Game Center (ein Schelm, wer dabei Arges denkt). Aktivieren Sie die iCloud-Berechtigung über den Configure-Link rechts neben der iCloud-Zeile.
Beim Aktivieren der iCloud-Berechtigung im folgenden Dialog erscheint die Warnung, dass alle neuen Provisionsprofile für diese App ID die Berechtigung für den Zugriff auf die iCloud erhalten (siehe Abbildung 7.38). Bereits bestehende Provisionsprofile enthalten die Berechtigung naheliegenderweise noch nicht und müssen gegebenenfalls ersetzt werden. Dieser Hinweis ist natürlich nur dann relevant, wenn Sie eine bereits bestehende App ID nachträglich mit iCloud-Berechtigungen ausstatten.
Abbildung 7.38 Hinweis für die Verwendung von Provisionsprofilen
Nach dem Schließen dieses Dialogfensters sollte in der Übersicht über die App ID die Zeile iCloud ebenfalls durch grüne LEDs markiert sein (siehe Abbildung 7.39).
Abbildung 7.39 Die iCloud-Verwendung ist aktiviert.
Die neue App ID müssen Sie nun noch mit einem Provisionsprofil verbinden. Öffnen Sie dazu über den Navigationspunkt Provisioning die Verwaltung der Provisionsprofile, und erstellen Sie über den Button New Profile rechts ein neues Provisionsprofil. Wählen Sie dabei im Auswahlfeld App ID (siehe Abbildung 7.40) die eben erzeugte App ID aus.
Abbildung 7.40 Ein neues Provisionsprofil für die iCloud-App
Unmittelbar nach dem Erzeugen des neuen Provisionsprofils ist dieses noch nicht für den Download verfügbar. Warten Sie einige Augenblicke, laden Sie die Seite mit den Provisionsprofilen erneut, und laden Sie das neue Profil dann herunter (siehe Abbildung 7.41).
Abbildung 7.41 Wir bitten um etwas Geduld.
Nach dem Erzeugen des Provisionsprofils können Sie im Xcode-Organizer im Abschnitt Provisioning Profiles das neue Provisionsprofil aus dem Portal importieren, indem Sie auf den Refresh-Button unten rechts im Fenster klicken (siehe Abbildung 7.42). Der alte Weg über einen manuellen Download und händischen Import in Xcode ist nicht mehr notwendig.
Ab jetzt können Sie mit der eigentlichen Arbeit anfangen – der Programmierung Ihrer App. Legen Sie dazu ein neues Xcode-Projekt an (siehe Abbildung 7.43), und achten Sie bei der Benennung der App darauf, exakt den in der App ID vergebenen Namen zu verwenden, auch die Groß- und Kleinschreibung ist dabei wichtig!
Abbildung 7.42 Automatischer Import des Provisionsprofils im Organizer
Provisionsprofile und Geräte
Wenn Sie ein Provisionsprofil auf mehr als einem Gerät verwenden möchten, müssen Sie dies im Provisionsprofil entsprechend angeben. Insbesondere bei iCloud-Apps ist das Testen auf mehreren Geräten wichtig, um zu sehen, ob sich die Daten wie gewünscht über die iCloud an alle Geräte verteilen. Wenn Sie im Nachhinein ein Gerät zu einem bereits erstellten Provisionsprofil hinzufügen möchten, können Sie dies im iOS Provisioning Portal. Anschließend müssen Sie das neue Provisionsprofil erneut in Xcode importieren.
Abbildung 7.43 Der Name der App muss zur App ID passen.
Öffnen Sie anschließend die Eigenschaften des Targets iclous, und scrollen Sie im Summary-Tab bis zum Abschnitt Entitlements. Aktivieren Sie dort die Checkbox Entitlements. Im rechts daneben befindlichen Dropdown-Feld Use Entitlements File wählen Sie anschließend iclous aus (wenn es nicht bereits automatisch ausgewählt ist).
Aktivieren Sie anschließend ebenfalls die Checkboxen iCloud und Key-Value-Store. Im Textfeld rechts neben Key-Value-Store erscheint beim Aktivieren der Checkbox automatisch der Bundle Identifier (hier: de.cocoaneheads.iclous). Im Feld Ubiquity Containers fügen Sie über die Plus-Taste einen neuen Container hinzu. Xcode gibt diesem Container automatisch denselben Namen wie dem Key-Value-Store. Nach diesen Einstellungen sollte die App sich ohne Fehler übersetzen und auf einem angeschlossenen iDevice starten lassen.
Damit eine App die iCloud verwenden kann, muss auf dem betreffenden iDevice ein iCloud-Konto eingerichtet und in den Einstellungen der iCloud der Punkt Dokumente & Daten aktiviert sein (siehe Abbildung 7.44).
Abbildung 7.44 iCloud-Einstellungen in iOS
Da nicht jeder Benutzer die iCloud eingerichtet oder aktiviert hat, ist das Erste, was Sie in einer iCloud-fähigen App machen müssen, auf das Vorhandensein einer funktionierenden iCloud-Verbindung zu prüfen. Das können Sie, indem Sie im AppDelegate (KMRAppDelegate.m) am Ende der Methode application didFinishLaunchingWithOptions: den folgenden Code einfügen:
01 dispatch_async(dispatch_get_global_queue
(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
02 NSURL *iCloud = [[NSFileManager
defaultManager] URLForUbiquityContainerIdentifier:nil];
03 if (iCloud) {
04 dispatch_async(dispatch_get_main_queue(), ^{
05 NSLog(@"iCloud-Verzeichnis: %@", iCloud);
06 [self setTheCloud:YES];
07 });
08 }
09 else {
10 dispatch_async(dispatch_get_main_queue(), ^{
11 NSLog(@"No iCloud");
12 [self setTheCloud:NO];
13 });
14 }
15 });
Listing 7.24 Prüfen auf iCloud-Fähigkeit
Die eigentliche Kommunikation mit der iCloud erfolgt über die NSFilemanager-Methode URLForUbiquityContainerIdentifier in Zeile 2. Diese Methode ist mit iOS 5 neu zu NSFileManager hinzugekommen und liefert den iCloud-Container zurück, in dem die App ihre Daten speichern kann. Liefert der Aufruf dieser Methode nil zurück, funktioniert die Verbindung mit der iCloud nicht. Der Aufruf ist also ein verlässlicher Indikator dafür, ob eine App mit der iCloud arbeiten kann oder nicht.
Da die Methode, wie alle Netzwerk-Verbindungen, einige Zeit benötigen kann, bis sie zurückkehrt, sollten Sie sie nicht aus dem Haupt-Thread der App aus aufrufen, denn solange die Methode auf eine Antwort von der iCloud wartet, ist der Thread blockiert. Im Falle des Haupt-Threads wäre das also die gesamte App. Daher ist der Aufruf in einen Block gekapselt (siehe Kapitel 2, »Grundlagen«), der die Methode in eine Dispatch Queue von Grand Central Dispatch legt, sie dort ausführt und anschließend zum Haupt-Thread der App zurückkehrt.
Grand Central Dispatch
Grand Central Dispatch (GCD) ist eine von Apple entwickelte Bibliothek zum einfachen Verteilen von Programmoperationen auf mehrere Prozessoren bzw. Prozessorkerne. Nebenläufige Programmierung, also das Erzeugen von mehreren Ausführungssträngen, ist seit jeher ein kompliziertes und fehlerträchtiges Thema. Mit der flächendeckenden Verbreitung von Mehrkern-Prozessoren ist die nebenläufige Programmierung aber eine Grundanforderungen in der Software-Entwicklung geworden.
Apple hat mit GCD eine Bibliothek geschaffen, mit der ein Programmierer unabhängig von der tatsächlichen Arbeit nebenläufig programmieren kann. Er muss die Aufgaben, die auf verschiedene Ausführungsstränge aufgeteilt werden sollen, nur an GCD übergeben, und GCD kümmert sich um die weitere Verteilung. Der Programmierer übergibt Tasks, wie in ##Listing 7.24((bitte prüfen)) gezeigt, und muss sich nicht um Begriffe und Techniken wie Threads und Prozesse kümmern; das erledigt GCD.
Wenn Sie die App übersetzen und auf einem iDevice mit aktiviertem iCloud-Konto ausführen, werden Sie die folgende Ausgabe erhalten:
[...] iclous[1060:907] iCloud-Verzeichnis:
file://localhost/private/var/mobile/Library/
Mobile%20Documents/5397MKMK89~de~cocoaneheads~iclous/
Das Verzeichnis, das außerhalb der Sandbox der App liegt, ist das zentrale iCloud-Verzeichnis für diese App, der Ubiquity-Container. Der iCloud-Dienst von iOS überträgt Dateien aus diesem Verzeichnis in die iCloud und überträgt umgekehrt Dateien aus der iCloud zurück in dieses Verzeichnis; es ist also eine lokale Kopie der Daten in der iCloud. Als App-Programmierer kommunizieren Sie also lediglich indirekt über Datei-Operationen mit der iCloud, nicht direkt über Netzwerkverbindungen.
Der Ubiquity-Container ist das einzige Verzeichnis außerhalb der Sandbox, in das eine App schreiben darf. Über die Vergabe von Entitlements geben Sie einer App die Erlaubnis, in den betreffenden Ubiquity-Container zu schreiben. Sie können einen Ubiquity-Container auch von verschiedenen Apps nutzen lassen. Dabei ist dann Vorsicht geboten, um den ungewollten Zugriff einer App auf die Daten einer anderen App im selben Ubiquity-Container zu vermeiden. Für den Normalfall sind Sie gut damit beraten, jeder App ihren eigenen Ubiquity-Container zuzuweisen.
Die eindeutige Festlegung des Ubiquity-Containers für eine App dient noch einem anderen Zweck als der Trennung von verschiedenen Apps. Dadurch, dass eine App auf allen Geräten denselben Ubiquity-Container verwendet, ist der Datenaustausch per iCloud ganz profan.
7.5.3 Key Value Storage

Die wohl praktischste Funktion der iCloud ist der Key Value Storage. Dahinter verbirgt sich nichts anderes als die von NSUserDefaults bekannte Technik, Daten über Schlüssel zu speichern. Der Unterschied ist, dass diese Daten beim Speichern in der iCloud umgehend allen Geräten zur Verfügung stehen, auf denen die betreffende App installiert ist. Sie können als Programmierer auf diese Weise also ganz bequem Daten innerhalb einer App über verschiedene Endgeräte synchronisieren, ohne dass Sie sich Gedanken über ein Protokoll und dessen Synchronisierung machen müssen.
Erweitern Sie den im vorherigen Abschnitt implementierten GDC-Aufruf zur Prüfung auf iCloud-Fähigkeit um die folgenden Zeilen:
NSString *theLocalText = @"Lorem ipsum dolor sit
amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna
aliquyam erat, sed diam voluptua.";
// Key Value Store
NSUbiquitousKeyValueStore *cloudStore =
[NSUbiquitousKeyValueStore defaultStore];
if([[cloudStore stringForKey:CLOUDKEY] length] == 0){
NSLog(@"Kein Text in der Cloud gefunden");
[cloudStore setString:theLocalText forKey:CLOUDKEY];
[cloudStore synchronize];
} else {
NSLog(@"Die Cloud sagt: %@", [cloudStore
stringForKey:CLOUDKEY]);
}
Listing 7.25 Key Value Storage in der iCloud
Den Bezeichner CLOUDKEY definieren Sie direkt hinter den import-Anweisungen und vor der @implementation-Anweisung im Kopf der Implementierungsdatei:
[...]
NSString * const CLOUDKEY = @"iClous";
[...]
Wie bei der Verwendung von NSUserDefaults legen Sie über den Aufruf der Klasse NSUbiquitousKeyValueStore ein iCloud-Handle an, mit dem Sie anschließend die gewünschten Operationen ausführen können:
NSUbiquitousKeyValueStore *cloudStore =
[NSUbiquitousKeyValueStore defaultStore];
Der Zugriff auf Einträge in diesem iCloud-Handle erfolgt über die Verwendung der betreffenden Typ-Accessoren. Für das Auslesen eines Datums vom Typ NSString kommt daher stringForKey zum Einsatz. Der Key ist dabei der eindeutige Schlüssel, der das betreffende Datum identifiziert:
if([[cloudStore stringForKey:CLOUDKEY] length] == 0){
Diese Anweisung prüft, ob in der iCloud für den Schlüssel iClous ein String gespeichert ist. Ist dies der Fall, gibt die App den Text in der else-Anweisung aus, ansonsten setzt sie den Wert über die Methode setString des NSUbiquitousKeyValueStore:
[cloudStore setString:theLocalText forKey:CLOUDKEY];
[cloudStore synchronize];
Listing 7.26 Setzen eines Key Values in der iCloud
Die Methode synchronize des NSUbiquitousKeyValueStore dient zum expliziten Synchronisieren der lokal gespeicherten Daten mit der iCloud. Der iCloud-Dienst auf dem iDevice synchronisiert neue Daten nicht automatisch nach ihrer Erstellung mit der iCloud, sondern verwendet sein eigenes, von Apple festgelegtes Intervall. Über die Methode synchronize können Sie die Synchronisierung mit der iCloud anstoßen. Apple weist allerdings darauf hin, dass diese Methode sparsam zu verwenden ist, nämlich beim Start einer App und beim Wechseln vom Hintergrund in den Vordergrund. Für den normalen Betrieb ist dies sicher ausreichend, aber gerade beim Testen ist das Warten auf die automatische Synchronisierung sehr umständlich. Sie sollten den Gebrauch von synchronize vor dem Deployment Ihrer App in den App Store also auf die von Apple empfohlenen Anwendungsfälle beschränken.
7.5.4 Verschlüsselung in der Cloud
Wenn Sie sich auf die Angaben von Apple zur Verschlüsselung der Daten in der iCloud nicht verlassen wollen oder können, z. B. weil eine (gesetzliche) Vorgabe Sie daran hindert, personenbezogene Daten außerhalb des Geltungsbereiches des Bundesdatenschutzgesetzes zu speichern, bleibt Ihnen nichts anderes übrig, als die Daten »zu Fuß« zu verschlüsseln, bevor Sie diese in der iCloud speichern.
Das Mittel der Wahl dafür ist die CommonCrypto-Bibliothek aus dem Security Framework. Leider ist CommonCrypto denkbar schlecht dokumentiert. Die Xcode-Dokumentation dazu hält lediglich Manpages für den Programmierer bereit. Manpages sind Relikte aus der Steinzeit der Computer und werden üblicherweise über das Terminal eines Unix-Systems angesprochen. Freundlicherweise enthält die Xcode-Dokumentation zu CommonCrypto auch einen entsprechenden Hinweis:
This document is a Mac OS X manual page. Manual pages are a command-line technology for providing documentation. You can view these manual pages locally using the man(1) command. These manual pages come from many different sources, and thus, have a variety of writing styles.
For more information about the manual page format, see the manual page for manpages(5).
Leider enthält das Beispiel-Projekt zur Verwendung von CommonCrypto einige Fehler bei der Verwendung der kryptografischen Funktionen und ist nicht mehr auf einem aktuellen Stand, sodass man als Programmierer leider komplett im Regen steht, wenn man sich mit der manuellen Verschlüsselung von Daten unter iOS beschäftigen möchte.
Zur Verschlüsselung von Daten in der iCloud bietet sich die symmetrische Verschlüsselung mit dem Algorithmus AES an. AES ist ein Industriestandard und in den USA für alle Geheimhaltungsstufen zugelassen. Symmetrische Verschlüsselung bedeutet, dass der Vorgang des Verschlüsselns mit demselben Schlüssel erfolgt wie der des Entschlüsselns. Dies ist die bereits aus der Antike bekannte »natürliche« Form der Kryptografie.
Innerhalb einer App, die ihre Daten selbst ver- und entschlüsselt, ist symmetrische Verschlüsselung eine gute Wahl. Der dieser Methode inhärente Nachteil, dass für beide Operationen (also für die Verschlüsselung sowie für die Entschlüsselung) derselbe Schlüssel vorhanden sein muss, ist in einer App kein Problem. Bei der Übertragung von verschlüsselten Daten an Dritte im Internet (z. B. über verschlüsselte E?Mails) ist das Problem des Schlüssels erheblich, sodass bei solchen Konstellationen die asymmetrische Verschlüsselung zum Einsatz kommt, bei der Ver- und Entschlüsselung über verschiedene Schlüssel erfolgen.
Fügen Sie für die Verwendung von CommonCrypto zunächst das Security Framework zum iCloud-Projekt hinzu (siehe Abbildung 7.45).
Abbildung 7.45 Hinzufügen des Security Frameworks zum Projekt
Anschließend fügen Sie eine neue Klasse zum Projekt hinzu. In dieser Klasse werden Sie im Folgenden die gesamte Kryptografie-Funktionalität implementieren. Geben Sie der Klasse den Namen CryptoUtils, und fügen Sie in der Header-Datei die folgenden Propertys und Methoden ein:
#import <Foundation/Foundation.h>
@interface CryptoUtils : NSObject
-(NSData *)encryptData:(NSData *)clearText;
-(id)initWithPassword:(NSString *)thePassword;
@property (retain) NSData *salt;
@property (retain) NSData *iv;
@property (retain) NSString *password;
@property (retain) NSData *cryptKey;
@end
Listing 7.27 Die Header-Datei von »CryptoUtils«
Die vier Propertys der Klasse sind die essenziellen Bestandteile für die sichere Verschlüsselung von Daten mit AES. Das Passwort (password) ist das vom Benutzer vergebene Passwort, aus dem sich der Schlüssel für die Verschlüsselung ableitet. Aus Abschnitt 7.4.1, »Authentisierung«, wissen Sie bereits, dass die Verwendung des reinen Passwortes Gefahren birgt. Daher wird das Passwort mit einem Salt-Wert versehen (siehe dazu auch die Top 25 in Abschnitt 7.3.1, »Erstellen eines Datenflussdiagramms«). Die auch in CommonCrypto verfügbaren Password-Based Key Derivation Function 2 (PBKDF2) erzeugt aus dem aus Salt und Passwort bestehenden Wert dann den eigentlichen Schlüssel (cryptKey), der bei der Ver- und Entschlüsselung an CommonCrypto übergeben wird.
PBKDF2
Die Password-Based Key Derivation Function 2 ist seit iOS 5 Bestandteil von iOS. PBKDF2 erzeugt durch das wiederholte Anwenden einer Hashfunktion auf die Kombination von Salt und Passwort einen Schlüssel, der wesentlich besser gegen Brute-Force-Angriffe geschützt ist als das reine Passwort (mit Salt), da ein Angreifer diese Iterationen alle rückwärts durchführen müsste, um aus dem Schlüssel auf das ursprüngliche Passwort schließen zu können.
Die Anzahl der Wiederholungen der Hashfunktionen ist bei CommonCrypto frei definierbar. Ein guter Wert sind 10.000 Runden. Diese benötigen auf einem iPhone 4 ca. 900 Millisekunden. Das ist ein guter Wert, wenn man dazu die gestiegene Sicherheit gegenüber Passwort-Crackern bedenkt.
AES ist eine Blockchiffre, was bedeutet, dass die zu verschlüsselnden Daten in Blöcke aufgeteilt werden. Der Modus der Wahl bei AES, weil er die höchste Sicherheit bietet, ist der Cipher Block Chaining Mode (CBC) [https://secure.wikimedia.org/wikipedia/de/wiki/Cipher_Block_Chaining_Mode]. Dieser Modus teilt den zu verschlüsselnden Klartext in Blöcke auf und verschlüsselt jeden Block mit dem eigentlichen Schlüssel und dem Ergebnis der Verschlüsselung des vorhergehenden Blocks. Da es beim ersten Block einer Verschlüsselung keinen vorherigen Block gibt, müssen Sie für diesen Block einen zufälligen Wert verwenden. Dieser Wert ist der sogenannte Initialisierungsvektor (IV), der in CryptoUtils in der gleichnamigen Property abgelegt wird.
Salt und IV – geheim oder nicht?
Das Salt und der Initialisierungsvektor sind zwar essenzielle Parameter für eine wirksame Verschlüsselung, entgegen der häufig vertretenen Meinung gibt es aber keinen Grund, beide Werte geheim zu halten.
Sie müssen also weder das Salt noch den Initialisierungsvektor selbst verschlüsselt aufbewahren oder unzugänglich machen. Die Kenntnis eines oder beider Daten bringt einem Angreifer keinerlei Vorteil, sodass Sie vollkommen sorglos damit umgehen können. Auch ist die Quelle der Daten unerheblich. Verwenden Sie beliebig zufällige Werte für beides.
Die einzige wirklich wichtige Anforderung ist, dass Sie diese Werte überhaupt persistent speichern, denn ansonsten werden Sie die damit verschlüsselten Daten nicht mehr entschlüsseln können.
Die Implementierung von CryptoUtils beginnt mit den folgenden Anweisungen:
#import "CryptoUtils.h"
#import <CommonCrypto/CommonKeyDerivation.h>
#import <CommonCrypto/CommonCryptor.h>
NSUInteger const PBKDFRounds = 10000;
@implementation CryptoUtils
[...]
Listing 7.28 Der Kopf der Implementierung von »CryptoUtils«
Die Headerdatei CommonKeyDerivation.h enthält das Interface für die PBKDF2-Funktion. Der AES-Algorithmus ist in CommonCryptor.h deklariert.
Die Methode initWithPassword: übernimmt das Benutzerpasswort für die Verschlüsselung, erzeugt mit der Funktion SecRandomCopyBytes einen zufälligen Initialisierungsvektor und ein zufälliges Salt und weist Passwort, Initialisierungsvektor und Salt den entsprechenden Propertys zu:
- (id)initWithPassword:(NSString *)thePassword
{
self = [super init];
if (self) {
NSMutableData *theData = [NSMutableData
dataWithLength:kCCBlockSizeAES128];
SecRandomCopyBytes(kSecRandomDefault,
kCCBlockSizeAES128, theData.mutableBytes);
self.iv = theData;
theData = nil;
theData = [NSMutableData
dataWithLength:kCCBlockSizeAES128];
SecRandomCopyBytes(kSecRandomDefault,
kCCBlockSizeAES128, theData.mutableBytes);
self.salt = theData;
theData = nil;
self.password = thePassword;
}
return self;
}
Listing 7.29 Die Methode »initWithPassword:«
kCCBlockSizeAES128 ist die Größe eines Blocks bei der Verschlüsselung mit AES, und kSecRandomDefault ist der empfohlene Zufallsgenerator unter iOS. Die eigentliche Verschlüsselungsoperation erfolgt in der Methode encryptData: über den Aufruf CCCrypt:
-(NSData *)encryptData:(NSData *)clearText {
[self createKey:[self.password 
dataUsingEncoding:NSUTF8StringEncoding]];
// Puffer für Ciphertext
NSMutableData *cipherData = [NSMutableData 
dataWithLength:clearText.length + kCCBlockSizeAES128];
// Länge des Ciphertextes
size_t ciperLength;
// Durchführen der Verschlüsselung
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
self.cryptKey.bytes,
self.cryptKey.length,
self.iv.bytes,
clearText.bytes,
clearText.length,
cipherData.mutableBytes,
cipherData.length,
&ciperLength);
if(cryptStatus){
NSLog(@"Something terrible during encryption 
happened!");
} else {
NSLog(@"Ciphertext length: %i", [cipherData length]);
}
return cipherData;
}
Listing 7.30 Die Implementierung der Methode »encryptData:«
Der Aufruf der Methode createKey: erzeugt in einer noch zu implementierenden Methode aus dem Benutzerpasswort per PBKDF2 einen sicheren Schlüssel. Das Objekt cipherText ist der Zielpuffer, in den CCCrypt die verschlüsselten Daten schreibt. Die Variable ciperLength nimmt die Anzahl der von CCCrypt verarbeiteten Daten auf.
Die Funktion CCCrypt selbst übernimmt 12 Parameter:
- die gewünschte Operation (hier: Verschlüsselung)
- den gewünschten Algorithmus (hier: AES128)
- Angaben zum Padding von Daten (siehe den folgenden Kasten)
- einen Zeiger auf den Schlüssel
- die Länge des Schlüssels
- einen Zeiger auf den Initialisierungsvektor
- die Länge des Initialisierungsvektors
- einen Zeiger auf die zu verschlüsselnden Daten (den »Klartext«)
- die Länge der zu verschlüsselnden Daten
- einen Zeiger auf den Puffer für die verschlüsselten Daten (den »Ciphertext«)
- die Länge des Puffers für den Ciphertext
- die Adresse der Variable für Länge der verarbeiteten Daten
Padding
Bei der Verwendung von Blockchiffren wird der Klartext in Blöcke aufgeteilt, bei AES in Blöcke von 16 Bytes. Ist der Klartext nicht durch 16 teilbar, bleibt ein Rest, der keinen ganzen Block auffüllt. Dieser Rest fällt bei der Verschlüsselung raus. Daher muss man den nur teilweise aufgefüllten Block auffüllen, sodass er korrekt verschlüsselt wird. Dieses Auffüllen nennt sich Padding. CCCrypt führt das Padding automatisch durch, wenn die Option kCCOptionPKCS7Padding gesetzt ist.
Die Methode zum Erzeugen eines sicheren Schlüssels ist createKey:
-(BOOL)createKey:(NSData *)key{
NSMutableData *localKey = [NSMutableData 
dataWithLength:kCCKeySizeAES128];
int result = CCKeyDerivationPBKDF(kCCPBKDF2,
self.password.UTF8String,
self.password.length,
self.salt.bytes,
self.salt.length,
kCCPRFHmacAlgSHA1,
PBKDFRounds,
localKey.mutableBytes,
localKey.length);
if(kCCSuccess){
NSLog(@"Fehler beim Erstellen des Schlüssels: 
%d", result);
}
self.cryptKey = localKey;
return YES;
}
Listing 7.31 Die Methode »createKey:«
Der Aufbau der Methode entspricht der Methode encryptData:. Die Funktion CCKeyDerivationPBKDF übernimmt die folgenden neun Parameter:
- die gewünschte Funktion (hier: PBKDF2)
- das Passwort
- die Länge des Passworts
- einen Zeiger auf das Salt
- die Länge des Salts
- die zu verwendende Funktion (hier: SHA1, da in diesem Zusammenhang vollkommen ausreichend)
- die Anzahl der Runden
- einen Zeiger auf den Puffer für den Schlüssel
- die Länge des Schlüssels
Der Aufruf der Methode createKey: in der Methode encryptData: übergibt als Parameter das Benutzerpasswort, abgelegt in der Property password des Objekts:
[self createKey:[self.password
dataUsingEncoding:NSUTF8StringEncoding]];
Die Methode createKey legt den erzeugten Schlüssel anschließend in der Property cryptKey ab.
Um die Klasse CryptUtils zu verwenden, importieren Sie die Interface-Datei in der Implementierung des AppDelegate:
#import "CryptoUtils.h"
Anschließend erzeugen Sie ein Objekt von CryptoUtils, übergeben ihm ein Passwort und zu verschlüsselnden Text und können anschließend den Ciphertext samt Salt und Initialisierungsvektor in der iCloud ablegen:
[...]
CryptoUtils *theCrypt = [[CryptoUtils alloc] initWithPassword:@"4711"];
NSData *ciphertext = [theCrypt encryptData:[theLocalText dataUsingEncoding:NSUTF8StringEncoding]];
[cloudStore setData:theCrypt.salt forKey:@"Salt"];
[cloudStore setData:theCrypt.iv forKey:@"IV"];
[cloudStore setData:ciphertext forKey:@"Secret"];
[...]
Listing 7.32 Verschlüsselte Daten in der iCloud
Diesen Code fügen Sie in die GDC-Funktion in der Methode application didFinishLaunchingWithOptions: ein, nachdem Sie das Funktionieren des iCloud-Zugangs verifiziert haben.
Dadurch, dass Salt und Initialisierungsvektor nun auch in der iCloud liegen, haben Sie das Problem der Aufbewahrung umgangen. Zum Entschlüsseln der Daten benötigen Sie lediglich das korrekte Benutzerpasswort. Die Entschlüsselung erfolgt analog zur Verschlüsselung über die Funktion CCCrypt, wobei der erste Parameter nicht kCCEncrypt, sondern kCCDecrypt ist.
Hausmeistertätigkeiten
Das vorangegangene Beispiel wendet zwar Verschlüsselung an, ist aber nicht auf einen gesicherten Betrieb hin optimiert. Die sensiblen Daten (Klartext und Passwort), die in den Propertys von CryptoUtils liegen, befinden sich so lange im Speicher, wie ein Objekt vom Typ CryptoUtils existiert. Auch würde mangels Event-Handling ein Fehler beim Erstellen des PBKDF2-gesicherten Schlüssels dazu führen, dass die Verschlüsselungsoperation einfach mit dem ungesicherten Schlüssel durchgeführt wird. Achten Sie beim Programmieren einer App für besondere Sicherheitsanforderungen daher auf solche Aspekte. Wenden Sie die Designprinzipien aus Abschnitt 7.3.3, »Generische Design-Grundsätze« konsequent an, und Sie sind auf einem guten Weg.
Ihr Kommentar
Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.















Jetzt bestellen





