Generowanie certyfikatów dla usług udostępnianych na routerze

Certyfikaty można tworzyć samemu, można także skorzystać z przygotowanego do tego celu narzędzi. Poniżej zostanie opisane generowanie certyfikatów z wykorzystaniem easy-rsa oraz certtool . Certyfikaty dobrze jest tworzyć na zwyczajnym komputerze, a nie na routerze z tego względu, że ten pierwszy ma większa moc obliczeniowa i sam proces trwa o wiele krócej. Innym aspektem jest wolne miejsce. Istnieją co prawda odpowiednie pakiety w repo OpenWRT, przy pomocy których to możemy stworzyć odpowiednie pliki ale te pakiety zajmują dość sporo miejsca, przy założeniu, że nie dysponujemy extrootem, bo ten nie zawsze okazuje się praktyczny. W każdym razie, jeśli chcemy generować certyfikaty na routerze, musimy doinstalować poniższe pakiety:

# opkg update
# opkg install certtool openvpn-easy-rsa libgnutls-openssl openssl-util openssl

Niektóre programy mają problemy z obsługą certyfikatów powstałych za sprawą easy-rsa , natomiast bez problemu obsługują certyfikaty stworzone przy pomocy certtool i vice versa. Także sami musimy ustalić, który pakiet znajdzie zastosowanie w naszym przypadku.

Poniższe kroki możemy przeprowadzić zarówno mając do dyspozycji normalną dystrybucję linuxa, np. ubuntu-live, jak i w samym OpenWRT.

Easy-RSA

W przypadku linuxa, po instalacji tego pakietu, w systemie mamy dostępne narzędzie make-cadir . Po wskazaniu pustego katalogu, zostanie w nim podlinkowanych szereg plików, które posłużą nam później przy generowaniu certyfikatów. Wyglądać to powinno mniej więcej tak:

# make-cadir /etc/easy-rsa
# ls -al /etc/easy-rsa
total 44K
drwx------ 2 morfik morfik 4.0K Sep 24 17:57 ./
drwxr-xr-x 3 morfik morfik 4.0K Sep 24 17:57 ../
lrwxrwxrwx 1 morfik morfik   28 Sep 24 17:57 build-ca -> /usr/share/easy-rsa/build-ca*
lrwxrwxrwx 1 morfik morfik   28 Sep 24 17:57 build-dh -> /usr/share/easy-rsa/build-dh*
lrwxrwxrwx 1 morfik morfik   31 Sep 24 17:57 build-inter -> /usr/share/easy-rsa/build-inter*
lrwxrwxrwx 1 morfik morfik   29 Sep 24 17:57 build-key -> /usr/share/easy-rsa/build-key*
lrwxrwxrwx 1 morfik morfik   34 Sep 24 17:57 build-key-pass -> /usr/share/easy-rsa/build-key-pass*
lrwxrwxrwx 1 morfik morfik   36 Sep 24 17:57 build-key-pkcs12 -> /usr/share/easy-rsa/build-key-pkcs12*
lrwxrwxrwx 1 morfik morfik   36 Sep 24 17:57 build-key-server -> /usr/share/easy-rsa/build-key-server*
lrwxrwxrwx 1 morfik morfik   29 Sep 24 17:57 build-req -> /usr/share/easy-rsa/build-req*
lrwxrwxrwx 1 morfik morfik   34 Sep 24 17:57 build-req-pass -> /usr/share/easy-rsa/build-req-pass*
lrwxrwxrwx 1 morfik morfik   29 Sep 24 17:57 clean-all -> /usr/share/easy-rsa/clean-all*
lrwxrwxrwx 1 morfik morfik   33 Sep 24 17:57 inherit-inter -> /usr/share/easy-rsa/inherit-inter*
lrwxrwxrwx 1 morfik morfik   28 Sep 24 17:57 list-crl -> /usr/share/easy-rsa/list-crl*
-rw-r--r-- 1 morfik morfik 7.7K Sep 24 17:57 openssl-0.9.6.cnf
-rw-r--r-- 1 morfik morfik 8.3K Sep 24 17:57 openssl-0.9.8.cnf
-rw-r--r-- 1 morfik morfik 8.2K Sep 24 17:57 openssl-1.0.0.cnf
lrwxrwxrwx 1 morfik morfik   27 Sep 24 17:57 pkitool -> /usr/share/easy-rsa/pkitool*
lrwxrwxrwx 1 morfik morfik   31 Sep 24 17:57 revoke-full -> /usr/share/easy-rsa/revoke-full*
lrwxrwxrwx 1 morfik morfik   28 Sep 24 17:57 sign-req -> /usr/share/easy-rsa/sign-req*
-rw-r--r-- 1 morfik morfik 2.1K Sep 24 17:57 vars
lrwxrwxrwx 1 morfik morfik   35 Sep 24 17:57 whichopensslcnf -> /usr/share/easy-rsa/whichopensslcnf*

Przy pomocy tych skryptów możemy wygenerować certyfikaty do ssh, openvpn, freeradiusa i wielu innych rzeczy, z tym, że one chyba raczej były projektowane głównie na potrzeby openvpn -- przynajmniej pakiet easy-rsa się bardzo często pojawia w tekstach o tworzeniu własnego VPN.

Z kolei w OpenWRT jest już przygotowany katalog /etc/easy-rsa/ i nie musimy nic więcej robić, zatem przechodzimy do niego.

Na sam początek edytujemy plik vars . Są w nim zdefiniowane zmienne konfiguracyjne używane przy generowaniu certyfikatów. Poniżej znajduje się zrzut mojej konfiguracji:

# easy-rsa parameter settings
# NOTE: If you installed from an RPM,
# don't edit this file in place in
# /usr/share/openvpn/easy-rsa --
# instead, you should copy the whole
# easy-rsa directory to another location
# (such as /etc/openvpn) so that your
# edits will not be wiped out by a future
# OpenVPN package upgrade.
# This variable should point to
# the top level of the easy-rsa
# tree.
export EASY_RSA="`pwd`"
#
# This variable should point to
# the requested executables
#
export OPENSSL="openssl"
export PKCS11TOOL="pkcs11-tool"
export GREP="grep"

# This variable should point to
# the openssl.cnf file included
# with easy-rsa.
export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA`
# Edit this variable to point to
# your soon-to-be-created key
# directory.
#
# WARNING: clean-all will do
# a rm -rf on this directory
# so make sure you define
# it correctly!
export KEY_DIR="$EASY_RSA/keys"
# Issue rm -rf warning
echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
# PKCS11 fixes
export PKCS11_MODULE_PATH="dummy"
export PKCS11_PIN="dummy"
# Increase this to 2048 if you
# are paranoid.  This will slow
# down TLS negotiation performance
# as well as the one-time DH parms
# generation process.
export KEY_SIZE=4096
# In how many days should the root CA key expire?
export CA_EXPIRE=3650
# In how many days should certificates expire?
export KEY_EXPIRE=3650
# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="SanFrancisco"
export KEY_ORG="NSA"
export KEY_EMAIL="morfik@nsa.com"
export KEY_OU="Security Division"
# X509 Subject Field
export KEY_NAME="morfik"
# PKCS11 Smart Card
# export PKCS11_MODULE_PATH="/usr/lib/changeme.so"
# export PKCS11_PIN=1234
# If you'd like to sign all keys with the same Common Name, uncomment the KEY_CN export below
# You will also need to make sure your OpenVPN server config has the duplicate-cn option set
# export KEY_CN="CommonName"

I to w zasadzie tyle jeśli chodzi o nasz wkład w proces tworzenia certyfikatów przy wykorzystaniu easy-rsa. Teraz już tylko wydajemy poniższe polecenia:

# source ./vars
# clean-all
# build-ca
Generating a 4096 bit RSA private key
...
# build-dh
Generating DH parameters, 4096 bit long safe prime, generator 2
This is going to take a long time
...

Tworzymy i podpisujemy certyfikat dla serwera:

# ./build-key-server server
Generating a 4096 bit RSA private key
...
Certificate is to be certified until Oct 22 14:42:40 2024 GMT (3650 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

Jeśli chodzi zaś o certyfikaty klienckie, to mamy trochę więcej opcji, bo możemy określić hasło oraz istnieje też możliwość zapakowania certyfikatów w plik .p12 . W zależności od potrzeb wpisujemy w terminal nazwę odpowiedniego skryptu. W tym przypadku jest to:

# build-key-pkcs12 client
Generating a 4096 bit RSA private key
...
Certificate is to be certified until Oct 22 14:44:36 2024 GMT (3650 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
Enter Export Password:
Verifying - Enter Export Password:

Do dyspozycji mamy także build-key-pass oraz build-key w zależności czy potrzebujemy zwykłego klucza (nie .p12).

Certtool

Kluczowe rolę przy tworzeniu certyfikatów jest dobranie parametru cn , który musi odpowiadać nazwie lub adresie IP danego hosta. Jeśli ta nazwa lub adres IP nie będzie się zgadzać z nazwą lub adresem IP danego hosta, to mogą wystąpić problemy przy uwierzytelnianiu.

Narzędzie certtool przyjmuje predefiniowaną konfigurację dla certyfikatów. Dlatego też stwórzmy kilka plików tekstowych, po jednym dla CA, serwera i dla każdego klienta. Te pliki umieścimy w katalogu /etc/CA/template/ , same certyfikaty zaś będą zlokalizowane w /etc/CA/ . Poniżej wzory plików pod certyfikaty:

Plik /etc/CA/template/ca_192.168.1.1
# X.509 Certificate options
#
# DN options
# The organization of the subject.
organization = "morfikownia"
# The organizational unit of the subject.
#unit = "sleeping dept."
# The state of the certificate owner.
#state = "Example"
# The country of the subject. Two letter code.
country = GB
# The common name of the certificate owner.
cn = "192.168.1.1"
# The serial number of the certificate. Should be incremented each time a new certificate is generated.
serial = 001
# In how many days, counting from today, this certificate will expire.
expiration_days = 3650
# Whether this is a CA certificate or not
ca
# Whether this key will be used to sign other certificates.
cert_signing_key
# Whether this key will be used to sign CRLs.
crl_signing_key

Plik /etc/CA/template/client_192.168.1.166

# X.509 Certificate options
#
# DN options
# The organization of the subject.
organization = "morfikownia"
# The organizational unit of the subject.
#unit = "sleeping dept."
# The state of the certificate owner.
state = "localhost"
# The country of the subject. Two letter code.
country = GB
# The common name of the certificate owner.
cn = "192.168.1.166"
# A user id of the certificate owner.
#uid = "scertowner"
# The serial number of the certificate. Should be incremented each time a new certificate is generated.
serial = 003
# In how many days, counting from today, this certificate will expire.
expiration_days = 3650
# X.509 v3 extensions
# DNS name(s) of the server
#dns_name = "server.example.com"
#dns_name = "server_alias.example.com"
# (Optional) Server IP address
ip_address = "192.168.1.1"
# Whether this certificate will be used for a TLS server
tls_www_server
# Whether this certificate will be used to encrypt data (needed
# in TLS RSA ciphersuites). Note that it is preferred to use different
# keys for encryption and signing.
encryption_key
signing_key

Plik /etc/CA/template/server_192.168.1.1

# X.509 Certificate options
#
# DN options
# The organization of the subject.
organization = "morfikownia"
# The organizational unit of the subject.
#unit = "sleeping dept."
# The state of the certificate owner.
state = "localhost"
# The country of the subject. Two letter code.
country = GB
# The common name of the certificate owner.
cn = "192.168.1.1"
# A user id of the certificate owner.
#uid = "scertowner"
# The serial number of the certificate. Should be incremented each time a new certificate is generated.
serial = 002
# In how many days, counting from today, this certificate will expire.
expiration_days = 3650
# X.509 v3 extensions
# DNS name(s) of the server
#dns_name = "server.example.com"
#dns_name = "server_alias.example.com"
# (Optional) Server IP address
ip_address = "192.168.1.1"
# Whether this certificate will be used for a TLS server
tls_www_server
# Whether this certificate will be used to encrypt data (needed
# in TLS RSA ciphersuites). Note that it is preferred to use different
# keys for encryption and signing.
encryption_key
signing_key

Przechodzimy teraz do katalogu /etc/CA/ i generujemy klucze prywatne na podstawie zdefiniowanych szablonów:

# certtool --generate-privkey --rsa --sec-param high --outfile ca_192.168.1.1.key
Generating a 3072 bit RSA private key...
# certtool --generate-privkey --rsa --sec-param high --outfile server_192.168.1.1.key
Generating a 3072 bit RSA private key...
# certtool --generate-privkey --rsa --sec-param high --outfile client_192.168.1.166.key
Generating a 3072 bit RSA private key...

Jeden z powyższych kluczy, a konkretnie ten mający w swojej nazwie ca musi zostać podpisany przez samego siebie, tak by mógł podpisywać inne certyfikaty:

# certtool --generate-self-signed --load-privkey ca_192.168.1.1.key --template ./template/ca_192.168.1.1 --outfile ca_192.168.1.1.crt
Generating a self signed certificate...
...
Signing certificate...

Teraz za pomocą tak stworzonego CA generujemy certyfikat dla serwera:

# certtool --generate-certificate --template template/server_192.168.1.1 --load-privkey server_192.168.1.1.key --load-ca-certificate ca_192.168.1.1.crt --load-ca-privkey ca_192.168.1.1.key --outfile server_192.168.1.1.crt
Generating a signed certificate...
...
Signing certificate...

Oraz dla klienta:

# certtool --generate-certificate --template template/client_192.168.1.166 --load-privkey client_192.168.1.166.key --load-ca-certificate ca_192.168.1.1.crt --load-ca-privkey ca_192.168.1.1.key --outfile client_192.168.1.166.crt
Generating a signed certificate...
...
Signing certificate...

Przesyłamy tak stworzone certyfikaty na odpowiednie maszyny przy pomocy scp. W tym przypadku, certyfikaty CA i serwera nie będą kopiowane -- trzeba tylko przesłać certyfikaty do maszyn klienckich. Trzeba także pamiętać by skopiować także certyfikat samego CA.

Taka mała uwaga -- standardowo wszystkie pliki .key nie są zaszyfrowane i pewnie w wielu przypadkach nie ma takiej potrzeby. Jeśli jednak życzymy sobie kluczy w formie zaszyfrowanej, możemy je zaszyfrować ręcznie po całym procesie generowania certyfikatów, przykładowo:

# openssl rsa -aes256 -in keys/client.key -out keys/client.key.encrypted
writing RSA key
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

A jeśli nie mamy pojęcia czy dany klucz jest zaszyfrowany czy też nie, możemy to sprawdzić przez:

# openssl rsa -text -noout -in keys/client.key 
Private-Key: (4096 bit)
modulus:
...

Jeśli po wydaniu powyższego polecenia nie zostaniemy poproszeni o hasło, znaczy, że klucz nie jest zaszyfrowany.

W tym przypadku wykorzystaliśmy dość długie klucze. Warto zrobić sobie test i zobaczyć jak radzi sobie nasza maszyna w kalkulacjach RSA. Taki benchmark można przeprowadzić wydając poniższe polecenie:

# openssl speed rsa
...
				  sign    verify    sign/s verify/s
rsa  512 bits 0.002821s 0.000226s    354.5   4421.8
rsa 1024 bits 0.013138s 0.000665s     76.1   1502.8
rsa 2048 bits 0.079758s 0.002217s     12.5    451.0
rsa 4096 bits 0.538947s 0.008252s      1.9    121.2

Powyższy wynik jest dla routera Archer C7 v2.