05 — Lokale Zertifizierungsstelle für SSL-Zertifikate mit OpenSSL

Zuletzt geändert: 3. November 2020
Geschätzte Lesezeit: 7 min

Sowas hätte ich gerne für meine Entwick­lung­sumge­bung unter OS X, um für die Entwick­lung­sumge­bung mit ihren ver­schiede­nen *.local.web Domains SSL Zer­ti­fikate erstellen zu kön­nen. Ausser­dem ist es mal span­nend zu sehen wie so eine CA aufge­baut wird. 

Als Zer­ti­fizierungsstelle (CA) zu fungieren bedeutet, mit kryp­tographis­chen Paaren aus pri­vat­en Schlüs­seln und öffentlichen Zer­ti­fikat­en umzuge­hen. Das allererste kryp­tographis­che Paar, das ich erstelle, ist das Root-Paar. Dieses beste­ht aus 

  • dem Wurzelschlüs­sel - ca.key.pem
  • und dem Wurzelz­er­ti­fikat — ca.cert.pem
  • Dieses Paar bildet die Iden­tität der CA.

Nor­maler­weise sig­niert die Root-CA Serv­er- oder Client-Zer­ti­fikate nicht direkt. Die Root-CA wird immer nur dazu ver­wen­det, eine oder mehrere Zwis­chen-CAs zu erstellen, denen die Root-CA ver­traut, Zer­ti­fikate in ihrem Namen zu sig­nieren. Das ermöglicht, den Root-Schlüs­sel offline und so weit wie möglich unbe­nutzt zu hal­ten, da jede Kom­pro­mit­tierung des Root-Schlüs­sels katas­trophal ist. Es ist bewährte Prax­is, das Wurzel­paar in ein­er sicheren Umge­bung zu erstellen. Ide­al­er­weise sollte das auf einem voll­ständig ver­schlüs­sel­ten Com­put­er geschehen, der per­ma­nent vom Inter­net getren­nt ist. 

Falls openSSL noch nicht instal­liert ist sollte ich das jet­zt machen: 

brew install openssl export PATH=$(brew --prefix openssl):$PATH

A: Arbeitsverzeichnis erstellen

…und darin eine Zer­ti­fizierungsstelle und ein Sternz­er­ti­fikat erstellen. 

mkdir -p /usr/local/opt/ca cd /usr/local/opt/ca mkdir -p certs crl newcerts private chmod 700 private mkdir --parents intermediate/certs mkdir --parents intermediate/crl mkdir --parents intermediate/newcerts mkdir --parents intermediate/private mkdir --parents intermediate/csr
  • certs — Hier liegt der Pub­lic Key der Zertifikatsstelle
  • crl ‑cer­tifi­cate revo­ca­tion list —  Eine Liste, die die Ungültigkeit von Zer­ti­fikat­en beschreibt. Sie ermöglicht es, festzustellen, ob ein Zer­ti­fikat ges­per­rt oder wider­rufen wurde und warum.
  • pri­vate — Hier liegt der pri­vate Schlüs­sel der CA
  • csr — Hier liegen die Anfragen?

Weit­ere Dateien, die OpenSSL zur Erstel­lung von Zer­ti­fikat­en benötigt: 

touch index.txt echo 1000 > serial touch intermediate/index.txt echo 1000 > intermediate/serial
  • ser­i­al — Das File spe­ichert die zulet­zt vergebene ein­deutige Seri­en­num­mer. Die Hexa­dez­i­malzahl muss min­destens zwei Stellen haben.
  • index.txt — Das File lis­tet die von der CA aus­gestell­ten Zertifikate.

Ein langes, kom­plex­es Pass­wort in die Datei passwort.txt schreiben und speichern: 

nano passwort.txt

Die Datei mit einem kürz­eren und bess­er merk­baren Pass­wort ver­schlüs­seln (TODO wird momen­tan nicht benutzt): 

openssl enc -aes256 -salt -in passwort.txt -out passwort.enc

Die ver­schlüs­selte Datei kontrollieren: 

cat passwort.enc

Zur Kon­trolle das lange Pass­wort nochmal entschlüsseln 

openssl enc -aes256 -salt -d -in passwort.enc

und dann die nicht ver­schlüs­selte Datei mit rm -f passwort.txt löschen. 

B: OpenSSL-Konfiguration erstellen

Eine benutzerdefinierte OpenSSL-Kon­fig­u­ra­tion (openssl.cnf) erstellen, die für die Erstel­lung ein­er Zer­ti­fizierungsstelle (Root-SSL-Zer­ti­fikat) geeignet ist. 

  1. Der Abschnitt [ ca ] ist oblig­a­torisch. Hier weisen wir OpenSSL an, die Optio­nen aus der Sek­tion [ CA_default ] zu verwenden.
  2. Der Abschnitt [ CA_default ] enthält eine Rei­he von Stan­dard­e­in­stel­lun­gen. Stelle sich­er, dass du das Verze­ich­nis deklar­i­erst, das du zuvor gewählt hast (/usr/local/opt/ca).
  3. Wende policy_strict für alle Root-CA-Sig­na­turen an, da die Root-CA nur zur Erstel­lung von Zwis­chen-CAs ver­wen­det wird.
  4. Wende policy_loose für alle Zwis­chen-CA-Sig­na­turen an, da die Zwis­chen-CA Serv­er- und Client-Zer­ti­fikate sig­niert, die von ein­er Vielzahl von Drit­tan­bi­etern stam­men können.
  5. Optio­nen aus dem Abschnitt [ req ] wer­den bei der Erstel­lung von Zer­ti­fikat­en oder Zer­ti­fikatssig­nierungsan­forderun­gen angewendet.
  6. Der Abschnitt [ req_distinguished_name ] deklar­i­ert die Infor­ma­tio­nen, die nor­maler­weise in ein­er Zer­ti­fikat­sun­terze­ich­nungsan­forderung erforder­lich sind. Gib option­al einige Stan­dard­w­erte an.
  7. Die näch­sten Abschnitte sind Erweiterun­gen, die beim Sig­nieren von Zer­ti­fikat­en angewen­det wer­den kön­nen. Wenn du beispiel­sweise das Befehlszeile­nar­gu­ment -extensions v3_ca übergib­st, wer­den die in [ v3_ca ] fest­gelegten Optio­nen angewen­det. Ich wende die Erweiterung v3_ca an, wenn ich das Stam­mz­er­ti­fikat erstelle.
  8. Ich wende die Erweiterung v3_ca_intermediate an, wenn ich das Zwis­chen­z­er­ti­fikat erstelle. pathlen:0 stellt sich­er, dass es keine weit­eren Zer­ti­fizierungsstellen unter­halb der Zwis­chen-CA geben kann.
  9. Ich wende die Erweiterung usr_cert an, wenn ich Client-Zer­ti­fikate sig­niere, wie sie z.B. für die Remote-Benutzer­authen­tifizierung ver­wen­det werden.
  10. Ich wende die Erweiterung server_cert beim Sig­nieren von Serv­er-Zer­ti­fikat­en, wie sie beispiel­sweise für Web­serv­er ver­wen­det wer­den, an.
  11. Die Erweiterung crl_ext wird bei der Erstel­lung von Zer­ti­fikatswider­ruf­s­lis­ten automa­tisch angewendet.
  12. Ich wende die ocsp — Erweiterung bei der Unterze­ich­nung des OCSP-Zer­ti­fikats (Online Cer­tifi­cate Sta­tus Pro­to­col) an.
# OpenSSL root CA configuration file. # Copy to `/usr/local/opt/ca/openssl.cnf`. [ ca ] # `man ca` default_ca = CA_default [ CA_default ] # Directory and file locations. dir = /usr/local/opt/ca certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts database = $dir/index.txt serial = $dir/serial RANDFILE = $dir/private/.rand # The root key and root certificate. private_key = $dir/private/ca.key.pem certificate = $dir/certs/ca.cert.pem # For certificate revocation lists. crlnumber = $dir/crlnumber crl = $dir/crl/ca.crl.pem crl_extensions = crl_ext default_crl_days = 30 # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha256 name_opt = ca_default cert_opt = ca_default default_days = 375 preserve = no policy = policy_strict [ policy_strict ] # The root CA should only sign intermediate certificates that match. # See the POLICY FORMAT section of `man ca`. countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ policy_loose ] # Allow the intermediate CA to sign a more diverse range of certificates. # See the POLICY FORMAT section of the `ca` man page. countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] # Options for the `req` tool (`man req`). default_bits = 2048 distinguished_name = req_distinguished_name string_mask = utf8only # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha256 # Extension to add when the -x509 option is used. x509_extensions = v3_ca [ req_distinguished_name ] # See <https://en.wikipedia.org/wiki/Certificate_signing_request>. countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address # Optionally, specify some defaults. countryName_default = DE stateOrProvinceName_default = NRW localityName_default = Bergisch Gladbach 0.organizationName_default = Carsten Nichte organizationalUnitName_default = Software-Entwicklung emailAddress_default = meine@mailadresse.de [ v3_ca ] # Extensions for a typical CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ v3_intermediate_ca ] # Extensions for a typical intermediate CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ usr_cert ] # Extensions for client certificates (`man x509v3_config`). basicConstraints = CA:FALSE nsCertType = client, email nsComment = "OpenSSL Generated Client Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth, emailProtection [ server_cert ] # Extensions for server certificates (`man x509v3_config`). basicConstraints = CA:FALSE nsCertType = server nsComment = "OpenSSL Generated Server Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth crlDistributionPoints = URI:http://www.local.web/intermediate.crl.pem [ crl_ext ] # Extension for CRLs (`man x509v3_config`). authorityKeyIdentifier=keyid:always [ ocsp ] # Extension for OCSP signing certificates (`man ocsp`). basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, digitalSignature extendedKeyUsage = critical, OCSPSigning [ san_env ] subjectAltName=DNS:local.web,DNS:*.local.web

C: Zertifizierungsstelle schaffen

1.) Einen pri­vat­en Schlüs­sel erzeu­gen. Alle Zer­ti­fikate wer­den mit einem Pass­wort ver­schlüs­selt. Ich set­ze hier mypass­word ein. Bitte das lange Pass­wort von oben nehmen. 

openssl genrsa -aes256 \ -passout pass:mypassword \ -out private/ca.key.pem 4096

Wichtig: Ein Back­up des pri­vat­en Schlüs­sels machen (und gut sich­ern, zB. auf einem USB-Stick). Wenn der ver­loren geht, ist das SSL-Zer­ti­fikat wertlos. 

2.) Einen Cer­tifi­cate Sign­ing Request (CSR) erzeugen: 

openssl req -config openssl.cnf \ -key private/ca.key.pem -passin pass:mypassword \ -new -x509 -days 7300 -sha256 -extensions v3_ca \ -subj "/CN=OS-X Developer Root CA/O=Carsten Nichte/OU=Software-Entwicklung/C=CY/ST=NRW/L=Bergisch Gladbach" \ -out certs/ca.cert.pem
  • Das CA-Zer­ti­fikat entspricht dem „Pub­lic Key“ der Zertifizierungsstelle.
  • -key — Der zum kün­fti­gen Zer­ti­fikat gehörende pri­vate Schlüssel
  • -out  — Der Name des kün­fti­gen Zertifikats
  • -x509 — Erstellt ein selb­st­sig­niertes Zer­ti­fikat, anstelle ein­er gewöhn­lichen Zertifikats-Anfrage
  • Das Zer­ti­fikat ist 7300 Tage — also 20 Jahre — gültig
  • -exten­sions v3_ca — Die Ein­stel­lun­gen aus der Datei openssl.cnf unter dem Abschnitt v3_ca a wer­den berücksichtigt.

D: Zwischenzertifizierungsstelle einrichten

Eine Kon­fig­u­ra­tions­datei für die Zwis­chen­stelle einrichten: 

# OpenSSL root CA configuration file. # Copy to `/usr/local/opt/ca/intermediate/openssl.cnf`. # # This file is based on the instructions found in https://jamielinux.com/docs/openssl-certificate-authority/index.html [ ca ] # `man ca` default_ca = CA_default [ CA_default ] # Directory and file locations. dir = /usr/local/opt/ca/intermediate certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts database = $dir/index.txt serial = $dir/serial RANDFILE = $dir/private/.rand # The root key and root certificate. private_key = $dir/private/intermediate.key.pem certificate = $dir/certs/intermediate.cert.pem # For certificate revocation lists. crlnumber = $dir/crlnumber crl = $dir/crl/intermediate.crl.pem crl_extensions = crl_ext default_crl_days = 30 # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha256 name_opt = ca_default cert_opt = ca_default default_days = 375 preserve = no policy = policy_loose [ policy_strict ] # The root CA should only sign intermediate certificates that match. # See the POLICY FORMAT section of `man ca`. countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ policy_loose ] # Allow the intermed# Al CA to sign a more diverse range of certificates. # See the POLICY FORMAT section of the `ca` man page. countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] # Options for the `req` tool (`man req`). default_bits = 2048 distinguished_name = req_distinguished_name string_mask = utf8only # SHA-1 is deprecated, so use SHA-2 instead. default_md = sha256 # Extension to add when the -x509 option is used. x509_extensions = v3_ca [ req_distinguished_name ] # See . countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address # Optionally, specify some defaults. countryName_default = DE stateOrProvinceName_default = NRW localityName_default = Bergisch Gladbach 0.organizationName_default = Carsten Nichte organizationalUnitName_default = Software-Entwicklung emailAddress_default = meine@mailadresse.de [ v3_ca ] # Extensions for a typical CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ v3_intermediate_ca ] # Extensions for a typical intermediate CA (`man x509v3_config`). subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ usr_cert ] # Extensions for client certificates (`man x509v3_config`). basicConstraints = CA:FALSE nsCertType = client, email nsComment = "OpenSSL Generated Client Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth, emailProtection [ server_cert ] # Extensions for server certificates (`man x509v3_config`). basicConstraints = CA:FALSE nsCertType = server nsComment = "OpenSSL Generated Server Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth [ crl_ext ] # Extension for CRLs (`man x509v3_config`). authorityKeyIdentifier=keyid:always [ ocsp ] # Extension for OCSP signing certificates (`man ocsp`). basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer keyUsage = critical, digitalSignature extendedKeyUsage = critical, OCSPSigning [ san_env ] subjectAltName=DNS:local.web,DNS:*.local.web

1.) Den pri­vat­en Schlüs­sel für die Inter­me­di­ate-CA erstellen. Wird später fürs elek­tro­n­is­che Sig­nieren der aus­gestell­ten Zer­ti­fikate benutzt: 

cd /usr/local/opt/ca openssl genrsa -aes256 \ -passout pass:mypassword \ -out intermediate/private/intermediate.key.pem 4096

2.) Einen Cer­tifi­cate Sign­ing Request (CSR) erzeugen: 

openssl req -config intermediate/openssl.cnf -new -sha256 \ -key intermediate/private/intermediate.key.pem \ -passin pass:mypassword \ -subj "/CN=OS-X Developer Intermediate CA/O=Carsten Nichte/OU=Software-Entwicklung/C=CY/ST=NRW/L=Bergisch Gladbach" \ -out intermediate/csr/intermediate.csr.pem

Den erzeugten CSR würde ich jet­zt nor­maler­weise an eine offizielle Zer­ti­fizierungsstelle über­mit­teln bzw. hochladen. Ich mache das aber selb­st, und nutze wie oben meine eigene CA dafür: 

3.) CSR für Inter­me­di­ate-CA-Zer­ti­fikat mit Root-CA signieren. 

Ich unterze­ichne den CSR mit der eige­nen Root-CA . Das Zer­ti­fikat ist 3650 Tage — also rund 10 Jahren gültig. 

openssl ca -config openssl.cnf -extensions v3_intermediate_ca -batch \ -passin pass:mypassword \ -days 3650 -notext -md sha256 \ -in intermediate/csr/intermediate.csr.pem \ -out intermediate/certs/intermediate.cert.pem

In der index.txt im Root-CA-Verze­ich­nis nach­schauen, und das Zer­ti­fikat ansehen: 

openssl x509 -noout -text -in intermediate/certs/intermediate.cert.pem

4.) Zer­ti­fikats­kette erstellen. 

Die Zer­ti­fikats­kette / Cer­tifi­cate-Chain beste­ht aus den aneinan­der gehängten einzel­nen Zer­ti­fikat­en, das unter­ste der Hier­ar­chie ste­ht zuerst in der Datei, das ober­ste — also die Root-CA als letztes. 

cat intermediate/certs/intermediate.cert.pem \ certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem

5.) Mit der Root-CA ver­i­fizieren (todo, ver­i­fy-a-cer­tifi­cate-chain-using-openssl-ver­i­fy)

openssl verify -CAfile intermediate/certs/ca-chain.cert.pem

E: SSL-Zertifikat für den Entwicklungsserver erstellen 

Als let­ztes erstellen wir das SSL-Zer­ti­fikat für den Entwick­lungsserv­er, und kopieren alles nach /usr/local/etc/httpd/ssl.

cd /usr/local/opt/ca

1.) Den pri­vat­en Schlüs­sel für *.local.web erzeugen: 

openssl genrsa -aes256 \ -passout pass:mypassword \ -out intermediate/private/local.web.key.pem 2048

2.) Einen Cer­tifi­cate Sign­ing Request (CSR) für *.local.web erzeugen: 

openssl req -config /usr/local/opt/ca/intermediate/openssl.cnf \ -key intermediate/private/local.web.key.pem \ -extensions san_env \ -passin pass:mypassword \ -subj "//CN=*.local.web/O=Carsten Nichte/OU=Software-Entwicklung/C=CY/ST=NRW/L=Bergisch Gladbach" \ -new -sha256 -out intermediate/csr/local.web.csr.pem

Den erzeugten CSR würde ich jet­zt nor­maler­weise an eine offizielle Zer­ti­fizierungsstelle über­mit­teln bzw. hochladen. Ich mache das aber selb­st, und nutze meine eigene CA dafür: 

3.) Den CSR mit dem Inter­me­di­ate-CA-Zer­ti­fikat signieren 

openssl ca -config intermediate/openssl.cnf -batch \ -extensions server_cert -extensions san_env \ -days 1835 -notext -md sha256 \ -in intermediate/csr/local.web.csr.pem \ -passin pass:mypassword \ -out intermediate/certs/local.web.cert.pem
  • 1835 Tage gültig — als ca. 5 Jahre

4.) und alles kopieren… 

mkdir /usr/local/etc/httpd/ssl

Die Zertifikatskette, … 

cp intermediate/certs/ca-chain.cert.pem /usr/local/etc/httpd/ssl

…den Schlüssel, … 

openssl rsa -in intermediate/private/local.web.key.pem \ -out /usr/local/etc/httpd/ssl/local.web.key \ -passin pass:mypassword

Erzeugt aus der mit Pass­wort geschützten Schlüs­sel­datei local.web.key.pem eine Schlüs­sel­datei ohne Pass­wortschutz local.web.key. Die ungeschützte Schlüs­sel­datei muss vor unbefugtem Zugriff geschützt wer­den, zB. über Dateizugriffsrechte. 

…und das Zertifikat… 

cp intermediate/certs/local.web.cert.pem /usr/local/etc/httpd/ssl/local.web.crt

Bis hier­hin hat das super funk­tion­iert. Jet­zt will ich das Ganze natür­lich noch im Apache aktivieren und nutzen. 

F: SSL-Unterstützung im Apache aktivieren

codium /usr/local/etc/httpd/httpd.conf

Finde Zeile 

Listen 80

Eine weit­ere Zeile hinzufü­gen, damit Apache auch auf dem Stan­dard-HTTPS-Port, lauscht (443).

Listen 80 Listen 443

HTTPS-Unter­stützung im Apache aktivieren. Die fol­gen­den Zeilen suchen und das # davor entfernen. 

#LoadModule ssl_module lib/httpd/modules/mod_ssl.so #LoadModule http2_module lib/httpd/modules/mod_http2.so

Wird fortgesetzt.… 

War der Artikel hilfreich?
Nich so 0 0 von 0 fanden den Artikel hilfreich.
Ansichten: 74
Vorheriger Artikel: 05 — Apache
Nächster Artikel: 06 — PHP