QVINTVS · SCRIBET

OpenSSL Mini-CA (PKI)

Eine tutorialartige Anleitung zur Benutzung von „openssl ca“ und Beschreibung, wie man seine eigene CA bzw. PKI mit OpenSSL betreibt.

Dieser Artikel ist quasi aus der Not heraus geboren. Immer mal wieder kommt es vor, dass ich zu Testzwecken meine eigene Zertifizierungsstelle (CA) betreiben will. Weil das aber nur alle paar Monate/Jahre einmal vorkommt, habe ich dann unweigerlich vergessen, wie es funktioniert, und schreibe das Prozedere daher nun ein für alle Mal hier nieder. Da die Dokumentation von OpenSSL selbst quasi nicht vorhanden ist, hilft der Artikel möglicherweise auch anderen Leuten weiter.

Achtung: Eine selbstgebaute PKI ist zwar sehr interessant, genügt aber faktisch niemals den hohen Sicherheitsanforderungen, die im Geschäftsfeld der CAs gestellt werden. Zur mehr als Testzwecken oder nur kleinen Betätigungsfeldern (etwa für die eigene Familie) ist sie aber durchaus geeignet.

Grundlagen

Das Kürzel „PKI“ steht für „Public Key Infrastructure“ und bezeichnet den gesamten involvierten Stapel an öffentlichen (und privaten) Schlüsseln. Das sind im Minimalfall:

  1. CA-Zertfikat
  2. Server-Zertifikat (signiert durch CA-Zertifikat)
  3. Optional: Klient-Zertifikat (signiert durch CA-Zertifikat)

Eine solche PKI anzulegen ist eigentlich gar nicht einmal so schwierig, wenn es den von Seiten des OpenSSL-Projekts sauber dokumentiert wäre. Da man dort aber lieber Referenzdokumentation anstatt einführendes Material verfasst, steht man als Neuling (oder aus der Übung Gekommener) zunächst ziemlich auf dem Schlauch.

Das SSL/TLS-System funktioniert durch eine hierarchische Signierung. An der Spitze steht eine Zertifizierungsstelle (engl. Certificate Authority, CA), der man zwangsweise vertrauen muss, wogegen es nicht unerhebliche Bedenken gibt. Alternative Lösungen werden diskutiert, aber die einzig praktikable Alternative als völlig dezentrales System bietet zurzeit nur GnuPG, was sich aber für Webseiten leider nicht durchgesetzt hat. Für den Moment muss man die unbefriedigende Situation also hinnehmen.

Die Zertifizierungsstelle besitzt ein sog. „Wurzelzertifikat“, das sich selbst beglaubigt hat. Ein Zertifikat ist kryptologisch gesehen dabei nichts anderes als ein öffentlicher Schlüssel, der von einem anderen öffentlichen Schlüssel — oder bei Wurzelzertifikaten eben mit sich selbst — signiert wurde. Die Zertifizierungsstelle hat nun besonders Acht darauf zu geben, dass der für ihr Wurzelzertifikat passende private Schlüssel nicht in falsche Hände gerät.

Aufgabe einer Zertifizierungsstelle ist es, an sie gerichtete Zertifizierungsanfragen zu bearbeiten. Eine Zertifizierungsanfrage läuft so ab, das zunächst eine Person sich selbst einen privaten Schlüssel erzeugt. Danach erstellt sie ein Certification Request, in welchem sie ihre Daten angibt (etwa der eigene bürgerliche Name) und welches sie anschließend der Zertifizierungsstelle als „distinguished Name“ (DN, Subject) übersendet. Diese prüft nun, ob die in dem Request angegebenen Daten der Wirklichkeit entsprechen (bzw. sollte dies tun). Ist dem so, nutzt sie ihren zu ihrem Wurzelzertifikat passenden privaten Schlüssel und signiert damit das Request, welches so zum vollwertigen Zertifikat erstarkt. Dieses sendet die Zertifizierungsstelle dann an den Antragsteller zurück (und stellt ihm das Prozedere teuer in Rechnung). Von nun an kann jeder, der der Zertifizierungsstelle, sprich ihrem Wurzelzertifikat, vertraut, auch automatisch dem Inhaber des auf das ausgestellte Zertifikat passenden Schlüssels vertrauen. In Wirklichkeit erstellen Zertifizierungsstellen oftmals noch Unter- oder Zwischenzertifikate, um nicht beständig ihren hochgeheimen Wurzelschlüssel benutzen zu müssen, aber so ist der grundlegende Ablauf.

Die Zertifikate selbst sind Dateien, die einen Baum von Informationen nach dem X.509-Standard enthalten, der derartig kompliziert ist, dass selbst erfahrene Programmierer bei der Implementation Fehler mit fatalen Folgen machen, was sehr für die hohe Qualität des Standards spricht. Das soll hier aber nicht Thema sein.

Neben den bereits angesprochenen Personenzertifikaten besonders relevant und für diesen Artikel maßgeblich sind Server- bzw. Dienstezertifikate, die die Inhaberschaft über einen Domainnamen im DNS beglaubigen und somit verschlüsselte Verbindungen etwa über HTTPS erlauben. Dabei wird als Namensfeld („Common Name“ im X.509-Jargon) üblicherweise der Domainname eingetragen, es ist aber auch möglich, den „Common Name“ für eine menschenlesbare Bezeichnung zu benutzen und den oder die passenden Domainnamen (es können auch mehrere sein) im Feld „subjectAltName“ unterzubringen. Vor einer solchen Konstellation stand der Autor, weshalb sie diesem Artikel zugrundeliegt.

OpenSSL-CA praktisch

Datei- und Verzeichnisstruktur

Es ist zweckdienlich, für die neue PKI ein eigenes Verzeichnis anzulegen.

$ cd /tmp
$ mkdir pki
$ cd pki

Sodann müssen einige Verzeichnisse angelegt werden, auf die das Kommando openssl ca später Zugriff nehmen wird, sowie einige Dateien aus demselben Grunde:

$ mkdir certs crl newcerts private
$ touch index.txt
$ echo 01 > serial

Diese Informationen lassen sich mit etwas Sucherei der Manpage ca(1) entnehmen. Die Verzeichnsise und Dateien erfüllen folgende Funktionen:

certs
Vorgesehenes Verzeichnis für die ausgestellten Zertifikate. Man muss die Zertifikate aber noch selbst hineinschieben (und sinnvoll benennen).
crl
Verzeichnis für Zertifikatswiderrufe (Certificate Revocation List, CRL). Für diesen Artikel ungenutzt.
newcerts
Hier legt openssl ca ein neu ausgestelltes Zertifikat ab, welches anschließend manuell in das Verzeichnis certs/ verschoben werden muss.
private
Hier wird der private Schlüssel der Zertifizierungsstelle drin liegen.
index.txt
Datenbank aller ausgestellten Zertifikate in Textform. Darf zu Anfang leer sein, muss aber existieren.
serial
Seriennummer des nächsten Zertifikats. Muss mindestens zweistellig sein, sonst verweigert OpenSSL die Arbeit.

Die Dateien index.txt und serial hält OpenSSL selbst auf dem aktuellen Stand, sie müssen nur einmal initial angelegt werden.

Konfiguration von OpenSSL

Der nun folgende Teil ist derjenige, der dem Autor jedes Mal neu Kopfschmerzen verursacht, weil die Konfigurationsdatei von OpenSSL extrem umfangreich und vor allem unübersichtlich ist, weil sie Gebrauch von zahlreichen Querverweisen sowie von Abschnitten mit besonderen Namen macht, deren Dokumentation man mit der Lupe in den Manpages suchen muss. Ausgangspunkt ist die von jeder Linux-Distribution mitgelieferte Datei /etc/ssl/openssl.cnf, welche jedoch für Zwecke dieser PKI so stark angepasst wird, dass eine Kopie sich nicht lohnt. Ein Blick in diese Datei kann aber nicht schaden. Beim Lesen dieser oder der unten beschriebenen Datei ist dabei im Auge zu behalten, dass sie Programme konfiguriert, die genau inverse Sichtweisen auf das Geschehen haben:

Bringt man diese beiden Themenkomplexe durcheinander, ist die OpenSSL-Konfiguration unverständlich.

Es wird also nunmehr eine Datei openssl.cnf mit folgendem Inhalt angelegt:

# Arbeite im aktuellen Arbeitsverzeichnis
HOME     = .
RANDFILE = .rnd

# Magicher Abschnitt [ca]. Wird vom Kommando "openssl ca" als
# erstes eingelesen. Querverweis auf Abschnitt CA_default.
[ ca ]
default_ca = CA_default

# Einstellungen für unsere Zertifizierungsstelle.
[ CA_default ]

# Pfadeinstellungen. Muss zu den Verzeichnissen passen, die
# wir zuvor angelegt haben. $dir wird mit dem Wert von "dir"
# ersetzt, welcher hier das aktuelle Arbeitsverzeichnis ist.
# Die CRL-Optionen nutzen wir in unserem Setup nicht.
dir = .
certs = $dir/certs
crl_dir = $dir/crl
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem
serial = $dir/serial
crlnumber = $dir/crlnumber
crl = $dir/crl.pem
private_key = $dir/private/cakey.pem
RANDFILE = $dir/private/.rand

# WICHTIGE OPTION. Diese Option gibt an, welche X.509-Erweiterungen
# *standardmäßig* in signierte Zertifikate eingebaut werden sollen,
# auch dann, wenn sie nicht angefordert wurden. Mit der Kommando-
# zeilenoption "-extensions" kann ein anderer als der hier ange-
# gebene Abschnitt gewählt werden.
x509_extensions = v3_ca

# Honoriere die vom Certification Request angegebenen Werte.
# Ohne diese Option werden nur die oben genannten x509_extensions
# beglaubigt. Gibt es Kollisionen, geht mit "copyall" (wie hier)
# das vor, was im Certification Request genannt ist; mit "copy"
# geht das vor, was hier in der Konfiguration steht.
copy_extensions = copyall

# Aus originaler openssl.cnf übernommen, ich weiß nicht recht,
# was es ist...
name_opt = ca_default
cert_opt = ca_default

# Standardmäßig über 1 Jahr zertifizieren
default_days        = 365
default_crl_days= 30
default_md        = default
preserve        = no

# Welche Werte zwingend vorhanden sein müssen, regelt die sog.
# "Policy".
policy                = policy_match

# Die oben referenzierte Policy wird hier definiert.
# "match" bedeutet, dass das zu Certificate Request
# dem Wert des CA-Zertifikats entsprechen muss, "optional"
# bedeutet, der Schlüssel ist optional, "supplied" bedeutet,
# dass der Schlüssel vorhanden sein muss, sein Wert jedoch
# keine Rolle Spielt.
[ policy_match ]
countryName                = match
stateOrProvinceName        = match
organizationName        = match
organizationalUnitName        = optional
commonName                = supplied
emailAddress                = optional

########################################
# Magischer Abschnitt [req].
# Wird von "openssl req" als erstes eingelesen,
# wenn ein neues Certificate Request erstellt werden
# soll.

[ req ]
# 4096 Bit sollten sicher sein.
default_bits                = 4096
default_keyfile         = privkey.pem

# Querverweis auf Abschnitt, der den DN definiert.
distinguished_name        = req_distinguished_name

# Kopiert
attributes  = req_attributes
string_mask = utf8only

# Welche X.509-Erweiterungen standardmäßig angefordert werden sollen.
# "x509_extensions" kommt nur bei Selbstignierung zum Tragen (also
# dann, wenn ein Wurzelzertifikat erzeugt wird). Ansonsten greift
# "req_extensions", was somit der Regelfall ist.
x509_extensions        = v3_ca
req_extensions = server_cert

# Der standardmäßig vorgeschlagene DN für neue Certification
# Requests. Die Felder sind recht selbsterklärend.
# Beachte hierzu aber https://superuser.com/questions/512673/#comment1084446_641055
[ req_distinguished_name ]
countryName                        = Country Name (2 letter code)
countryName_default                = DE
countryName_min                        = 2
countryName_max                        = 2

stateOrProvinceName                = State or Province Name (full name)
stateOrProvinceName_default        = Baden-Württemberg

localityName                        = Locality Name (eg, city)
localityName_default                = Stuttgart

0.organizationName                = Organization Name (eg, company)
0.organizationName_default        = Ich AG

commonName                        = Common Name (e.g. server FQDN or YOUR name)
commonName_max                        = 64

emailAddress                        = Email Address
emailAdress_default                = ich@example.com
emailAddress_max                = 64

# Kopiert
[ req_attributes ]
challengePassword                = A challenge password
challengePassword_min                = 4
challengePassword_max                = 20

unstructuredName                = An optional company name

########################################
# Es folgen die Standarderweiterungs-
# sets. Diese können per Kommandozeilen-
# option "-extensions" ausgewählt werden.
########################################

########################################
# Nutzerzertifikat

[ usr_cert ]

# Keine Unter-CA erlauben.
basicConstraints=CA:FALSE

# Nutzungstyp. "nsCertType" ist ein älterer Standard als "keyUsage",
# es schadet nicht, beides anzugeben. Hier: Reiner Nutzer, d.h.
# vor allem kein Ausgeben als Server zuzulassen.
nsCertType = client, email
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

########################################
# Serverzertifikat
# Ganz ähnlich wie schon oben, nur halt
# mit Serverstatus.

[ server_cert ]
basicConstraints = CA:FALSE
nsCertType = server
extendedKeyUsage = serverAuth
keyUsage = digitalSignature, keyEncipherment

########################################
# CA-Zertifikat

# Dies darf nur und nur für solche Zertifikate verwandt werden,
# die Unterzertifikate beglaubigen können sollen dürfen!

[ v3_ca ]

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer

basicConstraints = CA:true
keyUsage = cRLSign, keyCertSign
nsCertType = sslCA, emailCA

Erzeugung der Zertfikate

Wurzelzertifikat

Hat man diese grundlegenden Prozesse hinter sich, wird der private Schlüssel für das Wurzelzertifikat generiert. Dieser wird dabei in der Datei private/cakey.pem abgelegt. Wer mag, kann zusätzlich noch strenge Zugriffsrechte an die Datei anlegen. Im folgenden Beispiel wird ein 4096 Bit großer RSA-Schlüssel erzeugt; allgemein empfohlen werden wenigstens 2048 Bit.

$ cd private
$ openssl genrsa -out cakey.pem 4096
Generating RSA private key, 4096 bit long modulus
...................................................................................................++
..............................................................................++
e is 65537 (0x10001)

Nun erzeugt man das dazugehörige Wurzelzertifikat. Dabei wird mithilfe der Kommandozeilenoption -extensions v3_ca explizit die Erstellung eines Zertifikats mit CA-Fähigkeiten angefordert, welche für ein Wurzelzertifikat unabdingbar sind. Es handelt sich dabei um nichts anderes als einen Verweis auf den entsprechenden Abschnitt in der oben angelegten Konfigurationsdatei, der zusätzlich zum Abschnitt [req] eingelesen wird und dessen Option req_extensions überschreibt. Tatsächlich sollte es auch ohne -extensions v3_ca gehen, weil der alternative Konfigurationsparameter x509_extensions greifen sollte, aber es wird klarer, wie die Technik funktioniert, wenn man den Parameter explizit auf der Kommandozeile angibt.

$ cd ..
$ openssl req -new -x509 -days 3650 \
  -extensions v3_ca -key private/cakey.pem \
  -out cacert.pem -config openssl.cnf

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [DE]:
State or Province Name (full name) [Baden-Württemberg]:
Locality Name (eg, city) [Stuttgart]:
Organization Name (eg, company) [Ich AG]:
Common Name (e.g. server FQDN or YOUR name) []:Test MAster-CA
Email Address []:

Dieses Zertifikat sollte man nun kontrollieren. Besonderes Augenmerk ist auf die Feststellung zu verwenden, ob die X.509-Erweiterungen korrekt gesetzt wurden und mit denen in der Konfigurationsdatei übereinstimmen. Es sollte etwa so aussehen:

$ openssl x509 -in cacert.pem -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 16014081171043375726 (0xde3d71fa944fda6e)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=DE, ST=Baden-W\xC3\x83\xC2\xBCrttemberg, L=Stuttgart, O=Ich AG, CN=Test MAster-CA
        Validity
            Not Before: Nov  2 20:09:35 2015 GMT
            Not After : Oct 30 20:09:35 2025 GMT
        Subject: C=DE, ST=Baden-W\xC3\x83\xC2\xBCrttemberg, L=Stuttgart, O=Ich AG, CN=Test MAster-CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
		Modulus:
 [...]
                 Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                4A:5A:70:B4:C6:FF:41:BD:72:76:E9:82:77:FB:6E:DE:00:61:86:8C
            X509v3 Authority Key Identifier:
                keyid:4A:5A:70:B4:C6:FF:41:BD:72:76:E9:82:77:FB:6E:DE:00:61:86:8C

            X509v3 Basic Constraints:
                CA:TRUE
            X509v3 Key Usage:
                Certificate Sign, CRL Sign
            Netscape Cert Type:
                SSL CA, S/MIME CA
    Signature Algorithm: sha256WithRSAEncryption
 [...]
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----

Damit steht das Wurzelzertifikat und es kann nunmehr daran gegangen werden, ein Zertifikat für einen Webserver auzustellen. Als Beispiel wird hier zunächst ein einfaches Serverzertifikat für die Domain example.com ausgestellt.

Normales Webserver-Zertifikat

Wie zuvor beginnt man mit dem privaten Schlüssel.

$ openssl genrsa -out example.com.privatekey.pem 4096
Generating RSA private key, 4096 bit long modulus
....................................................++
..++
e is 65537 (0x10001)

Nun wird ein Certification Request erstellt. Dabei darauf achten, dass im [req] der openssl.cnf auch wirklich ein server_crt erstellt wird.

$ openssl req -new -out example.com.req \
  -key example.com.privatekey.pem -config openssl.cnf

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [DE]:
State or Province Name (full name) [Baden-Württemberg]:
Locality Name (eg, city) [Stuttgart]:
Organization Name (eg, company) [Ich AG]:
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Kontrollieren, ob das Certificate Request den Anforderungen entspricht, also insbesondere, ob die X.509-Erweiterungen korrekt angefordert werden:

$ openssl req -in example.com.req -text
[...]
        Requested Extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Cert Type:
                SSL Server
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Key Usage:
                Digital Signature, Key Encipherment
[...]

Stimmt alles, kann nunmehr von unserer CA signiert werden. Man beachte hier, wie openssl ca zunächst den magischen Abschnitt [ca] einliest und so den öffentlichen (Zertifikat) und privaten Schlüssel der CA findet. Unbedingt zu kontrollieren ist dabei, ob die nun von openssl ca vorgeschlagenen X.509-Extensions mit den eigenen Erwartungen übereinstimmen. Optionen wie copy_extensions in der Konfiguration führen zuweilen zu unerwarteten Ergebnissen, bei denen auch schonmal ein nsCertType von „client“ festgelegt wird bei gleichzeitigem extendedUsage als „TLS Web Server“ (weil ersterer im Request nicht enthalten war). Also dreimal kontrollieren, bevor man die Nachfrage bestätigt.

$ openssl ca -config openssl.cnf \
  -out example.com.cert.pem -infiles example.com.req

Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Nov  2 20:27:36 2015 GMT
            Not After : Nov  1 20:27:36 2016 GMT
        Subject:
            countryName               = DE
            stateOrProvinceName       = Baden-W\C3\BCrttemberg
            organizationName          = Ich AG
            commonName                = example.com
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                FF:C6:2F:16:FE:A7:FB:32:CE:BB:13:FC:23:5B:80:68:8F:A0:E4:A5
            X509v3 Authority Key Identifier:
                keyid:4A:5A:70:B4:C6:FF:41:BD:72:76:E9:82:77:FB:6E:DE:00:61:86:8C

            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Cert Type:
                SSL Server
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Key Usage:
                Digital Signature, Key Encipherment
Certificate is to be certified until Nov  1 20:27:36 2016 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Zertifikat noch einmal kontrollieren:

$ openssl x509 -in example.com.cert.pem -text
[...]
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                FF:C6:2F:16:FE:A7:FB:32:CE:BB:13:FC:23:5B:80:68:8F:A0:E4:A5
            X509v3 Authority Key Identifier:
                keyid:4A:5A:70:B4:C6:FF:41:BD:72:76:E9:82:77:FB:6E:DE:00:61:86:8C

            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Cert Type:
                SSL Server
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Key Usage:
                Digital Signature, Key Encipherment
[...]

Perfekt. Das Zertifikat ist fertig. Die Dateien example.com.cert.pem und newcerts/01-example.com sind im übrigen identisch; letzte wird automatisch erzeugt, erstere ist das Ergebnis der Kommandozeilenoption -out. Man hätte auch darauf verzichten können.

Noch in den Verzeichnissen aufräumen:

$ mv newcerts/01.pem certs/01-example.com
$ rm example.com.req
$ mkdir /tmp/distribute-example.com
$ mv example.com.privatekey.pem /tmp/distribute-example.com
$ mv example.com.cert.pem /tmp/distribute-example.com
$ cp cacert.pem /tmp/distribute-example.com

Das Verzeichnis /tmp/distribute-example.com enthält nunmehr alles, was ein Webserver-Administrator benötigt: Sein privater Schlüssel, das beglaubigte Zertifikat, und das CA-Zertifikat.

Zertifikat mit besonderen Anforderungen

Wie schon eingangs erwähnt gibt es zuweilen Situationen, die nicht mit den Standardwerten aus der openssl.cnf abgedeckt werden können. So werden etwa an XMPP-Server-Zertifikate eine ganze Reihe Anforderungen gestellt. Ist man mit einer solchen Anforderung konfrontiert, empfiehlt sich das Anlegen einer separaten Konfigurationsdatei allein für die Erstellung des zugehörigen Certification Requests. Für ein XMPP/Jabber-Server-Zertifikat könnte eine Datei xmpp.cnf etwa so aussehen:

HOME = .
RANDFILE = rand

oid_section = new_oids

# Define shortcuts we use later in the subjectAltName
[new_oids]
xmppAddr = 1.3.6.1.5.5.7.8.5
SRVName = 1.3.6.1.5.5.7.8.7

# Special name [req] as per req(1) manpage.
[req]
default_bits = 4096
default_keyfile = privkey.pem
distinguished_name = xmpp_server_req_dn
req_extensions = xmpp_server_req_usage
x509_extensions = xmpp_server_req_usage
prompt = no

[xmpp_server_req_dn]
countryName = DE
stateOrProvinceName = Baden-Württemberg
localityName = Stuttgart
organizationName = Ich AG
commonName = XMPP-Server der Ich AG
emailAddress = admin@example.com

[xmpp_server_req_usage]
basicConstraints = CA:FALSE
nsCertType = server
extendedKeyUsage = serverAuth
keyUsage = digitalSignature, keyEncipherment
subjectAltName = @xmpp_server_req_altnames

[xmpp_server_req_altnames]
DNS.0 = example.com
DNS.1 = *.example.com
otherName.0 = xmppAddr;FORMAT:UTF8,UTF8:example.com
otherName.1 = SRVName;IA5STRING:_xmpp-client.example.com
otherName.2 = SRVName;IA5STRING:_xmpp-server.example.com

In der Anwendung sieht das dann so aus; man beachte beim zweiten Befehl die Angabe der neu angelegten Konfigurationsdatei xmpp.cnf:

$ openssl genrsa -out xmpp.privatekey.pem 4096
$ openssl req -new -out xmpp.req \
  -key xmpp.privatekey.pem -config xmpp.cnf
$ openssl req -in xmpp.req -text
[...]
        Requested Extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Cert Type: 
                SSL Server
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Key Usage: 
                Digital Signature, Key Encipherment
            X509v3 Subject Alternative Name: 
                DNS:example.com, DNS:*.example.com,
		othername:<unsupported>, othername:<unsupported>,
		othername:<unsupported>
[...]
$ openssl ca -config openssl.cnf \
  -out xmpp.cert.pem -infiles xmpp.req
Using configuration from openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Nov  2 20:43:44 2015 GMT
            Not After : Nov  1 20:43:44 2016 GMT
        Subject:
            countryName               = DE
            stateOrProvinceName       = Baden-W\C3\BCrttemberg
            organizationName          = Ich AG
            commonName                = XMPP-Server der Ich AG
            emailAddress              = admin@example.com
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                10:13:C1:E9:3D:D5:0E:A7:AF:BB:62:BA:E3:D4:36:47:AA:70:20:22
            X509v3 Authority Key Identifier:
                keyid:4A:5A:70:B4:C6:FF:41:BD:72:76:E9:82:77:FB:6E:DE:00:61:86:8C

            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Cert Type:
                SSL Server
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Key Usage:
                Digital Signature, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:example.com, DNS:*.example.com, othername:<unsupported>, othername:<unsupported>, othername:<unsupported>
Certificate is to be certified until Nov  1 20:43:44 2016 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
$ openssl x509 -in xmpp.cert.pem -text
[...]
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                10:13:C1:E9:3D:D5:0E:A7:AF:BB:62:BA:E3:D4:36:47:AA:70:20:22
            X509v3 Authority Key Identifier: 
                keyid:4A:5A:70:B4:C6:FF:41:BD:72:76:E9:82:77:FB:6E:DE:00:61:86:8C

            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Cert Type: 
                SSL Server
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Key Usage: 
                Digital Signature, Key Encipherment
            X509v3 Subject Alternative Name: 
                DNS:example.com, DNS:*.example.com, othername:<unsupported>, othername:<unsupported>, othername:<unsupported>
[...]
$ mkdir /tmp/distribute-xmpp
$ mv xmpp.cert.pem /tmp/distribute-xmpp
$ mv xmpp.privatekey.pem /tmp/distribute-xmpp
$ mv newcerts/02.pem certs/02-xmpp.pem
$ rm xmpp.req
$ mv xmpp.cnf /var/backup

So erhält man dann auch ein Zertifikat mit dem etwas schwierigen subjectAltName.

Abschluss

Der Artikel basiert zu wesentlichen Teilen auf dieser Anleitung für FreeBSD, erläutert aber für mich schwerer verständliche Punkte wie insbesondere die OpenSSL-Konfiguration erheblich ausführlicher. Es wäre wünschenswert, wenn das OpenSSL-Projekt mehr und vor allem reichlich kommentierte Beispielkonfigurationen für verschiedene Anwendungsfälle bereitstellte, und noch besser wäre es, die Konfigurationen für drei völlig unterschiedliche Programme — req, x509 und ca — auch optisch-funktional in unterschiedlichen Konfigurationsdateien unterzubringen. Am allerbesten allerdings wäre die Abschaffung dieser unsäglichen Mengen von Quervweisen in der Konfiguration und die sichtbare und ins Auge stechende Dokumentation magischer Abschnittsnamen.

Valete.