Private CA für SSL Kommunikation im Intranet

Allgemeines

Hat man Webseiten im Intranet die mittels HTTP erreichbar sind zeigt der Browser, so er überhaupt den Zugriff zuläst, immer eine Warnung an, das ist lästig und unschön. Erstellt man hierfür ein sogenanntes self signed Zertifikat um SSL zu ermöglichen wir das noch schlimmer, man muss je nach Browser noch ein oder mehrere Sicherheitsabfragen bestätigen und kann die Seite teilweise gar nicht mehr aufrufen.

Einzige brauchbare Lösung ist eine eigene CA mit der man dann das Zertifikat signieren kann. Diese wir je nach dem im OS oder im Browser importiert und als vertrauenswürdig definiert. Leider muss das bei jedem Client geschehen. Doch dazu später.

Nun gibt es hierzu im Internet jede Menge Beschreibungen, die eine sind sehr spezifisch, die anderen sehr allgemein oder überhaupt unbrauchbar.

Die einzige wirklich gute die ich fand war diese, allerdings, wie man aus dem Kommentare sieht, auch noch mit kleinen Fehlern.

Im Kommentar von  "MacCliF commented on 10 May 2020" wurde noch eine verbesserte Lösung vorgestellt auf die ich dann aufgebaut habe. Das heisst ich habe das erstellen der CA im wesentlichen übernommen und stelle die wesentlichen Befehle unten zusammen, sie können 1 zu 1 per cut and paste in ein Linux Terminal eingegeben werden, natürlich unter Eingabe der eigenen Daten. Wobei der Name der CA eigentlich egal ist. Voraussetzung ist ein installiertes openssl. Dringend empfohlen wird dafür ein eigenes Verzeichnis anzulegen, sowie mindestens von der CA ein Backup anzulegen, am besten das komplette Verzeichnis sichern. Ich empfehle auch den Originalartikel mit den Kommentare zu lesen.

Da man die CA nur einmal benötigt und damit Zertifikate für jede beliebig Domain ausstellen kann habe ich das so gelassen. Allerdings wird man vermutlich mehrere Zertfikate erstellen müssen, dann ist es lästig jedes mal viele Zeilen Befehle eingeben zu müssen, bzw. bei cut and paste die Daten anzupassen, was auch Fehler trächtig ist. Daher habe ich hierfür ein Shell Script gebaut, das am Anfang alle nötigen Daten abfragt und dann alles wesentliche private key, CSR, crt und auch ein crt Bundle erstellt.

 

 

Die CA erstellen

Die nachfolgenden Befehle können so 1 zu 1 im Linux Terminal eingegeben werden. Wer es etwas "schöner" haben passt die Daten an seine Bedürfnisse an.

 

$ mkdir CA_DIR
$ cd CA_DIR

 

Dann da mit einem beliebigen Editor, z.B. nano, vi, die Datei req.ca.cnf erstellen mit folgendem Inhalt

 

# The main section is named req because the command we are using is req
# (openssl req ...)
[ req ]
# This specifies the default key size in bits. If not specified then 512 is
# used. It is used if the -new option is used. It can be overridden by using
# the -newkey option. 
default_bits = 2048

# This is the default filename to write a private key to. If not specified the
# key is written to standard output. This can be overridden by the -keyout
# option.
default_keyfile = ca.key

# If this is set to no then if a private key is generated it is not encrypted.
# This is equivalent to the -nodes command line option. For compatibility
# encrypt_rsa_key is an equivalent option. 
encrypt_key = no

# This option specifies the digest algorithm to use. Possible values include
# md5 sha1 mdc2. If not present then MD5 is used. This option can be overridden
# on the command line.
default_md = sha256

# if set to the value no this disables prompting of certificate fields and just
# takes values from the config file directly. It also changes the expected
# format of the distinguished_name and attributes sections.
prompt = no

# if set to the value yes then field values to be interpreted as UTF8 strings,
# by default they are interpreted as ASCII. This means that the field values,
# whether prompted from a terminal or obtained from a configuration file, must
# be valid UTF8 strings.
utf8 = yes

# This specifies the section containing the distinguished name fields to
# prompt for when generating a certificate or certificate request.
distinguished_name = my_req_distinguished_name


# this specifies the configuration file section containing a list of extensions
# to add to the certificate request. It can be overridden by the -reqexts
# command line switch. See the x509v3_config(5) manual page for details of the
# extension section format.
x509_extensions = my_extensions

[ my_req_distinguished_name ]
C=DE
ST=Bundesland
L=Stadt
O=FooOrgCA
OU=FooOrgCA
CN=FooOrgCA

 

[ my_extensions ]
keyUsage=critical, digitalSignature, keyEncipherment, keyCertSign
basicConstraints=critical,CA:TRUE
extendedKeyUsage=critical,serverAuth
subjectKeyIdentifier = hash

 

Jetzt das CA selfsigned Zertifikat erstellen, notfalls Daten anpassen. die Gültigkeit ist 10 Jahre, das sollte im Normalfall reichen, ansonsten in der Kommandozeile anpassen.

 

$ openssl req -new -x509 -days 3650 -config req.ca.cnf -out ca.crt

 

Nun brauchen wir noch eine sign.ca.cnf zum signieren.

Wir erstellen die sign.ca.cnf mit folgendem Inhalt.

 

# we use 'ca' as the default section because we're usign the ca command
# we use 'ca' as the default section because we're usign the ca command
[ ca ]
default_ca = my_ca

[ my_ca ]
#  a text file containing the next serial number to use in hex. Mandatory.
#  This file must be present and contain a valid serial number.
serial = ./serial

# the text database file to use. Mandatory. This file must be present though
# initially it will be empty.
database = ./index.txt

# specifies the directory where new certificates will be placed. Mandatory.
new_certs_dir = ./newcerts

# the file containing the CA certificate. Mandatory
certificate = ./ca.crt

# the file contaning the CA private key. Mandatory
private_key = ./ca.key

# the message digest algorithm. Remember to not use MD5
default_md = sha256

# for how many days will the signed certificate be valid
# Max 825
default_days = 365

# a section with a set of variables corresponding to DN fields
policy = my_policy

[ my_policy ]
# if the value is "match" then the field value must match the same field in the
# CA certificate. If the value is "supplied" then it must be present.
# Optional means it may be present. Any fields not mentioned are silently
# deleted.
countryName = match
stateOrProvinceName = supplied
organizationName = supplied
commonName = supplied
organizationalUnitName = optional
commonName = supplied

 

Um das CA Zertifikat anzusehen, u.U. Namen anpassen

 

$ openssl x509 -in ca.crt -noout -text

 

Nun können Sie das CA Zertifikat schon in Ihrem Browser oder OS importieren. Für Windows und OS X benötigen Sie noch eine Format Konvertierung. Achtung notfalls wieder Namen/Daten anpassen. Die dabei folgende Abfrage nach einem Passwort einfach mit der Eingabetaste beantworten, also leer lassen.

$ openssl pkcs12 -in ca.crt -export -nokeys -out ca.pfx

jetzt noch 2 Dateinen und ein Verzeichnis anlegen.

 

$ echo '01' > serial; touch index.txt; mkdir newcerts

 

Das war es mit den einmaligen Vorbereitungen.

Das Script zum erstellen der Zertifikate

Nun erstellen wir das Script zum Erstellen der Zertifikate. Ich habe das make_cert.sh genannt.

Wir erstellen als diese Datei mit folgendem Inhalt, bitte passen Sie auch hier Daten an Ihre Bedürfnisse an, die entsprechenden Stellen sind kursiv dargestellt..

 

#! /bin/bash
# Script to generate SSL certificates signed by private CA
# We need this to avoid browser wornings accessing intranet
# websites vi SSL
# this ist bases on gist.github.com/Soarez/9688998
# and the comment from "MacCliF commented on 10 May 2020"
# who compressed the beased tutorial to the necessary and
# added .cnf files to make everything easier
# As the CA has to be created only once I did not make a extra script
# to create the CA and you should refer to the refenrend article

# first we need to get all information
echo -e "\033[1m"
echo -e "\033[31mYou are about generating a new server certficate"
echo -e "is that what you want, continue (y/N) \033[0m"
read ok
if [ "$ok"  != "y" ] ; then
        echo -e "\033[31mleaving script!\033[0m"
        exit 1
fi
echo -e "\033[1mYou will be ask all needed information\033[30m"
echo -e "\033[1mEnter Servername: [test]\033[30m"
read server_name
# echo -e "\033[1myou entered \033[32m$server_name\033[30m"
echo -e "\033[1mEnter Domain: [example]\033[30m"
read domain_name
# echo -e "\033[1myou entered \033[32m$domain_name\033[30m"
echo -e "\033[1mEnter TLD: [org]\033[30m"
read tld_name
#echo -e "\033[1myou entered \033[32m$tld_name\033[30m"
# check certificate exists
if test -f  "$server_name"_"$domain_name"_"$tld_name".crt ; then
        echo  -e "\033[31m Certificate for $server_name.$domain_name.$tld_name exists\033[30m"
        echo  -e "\033[31m leaving script\033[30m"
        exit 1
else
        echo ""
        echo "Filenames created will be"
        key_filename="$server_name"_"$domain_name"_"$tld_name".key
        echo "$key_filename"
        csr_filename="$server_name"_"$domain_name"_"$tld_name".csr
        echo "$csr_filename"
        crt_filename="$server_name"_"$domain_name"_"$tld_name".crt
        echo "$crt_filename"
        req_filename=req."$server_name"_"$domain_name"_"$tld_name".cnf
        echo "$req_filename"
        bundle_filename="$server_name"_"$domain_name"_"$tld_name".bundle.crt
        echo "$bundle_filename"
        host_name="$server_name"."$domain_name"."$tld_name"
        echo -e "your full server name is:\033[32m" 
        echo $host_name
        echo -e "\033[0m"
        echo "correct? [y/N]"
        read ok
        if [ "$ok"  != "y" ] ; then
                echo -e "\033[31mleaving script!\033[0m"
                exit 1
        else
                echo -e "\033[31mcontinue createing certificate\033[0m"
        fi
fi
echo [ req ] > ./$req_filename
echo default_bits = 2048 >> ./$req_filename
echo default_keyfile = "$key_filename" >> ./$req_filename
echo encrypt_key = no >> ./$req_filename
echo default_md = sha256 >> ./$req_filename
echo prompt = no >> ./$req_filename
echo utf8 = yes >> ./$req_filename
echo distinguished_name = my_req_distinguished_name >> ./$req_filename
echo [ my_req_distinguished_name ] >> ./$req_filename
echo C=DE >> ./$req_filename
echo ST=Bundesland >> ./$req_filename
echo L=Stadt >> ./$req_filename
echo O=Ihre Firma >> ./$req_filename
echo OU=Abteilung >> ./$req_filename
echo CN="$host_name" >> ./$req_filename
echo [ my_extensions ] >> ./$req_filename
echo keyUsage=critical, digitalSignature, keyEncipherment >> ./$req_filename
echo basicConstraints=critical,CA:FALSE >> ./$req_filename
echo extendedKeyUsage=critical,serverAuth >> ./$req_filename
echo subjectAltName=@my_subject_alt_names >> ./$req_filename
echo subjectKeyIdentifier = hash >> ./$req_filename
echo [ my_subject_alt_names ] >> ./$req_filename
echo DNS.1 = "$host_name" >> ./$req_filename
echo "create CSR file"
#echo openssl req -new -out $csr_filename -config $req_filename
openssl req -new -out $csr_filename -config $req_filename
echo "create CRT file"
#echo openssl ca -config sign.ca.cnf -extfile $req_filename -extensions my_extensions -out $crt_filename -infiles $csr_filename
openssl ca -config sign.ca.cnf -extfile $req_filename -extensions my_extensions -out $crt_filename -infiles $csr_filename
echo "create bundle"
#echo cat $crt_filename mueller-knoche-ca.crt > $bundle_filename
cat $crt_filename mueller-knoche-ca.crt > $bundle_filename

 

Jetzt noch das Script ausführbar machen.

 

$ chmod + x make_cert.sh

 

Und schon kann es losgehen. Das Script starten, die Kontrollabfrage mit "y" beantworten, dann die abgefragten Daten eingeben. Als ersten den Hostnamen (nur den Hostnamen), dann die Domain und dann die TLD und wieder die Kontrollabfrage mit "y" beantworten. Ist schon ein Zertifikat für diesen Host vorhanden bricht das Script ab. Ansonsten werden  die nötigen Dateien und Zertifikate erstellt. Am Ende kommen nochmal 2 Abfragen diese sind mit "y" zu beantworten, sonst gibt es keine Zertifikate.

Erstellte Dateien.

req.hostname_Domain_TLD.cnf wir intern benötigt

hostname_Domain_TLD.key privater Schlüssel des Zertifikats 

hostname_Domain_TLD.csr der Certifikate Signing Request

hostname_Domain_TLD.crt das Zertifikat selbst

hostname_Domain_TLD.bundle.crt eine aus dem CA Zertifikat und dem Server Zertifikat zusammengesetzte Datei die manchmal benötigt wird.

Wo und wie die Schlüssel und Zertifikate benötigt bzw. importiert/verwendet werden hängt von Ihrer Umgebung ab.

Was ich nicht berücksichtigt habe sind mehrere Namen für den selben Server, dazu muss das Script angepasst werden, siehe dazu auch die Original Doku.