SSL Zertifikat erstellen (& unter sparkjava verwenden)

CitroenDsVier

Lt. Commander
Registriert
Dez. 2013
Beiträge
1.896
Hallo zusammen,

ich versuche seit einigen Stunden erfolglos, mittels verschiedener Tools ein SSL-Zertifikat für meine Testumgebung auf localhost zu generieren. Dazu findet man etliche Tutorials im Internet, ich weiß, jedes davon spuckt am Ende unterschiedliche Dateien und -Typen aus, keine Version hat bis jetzt für mich funktioniert.

Ausgangslage ist folgende:
Auf localhost läuft eine REST-Api, die mit sparkjava (link) entwickelt ist. Laut Dokumentation ist das Einbinden eines SSL-Zertifikats für HTTPS ganz easy, für das Erstellen wird dort auf oracle (keytool) verwiesen. Da das dort verlinkte Oracle-howto mich Anfangs nicht weitergebracht hat, bin ich auf openssl gestoßen, was sparkjava aber anscheinend nicht mag (bricht mit exit code 100 ab, erzählt mir aber keine Details).
Im Linux-Subsystem habe ich nun mit oracle's keytool und der in der sparkjava-Doku verlinkten Anleitung von Oracle zwei Dateien erhalten (client.crt und clientkeystore). Die clientkeystore nimmt sparkjava zwar ohne abzubrechen entgegen, es fühlt sich für mich aber allein schon wegen des Dateinamens falsch an, "client..." im Server einzubinden. Davon abgesehen konnte ich aber die "client.crt" in Windows auch nicht als vertrauenswürdiges Zertifikat hinzufügen, Fehler "Dateityp ist nicht erkennbar".

In der Oracle-Anleitung steht "If the certificate is chained with the CA’s certificate, perform step 4; otherwise, perform step 5".
Jetzt mal ganz unabhängig davon, was damit gemeint ist (was ist damit gemeint?), kann ich step 5 garnicht ausführen, weil die "CARoot.cer" nicht existiert (FileNotFoundException).
Step 4 kann ich nur leider auch nicht ausführen: "java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 49)".

Kann mir hier Jemand eine Hilfestellung geben? Das wäre klasse!

MfG
 
Es ist bei solchen Problemen immer hilfreich wenn du die genauen Befehle nennen kannst die du verwendet hast...

Grundlegend solltest du deine Probleme in beherrschbare kleinere Brocken teilen:
1. Erzeugung eines (self signed) certificate
2. Umwandlung des certificate in den für Java passenden Keystore und TrustStore
3. Einbinden der Stores etc in dein Projekt

Die von dir verlinkte Doku zu sparkjava behandelt nur Schritt 3, Schritt 1 & 2 sollte hiermit klappen: https://blogs.oracle.com/blogbypuneeth/steps-to-create-a-self-signed-certificate-using-openssl
 
snaxilian schrieb:
Es ist bei solchen Problemen immer hilfreich wenn du die genauen Befehle nennen kannst die du verwendet hast...
Die Erzeugung habe ich strikt nach der Anleitung von Oracle (im 1. Post verlinkt) abgeabeitet, d.h. soweit das möglich war (wie geschrieben haben manche Dateien nicht existiert). Das bedeutet:

Code:
keytool -keystore clientkeystore -genkey -alias client
keytool -keystore clientkeystore -certreq -alias client -keyalg rsa -file client.csr
keytool -import -keystore clientkeystore -file client.cer -alias client
keytool -import -keystore clientkeystore -file CARoot.cer -alias theCARoot
Wobei ich in der 3. Zeile (=Schritt 4 aus Anleitung) aus "client.cer" ein "client.csr" gemacht habe (vermutlich typo in Anleitung?) und die 4. Zeile (=Schritt 5 aus Anleitung) sich nicht hat ausführen lassen, weil CARoot.cer nicht existiert, woher auch.

Mit openssl habe ich folgendes versucht:
Code:
openssl req -x509 -out localhost.crt -keyout localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
(Quelle)


Die Aufteilung in mehrere Schritte hat mir zumindest etwas Klarheit gebracht, danke! Jetzt müssten sich die Schritte nur noch umsetzen lassen.

Bei der von dir verlinkten Anleitung scheitert es im 2. Schritt:
Code:
$ java utils.ImportPrivateKey -keystore identity.jks (usw.)
Error: Could not find or load main class utils.ImportPrivateKey
Ausgeführt mit einer Java 9 JDK, Version 181
 
Zuletzt bearbeitet:
Nein, das ist kein Typo, ein CSR und CER sind zwei verschiedene Paar Schuhe.

Die Anleitung aus #1 zeigt die Erstellung eines CSR mit dem man dann bei einer vertrauenswürdigen CA ein Zertifikat bekommen kann. Für Schritt 4 deiner Anleitung benötigst du natürlich das öffentliche Zertifikat der CA, die deinen CSR signiert hat.

Wo hast du den openssl Befehl her? Denn einige der Parameter finden sich bspw. nirgends in der Manpage. Auch hier empfehle ich die Aufteilung in Teilprobleme anstatt alles in einen Befehl zu packen.
 
snaxilian schrieb:
Nein, das ist kein Typo, ein CSR und CER sind zwei verschiedene Paar Schuhe.
Gut zu wissen!

snaxilian schrieb:
Die Anleitung aus #1 zeigt die Erstellung eines CSR mit dem man dann bei einer vertrauenswürdigen CA ein Zertifikat bekommen kann. Für Schritt 4 deiner Anleitung benötigst du natürlich das öffentliche Zertifikat der CA, die deinen CSR signiert hat.
Sehe ich das richtig, dass das also in meinem Fall (localhost) nicht erforderlich/möglich ist, und demnach die Anleitung für mich nicht gilt?
Ca im März wird das Projekt öffentlich, dann ist die ganze Zertifikats-Sache eh nochmal neu zu überdenken. Aktuell möchte ich nur sicherstellen, dass das alles auch so läuft, wie ich mir das vorstelle.

snaxilian schrieb:
Wo hast du den openssl Befehl her?
Wie verlinkt, hierher.

snaxilian schrieb:
Auch hier empfehle ich die Aufteilung in Teilprobleme anstatt alles in einen Befehl zu packen.
Kannst du das für meinen Fall konkretisieren?

Könnte man ein Zertifikat für "localhost" nicht auch irgendwo herunterladen? Oder würde sich ein Zertifikat, das du für deinen localhost erstellst, von einem von mir in wichtigen Punkten unterscheiden? Ob ich jetzt Frankfurt oder Berlin als Stadt angebe, sollte ja für die Funktion keinen Unterschied machen?
 
Nicht so viel blindlings Tutorials abtippen, lieber verstehen, was da passiert. :daumen:

Vergiß den Weg über CA und CSR, einfach direkt ein self-signed Certificate erstellen, das für JAVA konvertieren und dort in den Truststore werfen.

Anderenfalls wird es eh nicht funktionieren, weil JAVA dann versuchen wird, die Trustchain zu verifizieren (wie jeder andere SSL-Client auch) und das schlägt fehl, weil es die ausstellende CA solange nicht kennt, bis Du diese als Vertrauenswürdig deklariert hast (für JAVA). Eben so, wie in jedem anderen SSL-Client auch.

PS, für den Truststore gibt es auch graphische Verwaltungstools. Die sind vielleicht an der Stelle sinnvoller.


@snaxilian
Die Syntax für den Openssl-Befehl ist schon okay so. Was da passiert, ist, daß für einen der Parameter ( -config) ein Dateiobjekt von stdin gelesen wird.

-config will normalerweise einen Pfad zu einer Konfigurationsdatei haben. Es gibt aber keine, weil das Tutorial zu faul war, eine simple Konfigdatei zu erstellen.

Mit dem Konstrukt <(...) startet man eine Subshell und gibt einen Filedescriptor zurück.

Bash:
$ echo <(ls /boot)
/dev/fd/11

$ cat  <(ls /boot)
 loader loader.conf kernel boot.4th

sprich, die Subshell generiert die Konfigparameter, gibt einen Filedescriptor zurück, und openssl(1) bindet diesen an seinen -config Parameter.

Ansonsten hätte man zB mit cat oder irgendeinem Editor die Konfigdatei selber erstellt, sagen wir als ./tempconfig, und hätte dann ... -config ./tempconfig gesagt.

Das funktioniert unter sh-kompatiblen Interpretern, unter Windows also im Normalfall nicht.


Über Sinn und Unsinn von sowas in einem Tutorial sollten wir vermutlich nicht nachdenken. :daumen:
 
  • Gefällt mir
Reaktionen: snaxilian
RalphS schrieb:
Nicht so viel blindlings Tutorials abtippen, lieber verstehen, was da passiert.
hört sich vernünftig an! :)

RalphS schrieb:
Vergiß den Weg über CA und CSR, einfach direkt ein self-signed Certificate erstellen, das für JAVA konvertieren und dort in den Truststore werfen.
Kannst du mir da Instruktionen / ein Tutorial geben? :D Spaß beiseite, eventuell ein kleiner hint in die richtige Richtung?

RalphS schrieb:
PS, für den Truststore gibt es auch graphische Verwaltungstools. Die sind vielleicht an der Stelle sinnvoller.
Truststore = die Datenbank unter Windows von vertrauenswürdigen Zertifikaten? Da gibt es doch die Microsoft Management Console? Wo ist der Unterschied?

konkretor schrieb:
Das Tool hilft auch sehr
Werde ich mir ansehen, danke!
 
Das Problem Zertifikate und Keys in nen Keystore zu bekommen hatte ich bereits einmal hier versucht zu lösen.
Das Umwandeln von Hand ist eigentlich recht simple, bequemer ist es aber natürlich wenn das Programm das mit erledigt.
Ich hab dann etwas rum gelesen, die pem Dateien die man von LE oder OpenSSL bekommt scheinen Base64 kodiert zu sein. Das muss man natürlich vorher Auflösen um an gültige Zertifikate zu kommen.
Das ganze sieht dann in etwa so aus vom Ablauf her (mit den entsprechenden Pfaden zu den Dateien natürlich):
Java:
KeyStore keyStore;
String keyStorePass;
String alias = "somealias";
// erstellt nen leeren Keystore
keyStore = KeyStore.getInstance("JKS");
keyStore.load(null);
// und nen passwort
Random random = new Random(System.currentTimeMillis());
keyStorePass = String.valueOf(random.nextLong());
// dateien ein/auslesen und zertifikat/key erstellen
byte[] cert = Files.readAllBytes(Path.of(pathToCert));
String certs = new String(cert);
certs = certs.substring(certs.indexOf("-----BEGIN CERTIFICATE-----")+27, certs.indexOf("-----END CERTIFICATE-----"));
certs = certs.replaceAll("\n", "");
cert = Base64.getDecoder().decode(certs);
byte[] key = Files.readAllBytes(Path.of(pathToPrivateKey));
String keys = new String(key);
keys = keys.substring(keys.indexOf("-----BEGIN PRIVATE KEY-----")+27, keys.indexOf("-----END PRIVATE KEY-----"));
keys = keys.replaceAll("\n", "");
key = Base64.getDecoder().decode(keys);

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)certFactory.generateCertificate(new ByteArrayInputStream(cert));

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(key));

keyStore.setCertificateEntry(alias+"_cert", certificate);
keyStore.setKeyEntry(alias+"_key", privateKey, keyStorePass.toCharArray(), new Certificate[] {certificate});
Vermutlich ist das ganze nicht gerade super umgesetzt aber es tut so in etwa das was es soll. Muss man ggf so anpassen das es das erzeugt was du brauchst.
 
CitroenDsVier schrieb:
eventuell ein kleiner hint in die richtige Richtung?
Einfach Google anstrengen nach OpenSSL und selfsigned.
Wenn die da mit CA und/oder CSR kommen bist Du falsch => nächstes Ergebnis. :daumen:

CitroenDsVier schrieb:
Truststore = die Datenbank unter Windows von vertrauenswürdigen Zertifikaten? Da gibt es doch die Microsoft Management Console? Wo ist der Unterschied?

Nein, eben nicht. JAVA ist plattformunabhängig, soll also unter jedem X-beliebigen System laufen: und statt nun für jedes beliebige System eine API bauen zu müssen, hat man einfach seinen eigenen Zertifikatsspeicher entworfen. Der ist unabhängig vom OS, egal welchem, weswegen man ihn auch zusätzlich verwalten muß.
 
Sp4rky schrieb:
Das ganze sieht dann in etwa so aus vom Ablauf her (mit den entsprechenden Pfaden zu den Dateien natürlich):
Cool, danke! Welche Dateien erwartet der Code als input für "pathToCert" und "pathToPrivateKey"? Ich habe jetzt im Laufe der Zeit vermutlich 10 verschiedene Dateitypen und -formate gehabt, sprich, was genau ist hier der input?

snaxilian schrieb:
Afaik bringt Java einen eigenen Speicher für Zertifikate mit anstatt den vom OS zu verwenden.
RalphS schrieb:
JAVA ... seinen eigenen Zertifikatsspeicher entworfen.
Ah, gut zu wissen! Das müsste dann ja aber für mich irrelevant sein, da ich in Java nur einen Server habe, aber kein "client-zertifikat" brauche, weil ich mich (in java) nicht mit dem Server verbinden will (?)
Stattdessen müsste ich das Zertifikat auf dem Rechner installieren, der die Verbindung zur java-REST-Api aufbauen will (?) Allerdings baut dieser Client zuerst eine Verbindung zu einem normalen Webserver auf, dessen Website dann AJAX-Requests an das Backend schickt. -> Wo muss das Client-Zertifikat dann hin?

Falls das zu wirr geredet war, im Anhang ein Sequenzdiagramm zum Ablauf. Die Verbindung zum Backend soll über https laufen, meinem bisherigen Kenntnisstand nach müsste dann die Verbindung zum Webserver auch über https laufen, was grundsätzlich (meines Wissens nach) kein Problem darstellen würde.
 

Anhänge

  • sequenzdiagramm.png
    sequenzdiagramm.png
    19,9 KB · Aufrufe: 435
CitroenDsVier schrieb:
Cool, danke! Welche Dateien erwartet der Code als input für "pathToCert" und "pathToPrivateKey"? Ich habe jetzt im Laufe der Zeit vermutlich 10 verschiedene Dateitypen und -formate gehabt, sprich, was genau ist hier der input?
An sich nimmt das einfach den Pfad mit Datei also sowas wie /path/to/file.pem, also das was openssl oder le dir so ausgeben.
 
Zuletzt bearbeitet:
Das sind alles verschiedene Paar Schuhe.

Vereinfacht:
Level 0: Webserver <> Browser/JAVA-Applikation = Client
Level 1: verschlüsselte Verbindung zwischen Server und Client.
Level 2: Daten.

Die haben alle miteinander nichts zu tun.
Wenn wir uns auf L1 bewegen (s.o.) und TLS im Backend haben, brauchen wir:
  • Ein Zertifikat auf dem Server.
  • Eine Möglichkeit auf dem Client, das Serverzertifikat zu überprüfen.
  • OPTIONAL ein Zertifikat auf dem Client (für die Clientauthentifizierung)
  • Falls Client-Authentifizierung: entsprechende Konfigurationserweiterung auf dem Server, damit der weiß, was für Client-Zertifikate er annehmen darf.

Der Client hat mit dem Server exakt nichts zu tun. Die müssen nur miteinander reden können.

ABER da wir hier von selfsigned reden, mußt Du dem CLIENT, also der JAVA-Applikation, bekanntgeben, daß dieses Zertifikat vertrauenswürdig ist, indem Du es in den JAVA-Truststore als "vertrauenswürdige ROOT CA" einträgst.

Client Auth würde ich an der Stelle dringend weglassen. Ist schick, ja, aber nochmal einen guten Ticken extra Aufwand, damit es funktioniert.

Standardmäßig haben Nutzer im Web kein Client-Zertiifkat. Woher auch? Nur der Server wird authentifiziert. Client Auth kann man im Intranet machen (zB für single-sign-on) für geschützte Umgebungen oder irgendwas, wo es keinen Benutzernamen/PW geben kann oder soll.
 
Sp4rky schrieb:
An sich nimmt das einfach den Pfad mit Datei also sowas wie /path/to/file.pem, also das was openssl oder le dir so ausgeben.
Ich meinte, welche Dateien er da haben will. "cert.pem" und welche noch?

RalphS schrieb:
Das sind alles verschiedene Paar Schuhe.

Vereinfacht:
Level 0: Webserver <> Browser/JAVA-Applikation = Client
Level 1: verschlüsselte Verbindung zwischen Server und Client.
Level 2: Daten.

Die haben alle miteinander nichts zu tun.
Okay, dessen bin ich mir grundsätzlich bewusst, es ist aber möglich, dass ich das gerade etwas durcheinander werfe :)

RalphS schrieb:
Eine Möglichkeit auf dem Client, das Serverzertifikat zu überprüfen.
Wie würde man so etwas (in diesem Fall oder generell) realisieren?

RalphS schrieb:
ABER da wir hier von selfsigned reden, mußt Du dem CLIENT, also der JAVA-Applikation, bekanntgeben, daß dieses Zertifikat vertrauenswürdig ist, indem Du es in den JAVA-Truststore als "vertrauenswürdige ROOT CA" einträgst.
Tut mir leid, falls ich da was falsch verstehe, aber einen Java-Client gibt es nicht :confused_alt: Alles, was hier Java ist, ist das REST-Api-Backend. Auf dem Client ist die index.html mit ein wenig javascript. Hier sollte es möglichst nicht notwendig sein, irgendwelche Zertifikate zu installieren. Wenn ich das richtig verstanden habe, ist es das ja aber auch nicht, weil wir kein Client-Auth verwenden wollen.

RalphS schrieb:
Client Auth würde ich an der Stelle dringend weglassen. Ist schick, ja, aber nochmal einen guten Ticken extra Aufwand, damit es funktioniert.
Sehe ich auch so, authentifiziert wird sich mit einem normalen Login, das reicht.


Heißt unter'm Strich aber was genau für den Java-Truststore? Der einzige Java-Code läuft ja hier im Backend (?)
Auf dem Client läuft ja garkein Java-Code.


Langsam kommt Licht ins Dunkel, glaube ich, danke für die Geduld!
 
Es gibt immer einen Client. :) Eine API hat weder was mit SSL noch mit sonstwas zu tun - das war das was ich ursprünglich meinte, hier sind wir schon auf Datenlevel. Verbindung kommt vorher.

Aha, also ist der Server die JAVA-Applikation? Okay, dann hab ich da was mißverstanden.

Dann haut das bisher Gesagte soweit schon hin:
1 Selfsigned Cert erstellen
2 dieses Cert in den JAVA Truststore schreiben
3 dieses Cert außerdem in die Vertrauenswürdigen Root CA schreiben - als Selfsigned ist Server Cert und CA Cert in einem
4 jenes Cert an die Anwendung binden.

Für die Validierung auf dem Client:
- Der versucht, die Zertifikatskette zu prüfen. Gibts bei dir nicht. Ansonsten die ausstellende CA sowie ggfs. weitere Zertifikate für die Zwischen-CA. Die sollten alle mit in demjenigen Zertifikat stecken, das die Anwendung gebunden hat. Alternativ (so machen es geläufige Betriebssysteme) bekannte CA einfach in den Truststore der Clients vorinstallieren.

Weiter kann ein Zertifikat Validierungspfade angeben, Stichwort CDP, AIA und OCSP. Das sind URIs, wo direkt im Zertifikat steht, daß man dort das Cert anfordern kann (AIA) schauen kann ob es gesperrt wurde (CDP) oder gleich live prüfen kann, was der Status "jetzt" ist (OCSP). Wenn angegeben und nicht erreichbar, sagt der moderne Client meist "möp"; ältere Clients lassen dies noch durchgehen.

Wenn Dich Zertifikate interessieren (aber Vorsicht, komplexes und schwieriges Thema) dann kannst Du Dir --- zb mit Hilfe von Tutorials :daumen: --- eine eigene CA auf Basis von OpenSSL einrichten und damit rumprobieren. Beteiligte Dateien haben wie unter Linux üblich keine definierten Endungen, aber zugehörig sind
  • keyfiles für den privaten Schlüssel, meist .pem oder .key
  • certificate signing requests (CSR), ein Antrag auf ein neues Zertifikat mit den erforderlichen Parametern, üblicherweise .csr
  • das Zertifikat selber, meist .pem, .cer oder .crt mit oder ohne zusätzlichen privaten Schlüssel - von der CA bekommt man so eins als Antwort auf sein CSR, immer OHNE den PK, den man NIE aus der Hand gibt

Wenn man das mit OpenSSL selber macht:
für die keyerstellung nur den Ausgabenamen für den Key
für die Anforderung (CSR) den privaten Schlüssel für das Zertifikat, was man haben will
für die Signatur den CA-Schlüssel und die CSR, Ausgabe in PEM
für selfsigned einfach nur das Zertifikat, ggfs. mit extra Schlüsseldatei

Wenn mans erstmal kapiert hat ist der ganze Vorgang eigentlich völlig logisch.
 
  • Gefällt mir
Reaktionen: CitroenDsVier
Was genau willst du damit zeigen? Dass man in Java einen HTTPS Webservice bauen kann?
Falls das nur eine Übung ist, dann stell dir mit dem Keytool laut Anleitung ein Zertifikat aus und richte es in der JVM ein. Der Thread schweift total ab finde ich
 
CitroenDsVier schrieb:
Ich meinte, welche Dateien er da haben will. "cert.pem" und welche noch?
Die Datei mit dem Zertifikat und die mit dem privaten Schlüssel, daher auch die Namen der Variablen ;). Bei LE also cert.pem und privkey.pem, bei OpenSSL eben deine Dateinamen dafür.
OpenSSL bietet sich nur für lokale Tests an, für alles andere sollten es richtige Zertifikate sein.
 
  • Gefällt mir
Reaktionen: CitroenDsVier
Den Begriff "Api" oder "REST-Api" habe ich vermutlich etwas irreführend verwendet, um den Server zu beschreiben, auf dem diese Anwendung läuft. :)

RalphS schrieb:
Aha, also ist der Server die JAVA-Applikation?
Korrekt! Der Client verwendet nur seinen Browser.

RalphS schrieb:
2 dieses Cert in den JAVA Truststore schreiben
Um die keystore-Dateien zu erhalten? Oder muss der Server sich auch selbst vertrauen?
RalphS schrieb:
3 dieses Cert außerdem in die Vertrauenswürdigen Root CA schreiben
= Windows-Truststore (/ Microsoft Management Console)?
RalphS schrieb:
4 jenes Cert an die Anwendung binden.
Meinst du hiermit, im Java-Code des backends auf die keystore-Dateien zu verweisen?

RalphS schrieb:
Alternativ (so machen es geläufige Betriebssysteme) bekannte CA einfach in den Truststore der Clients vorinstallieren.
Das müsste wohl der einfachste Weg sein, oder? Heißt ich erstelle mir ein Zertifikat, füge dieses dem Windows-Truststore hinzu und schaue, dass ich es irgendwie in einen Java-Keystore umwandle, um diesen im Java-Backend einzubinden?

@LencoX2: Nein, das ist keine Übung. Es existiert bereits ein lauffähiges System, nur läuft die Kommunikation eben noch über http und genau hier versuche ich anzusetzen. Von mir aus kann man es als Übung für mich bezeichnen, da das Thema für mich neu ist.
 
Nix Windows. Immer nur Java. Windows hat damit nichts zu tun.

Außer Du meinst auf Browserseite. Dann ja, wenn das Windows ist.

Dennoch, auch die Java Applikation muß sich selbst validieren können, deshalb auch dort im Java truststore unter Trusted Root CA. Java meldet sich aber, wenn da was zickt.

Der keystore sind alle für Java bekannten Zertifikate, nicht nur deins. Dem kann man Zertifikate hinzufügen, nur leider nicht im PEM Format.
 
Zurück
Oben