Die Kurve kriegen — Hacking the JVM for Enhanced Crypto

Bei einem staatlich getriebenen Projekt wie der Einführung der elektronischen Gesundheitskarte steht die IT-Sicherheit an oberster Stelle. Dass aber die Anforderungen des Auftraggebers und der Zulassungsstelle über das hinausgehen, was die von uns gewählte Plattform für unser embedded device in der Telematik-Infrastruktur von Haus aus liefern kann, ruft bei uns Entwicklern und Software-Ingenieuren gemischte Gefühle hervor: Der Kitzel, etwas wirklich Neues zu schaffen, aber auch die Ahnung, dass man sich auf unbekanntem Gebiet ziemlich verirren kann.
So auch bei n-design, als die neuen Spezifikationen des Auftraggebers vorschrieben, dass TLS mit ECDHE Cipher-Suiten umgesetzt werden muss. ECDHE steht für die Schlüsselaushandlung nach Diffie-Hellman mit Verfahren über elliptischen Kurven. Grundsätzlich ist das kein Problem, da Java schon seit einigen Jahren in diesem Bereich auch mit elliptischen Kurven umgehen kann. Außerdem ist es sehr zu begrüßen, dass diese modernen Verfahren Aufnahme in die Spezifikation finden. Sie bieten bei vergleichbarem Rechenaufwand ein deutlich höheres Sicherheitsniveau als die klassischen RSA-basierten Verfahren. Das ist für die Entwicklung eines embedded Geräts auf der Basis eines Mobilprozessors ein Segen, da die zukünftig notwendigen Schlüssellängen mit der im Projekt gegebenen Hardware nicht gut zu berechnen wären.
Schwierig wurde es allerdings, als die zu verwendenden Kurven spezifiziert wurden: Nicht nur die allgegenwärtigen Kurven P256 und P384 des amerikanischen Standardisierungsinstituts NIST sollten unterstützt werden, sondern auch die Kurven Brainpool256, Brainpool384 und Brainpool512.

Letztere entstanden aus einer Initiative des ECC Brainpool, einem Zusammenschluss deutscher Unternehmen, Bildungseinrichtungen und Behörden. Ziel dieser Gruppe ist es, elliptische Kurven zu definieren, die nicht auf spezielle Algorithmen hin ausgerichtet sind, sondern die sich aus „natürlichen“ Quellen wie Pi oder der Euler’schen Zahl e ableiten, und so zu besserer Überprüfbarkeit und Unabhängigkeit von amerikanischen Organisationen führen sollen.

Wir sind bei unserer Entwicklung auf die Java SE Plattform in der Inkarnation von OpenJDK festgelegt. Diese Plattform kennt zwar grundsätzlich die Brainpool-Kurven, aber nicht für die Verwendung mit ECDHE, sondern lediglich für digitale Signaturen nach ECDSA. Dass sich das auf absehbare Zeit nicht ändern wird, wurde uns schnell klar: Die einzigen Feature-Requests auf der Java Security Mailing Liste fristen seit Jahren unbearbeitet ein Mauerblümchendasein.

Das stellte uns vor ein großes Problem, zeigte andererseits aber auch einen Lösungsansatz auf, den wir letztendlich verfolgt haben.

Die Arbeitshypothese war, dass wir im Quellcode des OpenJDK an allen Stellen, die für ECDHE verantwortlich sind und die NIST-Kurven verwenden, zusätzlich noch die drei fehlenden Brainpool-Kurven der Längen 256, 384 und 512 Bit aufnehmen würden. Wenn die Algorithmen zur Berechnung von Punkten auf den Kurven identisch sind zu denen für die NIST-Kurven, müsste das klappen. Als Verifikationswerkzeuge sollten uns OpenSSL und Pari/GP dienen. OpenSSL ist das Schweizer Taschenmesser für kryptographische Verfahren, Pari/GP ist ein Algebra-Werkzeug, das an der Universität Bordeaux entwickelt wird. Mit dieser doppelten Verifikation soll sicher gestellt werden, dass die von unserem modifizierten OpenJDK berechneten Punkte tatsächlich auf der angegebenen Kurve liegen.

Die Ernüchterung kam recht schnell, als wir feststellten, dass nur der geringste Teil der beteiligten Codestellen im Java-Teil des OpenJDK zu finden ist. Der weitaus größere – und mathematisch komplexere – Teil liegt stattdessen im nativen Teil des Systems, das in C und C++ implementiert ist.

Wir sind bei n-design zwar sehr vielseitig aufgestellt, aber C und C++? Damit haben die Beteiligten seit dem Studium nicht mehr viel zu tun gehabt. Doch in irgendeiner Mottenkiste lag noch ein Kernighan-Ritchie und nach den ersten zögerlichen Gehversuchen konnten wir auch wieder mit Zeiger-Arithmetik umgehen (auch wenn wir weiterhin froh sind, dass uns das im allergrößten Teil unserer Projekte erspart bleibt…).

Problematisch blieben die Werkzeuge: Während man mit printf-Ausgaben kleinere Programme noch debuggen kann, ist es bei einem komplexen System wie dem OpenJDK schon schwieriger. So schwierig, dass wir schließlich doch noch gelernt haben, wie der GNU Debugger in Eclipse einzubinden ist. Im Endeffekt konnten wir in einem Projekt mit zwei Programmiersprachen fast genauso effizient entwickeln wie in Java. Die gewählten Werkzeuge zur Verifikation der Ergebnisse haben glücklicherweise sofort funktioniert: OpenSSL konnte sich mit einem TLS-Server, der unter unserem JDK lief, verbinden. Pari/GP bestätigte, dass die berechneten Punkt tatsächlich auf der Kurve liegen.

Zu guter letzt hat sich die Arbeitshypothese bewahrheitet. Es war zwar mehr zu tun, als nur in einigen Java-Klassen Ergänzungen vorzunehmen; doch tatsächlich können wir nun einen Haken hinter die Anforderung setzen, TLS/ECDHE mit Brainpool Kurven zu implementieren. Und manchmal hat man auch als erfahrener Softwareentwickler das Gefühl, etwas wirklich Neues geschaffen zu haben.