Seit dem Erscheinen von iOS 13 und MacOS 10.15 (Catalina) mehren sich im Netz die Berichte, dass Apple das Synchronisieren der App Erinnerungen über den weit verbreiteten caldav
-Standard nicht mehr unterstützen würde. Belege dafür findet man jedoch keine. Zur allgemeinen Verwirrung hat auch eine etwas unklare Aussage von Apple geführt, die man so verstehen konnte, dass die Synchronisation der neuen Erinnerungen-App über caldav
eingestellt worden sei (mittlerweile hat Apple den entsprechenden Artikel korrigiert und für Klarheit gesorgt).
Was allerdings seltsam ist: bei manchen Usern werden Erinnerungen auch unter den neuesten Apple-Betriebssystemen synchronisiert, bei anderen jedoch nicht. Die Synchronisation von Kalender über caldav
funktioniert aber in jedem Fall weiterhin.
Woran liegt das und gibt es eine Lösung?
Es liegt am Zertifikat: Apple hat die Anforderungen für gültige Serverzertifikate in iOS 13 und MacOS 10.15 hochgeschraubt. Die Serverzertifikate müssen folgende Mindestvoraussetzungen erfüllen, um vom Betriebssystem als gültig akzeptiert zu werden:
- der Schlüssel muss mindestens 2048 Bit groß sein
- es muss mit einem Hash-Algorithmus aus der SHA-2 Familie signiert sein
- der DNS-Name muss in der SAN-Erweiterung des Zertifikats stehen (er darf nicht im CommonName stehen!)
Für Zertifikate, deren Gültigkeitsbeginn nach dem 01.07.2019 liegt, gilt außerdem:
- die EKU-Erweiterung muss mit
id-kp-serverAuth OID
enthalten sein - das Zertifikat darf nicht länger als 825 Tage gültig sein
(Quelle: https://support.apple.com/de-de/HT210176)
Soweit ich weiß, benötigt man eine Konfigurationsdatei, um ein Serverzertifikat mit openssl
zu erstellen, das diese neuen Bedingungen erfüllt. Da im Internet hauptsächlich nur Einzeiler kursieren, um ein selbstsigniertes Serverzertifikat zu erstellen, dürften die allermeisten selbstsignierten Serverzertifikate daher diese neuen Bedingungen nicht erfüllen. Möglicherweise scheitern auch offiziell signierte Serverzertifikate daran. LetsEncrypt immerhin erfüllt Apples neue Bedingungen.
Aber warum funktioniert die Kalender-Synchronisation über caldav
weiterhin? Ich erkläre mir das so: Apple hat die Erinnerungen-App für iOS 13 und MacOS Catalina von Grund auf neu geschrieben. Sie funktioniert nur mit einem solchermaßen gültigen Zertifikat, während die Kalender-App noch mit älteren Zertifikaten funktioniert, die die neuen Zertifikatsbedingungen nicht erfüllen. Vermutlich ist es nur eine Frage der Zeit, bis Apple auch die Kalender- und Adressbuch-Synchronisation nur noch mit den strengeren Zertifikatsanforderungen ermöglicht.
Zum Erstellen und Signieren eines solchermaßen gültigen Serverzertifikats richtet man sich am Besten eine eigene Certificate Authority ein (im Folgenden CA genannt). So umgeht man das spätestens alle 825 Tage notwendige manuelle Importieren und Vertrauen in iOS/MacOS, da das Root-Zertifikat der CA (im Gegensatz zum Serverzertifikat) beliebig lange gültig sein kann. Vor allem, wenn man das bei Freunden/Bekannten oder Kunden einrichten muss, ist das ein enormer Vorteil!
Klingt kompliziert? Ist es nicht! Zunächst erstellen wir ein Verzeichnis, in dem die CA arbeiten soll, und erstellen dort ein paar für deren Betrieb notwendige Dateien:
mkdir -p /etc/CA && cd /etc/CA touch index.txt && mkdir certs crl newcerts && touch crlnumber
Dann müssen zwei Konfigurationsdateien erstellt werden. Die erste heißt openssl-ca.cnf
für die CA und wird mit folgendem Inhalt gefüllt (die Werte unter [req_distinguished_name]
können den eigenen Bedürfnissen angepasst werden):
#################################################################### # openssl-ca.cnf # openssl Konfigurationsdatei für eine Certificate Authority (CA) # um ein Root-Zertifikat zu erstellen und CSRs zu signieren HOME = . RANDFILE = $ENV::HOME/.rnd #################################################################### # CA definition [ ca ] default_ca = CA_default # die Default-CA-Sektion #################################################################### # Werte für die CA (siehe oben) [ CA_default ] dir = . # Hier wird alles gespeichert certs = $dir/certs # Hier werden die Zertifikate gespeichert crl_dir = $dir/crl # Hier werden die CRLs gespeichert database = $dir/index.txt # Datenbank Index Datei new_certs_dir = $dir/newcerts # Default Speicherort für neue Zertifikate certificate = $dir/cacert.pem # Das Root-Zertifikat der CA serial = $dir/serial # Die Seriennummer crlnumber = $dir/crlnumber # Die CRL-Nummer (auskommentieren, um V1 CRLs zu erzeugen) crl = $dir/crl.pem # Das aktuelle CRL private_key = $dir/cakey.pem # Der geheime Schlüssel der CA # Auf 'yes' ändern, um dasselbe Subject für verschiedene Zertifikate zu verbieten: unique_subject = no # Option, um die Extension vom CSR zum signierten Zertifikat zu kopieren: copy_extensions = copy # OpenSSL anweisen, nicht das traditionelle (und kaputte) Format zu benutzen: name_opt = ca_default # Optionen für den Subject Namen cert_opt = ca_default # Optionen für die Zertifikatsfelder x509_extensions = usr_cert # Erweiterungen, die dem Zertifikat hinzugefügt werden default_days = 825 # Gültigkeitsdauer des Zertifikats (iOS 13 + MacOS 10.15: max 825) default_crl_days= 30 # Dauer bis zum nächsten CRL default_md = default # benutze Standard MD für den öffentlichen Schlüssel (derzeit SHA256) preserve = no # übergebene DN-Reihenfolge beibehalten? # Es folgen zwei verschiedene Policies, wie der Request erstellt sein sollte. policy = policy_match #################################################################### # Die CA-Policy, die beim Signieren von CSRs benutzt wird: [ policy_match ] countryName = match stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### # Die 'anything' Policy für alles andere [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### # Wie CSRs erzeugt werden sollen: [ req ] default_bits = 4096 default_keyfile = cakey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # Erweiterungen, die einem selbstsignierten Zertifikat hinzugefügt werden sollen req_extensions = v3_req # Erweiterungen, die einem CSR hinzugefügt werden sollen string_mask = utf8only prompt = no # Werte von [req_distinguished_name] ohne Nachfrage übernehmen #################################################################### # Der 'distinguished name', der dem Zertifikat hinzugefügt wird (siehe [req]) # diese Werte können den eigenen Bedürfnissen angepasst werden # Länderkürzel sollte allerdings gültig sein! [ req_distinguished_name ] countryName = DE # Länderkürzel 0.organizationName = Privat commonName = Privat CA 1.0 #################################################################### # Extra Attribute für CSRs. Kann leer sein, muss aber existieren [ req_attributes ] #################################################################### # Diese Erweiterungen werden hinzugefügt, wenn die CA einen CSR signiert: [ usr_cert ] authorityKeyIdentifier = keyid,issuer basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectKeyIdentifier = hash #################################################################### # Erweiterungen, die einer Zertifikatsanforderung hinzugefügt werden: [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment #################################################################### # Benutzte Erweiterungen beim Selbstsignieren eines Zertifikats [ v3_ca ] authorityKeyIdentifier = keyid,issuer basicConstraints = critical,CA:true keyUsage = cRLSign, keyCertSign subjectKeyIdentifier = hash
Die zweite Konfigurationsdatei openssl-server.cnf
wird für das Erstellen eines Certificate Signing Request (CSR) für das Serverzertifikat benötigt und bekommt folgenden Inhalt (die Werte unter [server_distinguished_name]
können den eigenen Bedürfnissen angepasst werden – die Werte unter [alternate_names]
MÜSSEN(!) den eigenen Bedürfnissen angepasst werden):
#################################################################### # openssl-server.cnf # openssl Konfigurationsdatei, um einen CSR für ein gültiges Serverzertifikat zu erstellen HOME = . RANDFILE = $ENV::HOME/.rnd #################################################################### [ req ] default_bits = 4096 default_keyfile = serverkey.pem distinguished_name = server_distinguished_name req_extensions = server_req_extensions string_mask = utf8only prompt = no #################################################################### # der Name des Serverzertifikats # diese Werte können den eigenen Bedürfnissen angepasst werden # das Länderkürzel sollte allerdings gültig sein! [ server_distinguished_name ] countryName = DE # Länderkürzel organizationName = Privat commonName = Privat Serverzertifikat v1.0 #################################################################### [ server_req_extensions ] subjectKeyIdentifier = hash basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = TLS Web Server Authentication, TLS Web Client Authentication subjectAltName = @alternate_names #################################################################### # die folgenden Domainnamen werden dem Zertifikat hinzugefügt # und müssen den eigenen Wüsnchen anngepasst werden # Wildcards sind erlaubt (*.webroot.de) # Ebenso sind IP-Adressen möglich (allerdings problematisch, da IP-Adressen keine SNI sind!) [ alternate_names ] DNS.1 = privat.fritz.box DNS.2 = domain.example.com DNS.3 = *.webroot.de DNS.4 = 127.0.0.1 DNS.5 = 192.168.178.100 IP.1 = 127.0.0.1 IP.2 = 192.168.178.100
Sind beide Konfigurationsdateien unter den genannten Namen angelegt und die DNS-Namen in openssl-server.cnf
angepasst kann es losgehen.
Zunächst wird das Root-Zertifikat cacert.pem
der CA mit einer Gültigkeitsdauer von 20 Jahren (-days 7300
) erstellt:
openssl req -x509 -config openssl-ca.cnf -sha256 -nodes -out cacert.pem -outform PEM -days 7300
Als nächstes wird der private Schlüssel serverkey.pem
und der Certificate Signing Request servercert.csr
des Serverzertifikats erstellt:
openssl req -config openssl-server.cnf -newkey rsa:4096 -sha256 -nodes -out servercert.csr -outform PEM
Diesen CSR lassen wir nun von unserer CA signieren und erzeugen damit das signierte Serverzertifikat servercert.pem
:
openssl ca -create_serial -config openssl-ca.cnf -policy policy_anything -extensions usr_cert -out servercert.pem -infiles servercert.csr
Der private Schlüssel serverkey.pem
und das Zertifikat servercert.pem
im Arbeitsverzeichnis müssen nun der Webserverkonfiguration hinzugefügt werden. Unter nginx
erledigen das die folgenden beiden Zeilen im entsprechenden server
-Block:
ssl_certificate /etc/CA/servercert.pem; ssl_certificate_key /etc/CA/serverkey.pem;
Wie das bei anderen Webservern geht, findet sich in der Dokumentation des eingesetzten Webservers.
Das Root-Zertifikat der CA muss nun unter iOS/MacOS installiert werden. Entweder schickt man sich das Zertifikat per Mail oder man legt es in einem Samba-Share ab und greift über die Dateien-App darauf zu (Danke @barish). Die Frage, ob das Zertifikat installiert werden soll, mit ja
beantworten und die weiteren Schritte befolgen.
Als letzter Schritt muss das Betriebssystem angewiesen werden, dem Zertifikat zu vertrauen. Unter iOS findet man die entsprechende Einstellung unter Systemeinstellungen
-> Allgemein
-> Info
-> Zertifikatsvertrauenseinstellungen
. Unter MacOS ist die Schlüsselbundverwaltung dafür zuständig.
Ist das erledigt, vertrauen iOS und MacOS fortan allen von der CA signierten Serverzertifikaten, solange das Root-Zertifikat der CA gültig ist (das sind 20 Jahre).
Spätestens alle 825 Tage muss das Serverzertifikat erneuert werden. Mit den folgenden beiden Befehlen wird ein neuer CSR erstellt und anschließend das Zertifikat von unserer CA signiert. Die Option -batch
sorgt dafür, dass keine Fragen gestellt werden – so lässt sich das Ganze gut automatisieren z.B. über einen cronjob
:
openssl req -config openssl-server.cnf -newkey rsa:4096 -sha256 -nodes -out servercert.csr -outform PEM openssl ca -batch -create_serial -config openssl-ca.cnf -policy policy_anything -extensions usr_cert -out servercert.pem -infiles servercert.csr
iOS und MacOS vertrauen wie gesagt dem neu erstellten Serverzertifikat ohne weitere Handlungsnotwendigkeit, solange das Root-Zertifikat der CA gültig ist. Sehr komfortabel!
Sind alle Schritte befolgt worden, steht das Zertifikat einer erfolgreichen caldav
-Synchronisation von Erinnerungen auch unter den neuesten Apple-Betriebssystemen nicht mehr im Weg.
Changelog:
- [08.12.2019] komplette Überarbeitung mit Erstellen einer eigenen CA, um nicht alle 825 Tage ein neues Zertifikat importieren zu müssen
- [05.11.2019] kleine Umformulierungen zur besseren Verständlichkeit
Für die Übertragung auf iOS eignet sich auch sehr gut die neue Dateien-App, sofern man auf seinem Server auch Samba laufen hat.
Danke! Die Anleitung hat mir sehr geholfen.
Ich habe zunächst auch die (anscheinend) weggefallene CalDav-Unterstützung als Grund für den fehlenden Sync von iOS13 mit meinem Server vermutet. Dank der guten Anleitung klappt es nun wieder.
Danke für das positive Feedback.
Eine Frage bzgl. der Zeile der Domain, für die das Zertifikat gültig sein soll: Ich möchte auf Nextcloud zugreifen über 192.168.x.x, geht dies auch?
VG
Das sollte funktionieren.
Es ist auch möglich mehrere Domains durch Komma getrennt in der Zeile anzugeben – z.B. so (Achtung: die Zeile kommt 2x vor und sollte 2x gleich lauten!):
Viel Erfolg 🙂
Danke für die Antwort. Hat leider nicht funktioniert.
Hm. Ich habe es gerade auch ausprobiert und es ging auch mit einer IP im subjectAltName
Eine IP-Adresse ist allerdings keine SNI, daher könnte der Server ein anderes Zertifikat ausliefern als gewünscht.
Liefert der Webserver bei Zugriff auf die IP das richtige Zertifikat aus (überprüfen mit
curl -v IP.IP.IP.IP
)?Danke für die Erklärung. Damit rückte das Aufsetzen einer eigenen CA wieder etwas hoch in der Prio-Liste 😉
Vorläufig hat sich auch definitiv was getan, seit ich das neue Cert eingespielt habe.
Leider nur bedingt positiv, da jetzt ständig meine Tasks und manuellen Listen nach ein paar Sekunden verschwinden. Ich nutze den WebDAV/CalDAV auf einem Synology NAS.
Kalender Sync und Kontakte (CardDAV) funktioniert noch einwandfrei.
Jemand was ähnliches beobachtet?
Hallo Christian,
mit webdav/caldav auf Synology habe ich leider keine Erfahrungen und kann daher leider nichts dazu sagen. Ich nutze Nextcloud für caldav/carddav und da funktioniert die Synchronisation von Kalendern/Tasks/Adressen problemlos.
Wegen der CA: ich habe vor, den Artikel demnächst dahingehend zu aktualisieren.
Gruß, Bernhard.