Section author: Domagoj Margan, Vedran Miletić

Certifikat javnog ključa, certifikacijska tijela i sigurni poslužitelj

Note

Skup algoritama za šifriranje čije korištenje se preporuča kod rada sa TLS/SSL certifikatima mijenja se iz godine u godinu kako se pronalaze sigurnosni propusti u njima i kako procesna moć računala raste pa je dobro kod postavljanja TLS-a/SSL-a provjeriti aktualne najbolje prakse, primjerice one koje navodi Qualys SLL Labs, autor SSL Server Testa i SSL Client Testa.

Certifikat javnog ključa

Certifikat javnog ključa je elektronički dokument kojim se dokazuje posjedovanje javnog ključa. Certifikat sadrži informacije o ključu, subjektu koji je vlasnik ključa i digitalni potpis tijela koje je provjerilo sadržaj certifikata (tzv. izdavatelj certifikata, engl. certificate issuer). Ako je potpis valjan i ako softver koji radi s certifikatima vjeruje izdavatelju, tada je moguće ostvariti sigurnu komunikaciju sa subjektom koji je vlasnik ključa. Popisi izdavatelja kojima se vjeruje variraju među softverima; primjerice, u službenim razvojnim dokumentima web preglednika moguće je pronaći popise izdavatelja certifikata kojima vjeruju Mozilla Firefox i Google Chrome. Također, popis izdavatelja kojima se vjeruje varira i kroz vrijeme pa, primjerice, izdavatelj besplatnih TLS/SSL certifikata Let’s Encrypt od kraja srpnja 2018. godine ima povjerenje svih važnijih programa, uključujući Microsoft, Google, Apple, Mozillu, Oracle i Blackberry.

Stvaranje samopotpisanih certifikata

Generiranje samopotpisanog certifikata (engl. self-signed certificate) zbog složenosti zahtjeva uporabu više opcija i parametara.

Idući primjer pokazuje generiranje datoteke nazvane mojcertifikat.pem, koji će sadržavati privatni ključ i javni certifikat baziran na privatnome ključu. Moguće je odrediti broj dana valjanosti certifikata (u ovom slučaju jedna godina, znači 365), te hoće li ključ biti šifriran (opcija -nodes isključuje šifriranje). Pri stvaranju certifikata potrebno je odgovoriti na nekoliko pitanja kojima se u certfikat unose informacije o vlasniku.

$ openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mojkljuc.pem -out mojcertifikat.pem
Generating a RSA private key
.............................................++++
...................................................++++
writing new private key to 'mojcertifikat.pem'
-----
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) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Pregled i provjera valjanosti certifikata

Stvoreni certifikat možemo pregledati opcijom x509:

$ openssl x509 -in mojcertifikat.pem -noout -text
Certificate:
 Data:
     Version: 3 (0x2)
     Serial Number:
         6f:63:28:30:2c:2b:10:f3:ab:5c:91:50:27:76:97:7c:72:fc:a0:9c
     Signature Algorithm: sha256WithRSAEncryption
     Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
     Validity
         Not Before: Jun  8 17:13:57 2019 GMT
         Not After : Jun  7 17:13:57 2020 GMT
     Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
     Subject Public Key Info:
         Public Key Algorithm: rsaEncryption
             RSA Public-Key: (4096 bit)
             Modulus:
                 00:aa:b7:ca:a7:9d:23:dc:fc:1d:c8:6c:00:64:d7:
                 27:f1:91:c5:2a:e9:13:0d:ce:b3:3c:02:68:0e:70:
                 9c:33:fa:3c:f0:dc:3d:e4:cc:1f:73:f3:a1:46:d9:
                 (...)
             Exponent: 65537 (0x10001)
     X509v3 extensions:
         X509v3 Subject Key Identifier:
             39:E7:75:1C:26:F9:E7:0B:34:8A:A8:9D:AF:A9:43:B4:78:19:98:2D
         X509v3 Authority Key Identifier:
             keyid:39:E7:75:1C:26:F9:E7:0B:34:8A:A8:9D:AF:A9:43:B4:78:19:98:2D

         X509v3 Basic Constraints: critical
             CA:TRUE
 Signature Algorithm: sha256WithRSAEncryption
      40:dc:7e:ad:3f:1d:b6:22:08:2c:ac:71:a7:d0:46:32:66:9c:
      ce:24:c8:83:50:c2:8b:30:69:a4:40:b7:17:ce:8f:ab:75:ee:
      71:68:d2:61:3d:55:07:6c:e8:52:c6:88:f4:09:86:3a:98:de:
      (...)

Za provjeru valjanosti certifikata koristi se opcija verify. Ukoliko lokalna OpenSSL instalacija prepozna certifikat i potpis izdavatelja kojem se vjeruje, vraća se povratna poruka OK.

$ openssl verify valjani-certifikat.pem
OK

Ukoliko se pak pronađe problem sa certifikatom, javlja se obavijest o tome uz kratak opis problema, primjerice:

$ openssl verify sampotpisani-certifikat.pem
error 18 at 0 depth lookup:self signed certificate

Ovdje certifikat nije valjan jer je samopotpisan. Ukoliko se ne napravi iznimka, OpenSSL neće sampotpisani certifikat označiti kao valjan.

Certifikati su najčešće predodređeni za konkretni vremenski period, te OpenSSL javlja grešku ukoliko je taj period istekao.

$ openssl verify istekli-certifikat.pem
error 10 at 0 depth lookup:certificate has expired

Certifikacijsko tijelo

Warning

U nastavku navodimo postupak stvaranja certifikata certifikacijskog tijela u pojednostavljenoj varijanti kako bi razumijeli koji certifikati se gdje koriste, ali ne i kako se štite od krađe. U praksi bi bilo potrebno provesti dodatne mehanizme zaštite kako bi bili sigurni da su tajni ključevi zaista zaštićeni od krađe.

Stvaranje korijenskog ključa

Za korijenski ključ certifikacijskog tijela biramo 4096-bitni RSA:

$ openssl genrsa -out korijenskikljuc.pem 4096

Samopotpisivanje korijenskog certifikata

Potpisujemo sami sebi korijenski certifikat:

$ openssl req -x509 -new -nodes -key korijenskikljuc.pem -sha256 -days 3650 -out korijenskicertifikat.pem

Sada ovim certifikatom možemo potpisivati tuđe zahtjeve za potpisom certifikata. Ako bismo sada htjeli da naš certifikat i certifikati koje potpišemo bude valjani, organizacije kao Mozilla i Google bi ga morale dodati u popis izdavatelja kojima se vjeruje.

Potpisivanje certifikata putem zahtjeva za potpis certifikata

Kod primitka zahtjeva za potpisom certifikata, prvo provjeravamo zahtjev korištenjem opcije req:

$ openssl req -in zahtjev.pem -noout -text

Tuđe zahtjeve za potpisom certifikata možemo potpisati korištenjem opcije ca:

$ openssl ca -out potpisanicertifikat.pem -in zahtjev.pem

Potpuno ekvivalentno, za potpisivanje možemo koristiti korištenjem opcije x509:

$ openssl x509 -req -in zahtjev.pem -CA korijenskicertifikat.pem -CAkey korijenskikljuc.pem -CAcreateserial -out potpisanicertifikat.pem

Provjera potpisa

Potpisani certifikat pregledati korištenjem opcije x509:

$ openssl x509 -in potpisanicertifikat.pem -noout -text

Valjanost certifikata sada možemo provjeriti korištenjem opcije verify:

$ openssl verify -CAfile korijenskicertifikat.pem potpisanicertifikat.pem

Sigurni poslužitelj

Komunikaciju na internetu danas nemoguće je zamisliti bez HTTPS-a i TLS/SSL certifikata koji ga omogućuju. Danas se HTTPS uključuje rutinski (u pojedinim poslužiteljima, kao što je Caddy, čak i automatski), a TLS/SSL šifriranje radi brzo i pouzdano. Međutim, nije uvijek bilo tako.

U travnju 2014. godine otkriven je ozbiljan propust u OpenSSL-u popularno nazvan Heartbleed, a službeno CVE-2014-0160. Zahvaljujući Hearbleedu bilo je moguće ukrasti privatne ključeve pridružene certifikatima. Iste je godine otkriven još jedan ozbiljan propust, ovaj put na razini protokola. Propust popularno nazvan POODLE, a službeno CVE-2014-3566, pogađa SSL verziju 3.0 i omogućuje dešifriranje šifriranog sadržaja od treće strane. Zanimljiv osvrt specifično na ta dva sigurnosna propusta i općenito na stanje koda OpenSSL-a dao je Marco Peereboom, direktor u tvrtci Company Zero, u svom eseju pod naslovom OpenSSL is written by monkeys.

U narednim mjesecima OpenSSL je donio novu sigurnosnu politiku, a zatim je tijekom 2015. godine doživio čišćenje koda, otkriveni su i popravljeni i drugi sigurnosni propusti, i još sigurnosnih propusta, i još čišćenja koda. Krajem 2015. godine stanje projekta i koda bilo je bitno bolje nego pred godinu dana.

S rastom optimizma oko najkorištenije implementacije TLS-a/SSL-a porasla je i njena primjena. Od početka 2016. godine do polovine 2019. godine postotak web stranica koje se učitavaju korištenjem HTTPS-a se udvostručio, a paralelno s time nastavlja rasti i broj (besplatnih) TLS/SSL certifikata koje izdaje Let’s Encrypt.

Klijentska strana

Za ostvarivanje TLS/SSL veze s poslužiteljima i testiranje istih, koristimo opciju s_client i parametar -connect. Poslužitelj mora imati omogućeno pristupanje putem TLS-a/SSL-a.

Primjerice, za HTTP preko TLS-a/SSL-a:

$ openssl s_client -connect example.com:443

Za SMTP preko TLS-a/SSL-a:

$ openssl s_client -connect example.com:465

Za IMAP preko TLS-a/SSL-a:

$ openssl s_client -connect example.com:993

Za POP-3 preko TLS-a/SSL-a:

$ openssl s_client -connect example.com:995

Za LDAP preko TLS-a/SSL-a:

$ openssl s_client -connect example.com:636

Nakon spajanja mougće je unositi naredbe. Konkretno, ukoliko smo ostvarili HTTPS vezu, moguće je unositi uobičajene HTTP naredbe (npr. GET i POST).

Za primjer se možemo spojiti HTTPS-om na CNPSLabov poslužitelj dostupan na adresi lab.miletic.net.

$ $ openssl s_client -connect lab.miletic.net:443
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = miletic.net
verify return:1
---
Certificate chain
0 s:CN = miletic.net
  i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
  i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGNDCCBRygAwIBAgISA97SuFP8MdJDGngVxAaK1xqTMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA1MjAxMDA2NTRaFw0x
OTA4MTgxMDA2NTRaMBYxFDASBgNVBAMTC21pbGV0aWMubmV0MIIBIjANBgkqhkiG
(...)
-----END CERTIFICATE-----
subject=CN = miletic.net

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3475 bytes and written 433 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: F1D513CE8C25CE7B6055A68FBB67A71D3F2A37CFCBEAEAA2AC3054A91A0E6048
    Session-ID-ctx:
    Master-Key: 9991DF56B54564C72EEAE4F08B40146BC5CDCDB06A3F7CB1EA92588D3E829EC82005DE618DD9587942FB1CDC80176D69
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 5c 0a a8 4c c2 06 86 83-3b b5 83 10 4e 09 47 ea   \..L....;...N.G.
    0010 - 31 ef da dd 9a f9 9e f5-35 a2 69 a4 64 dc 3d cd   1.......5.i.d.=.
    0020 - 69 4d 7f 32 b8 59 18 37-57 3f ad 29 68 5e 5f 4c   iM.2.Y.7W?.)h^_L
    0030 - b1 49 ef 4e 77 c2 b0 3a-94 5c e0 bd 60 2c d8 99   .I.Nw..:.\..`,..
    0040 - 5c d3 92 ee 66 d5 07 68-0a eb 8e e2 e0 7b 63 fe   \...f..h.....{c.
    0050 - 32 6f 1d b2 9e 0d b1 a3-31 d2 df 07 07 b7 33 fb   2o......1.....3.
    0060 - 75 50 a7 31 b8 ba 27 ad-24 ae 29 4b 17 8f 91 37   uP.1..'.$.)K...7
    0070 - c4 2b 3b e4 9b 6b c8 f1-75 c9 db 5a 52 f1 7f 4b   .+;..k..u..ZR..K
    0080 - e3 6c 87 97 37 95 ca 4b-ca c4 ef cb 9b 00 dd b4   .l..7..K........
    0090 - 50 b3 c4 a7 14 25 cc e8-0c 69 77 2e 1a ce 0a a0   P....%...iw.....
    00a0 - 05 7d fe 0e fd 77 65 46-68 06 40 07 d0 cf 4e f4   .}...weFh.@...N.
    00b0 - 19 58 86 37 09 f8 45 76-38 40 70 6b e8 16 83 ef   .X.7..Ev8@pk....
    00c0 - 19 c2 8f c3 44 9f c9 df-19 6b 3f 87 4d 5e 24 ed   ....D....k?.M^$.

    Start Time: 1560016165
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
---

Ovdje možemo primijetiti brojne informacije. Odmah na početku vidimo podatke o certifikatu (CN = miletic.net) i pripadnom lancu izdavatelja certifikata (CN = Let's Encrypt Authority X3 i CN = DST Root CA X3). Navedeni su algoritmi koji se koriste za potpis hashiranog sadržaja (SHA256, RSA), broj bajtova koje poslanih i primljenih kod ostvarivanja veze (3475 i 433) i certifikat je označen kao valjan (Verification: OK). Slijede podaci o korištenoj verziji TLS-a/SSL-a (TLSv1.2), kombinaciji algoritama za šifriranje koja se koristi (ECDHE-RSA-AES256-GCM-SHA384), duljini ključa poslužitelja (2048 bit), kompresiji, TLS ekstenziji Application-Layer Protocol Negotiation (ALPN) i drugim značajkama ostvarene veze. Na ovaj način opciju s_client možemo koristiti pri dijagnosticiranju problema sigurnih poslužitelja jer pokazuje brojne podatke koje ne vidimo kod normalnog korištenja web preglednika ili klijenata za elektroničku poštu.

Todo

Dodaj priču o Server Name Identificationu (SNI).

Više o opciji s_client i parametrima koje prima moguće je naći u pripadnoj stranici priručnika s_client(1ssl).

Poslužiteljska strana

Za testiranje stvorenih certifikata možemo koristiti opciju s_server s parametrima -key i -cert. Da bi pokrenuli poslužitelj koji se ponaša kao web poslužitelj, koristimo opciju -www, a vrata na kojima radi navodimo parametrom -port.

$ openssl s_server -key mojkljuc.pem -cert mojcertifikat.pem -port 49152 -www

Ovako pokrenuti web poslužitelj sluša dokle god ga ne zaustavimo. Naredbom nestat -ln možemo se uvjeriti da je pokrenut, a testiranje možemo vršiti cURL-om ili ranije spomenutom opcijom s_client. Ako koristimo curl, prvo se možemo uvjeriti da nismo pokrenuli poslužitelj koji odgovara na HTTP zahtjeve, već isključivo HTTPS:

$ curl http://localhost:49152/
curl: (52) Empty reply from server

Parametrom -k označavamo da prihvaćamo samopotpisani certifikat koji nije prošao provjeru:

$ curl -k https://localhost:49152/
<HTML><BODY BGCOLOR="#ffffff">
<pre>

s_server -key mojkljuc.pem -cert mojcertifikat.pem -port 49152 -www
Secure Renegotiation IS supported
Ciphers supported in s_server binary
TLSv1.3    :TLS_AES_256_GCM_SHA384    TLSv1.3    :TLS_CHACHA20_POLY1305_SHA256
TLSv1.3    :TLS_AES_128_GCM_SHA256    TLSv1.2    :ECDHE-ECDSA-AES256-GCM-SHA384
TLSv1.2    :ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2    :DHE-RSA-AES256-GCM-SHA384
(...)
Ciphers common between both SSL end points:
TLS_AES_256_GCM_SHA384     TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256
ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384
(...)
Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224:DSA+SHA224:DSA+SHA256:DSA+SHA384:DSA+SHA512
Shared Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224
Supported Elliptic Groups: X25519:P-256:X448:P-521:P-384
Shared Elliptic groups: X25519:P-256:X448:P-521:P-384
---
No server certificate CA names sent
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 6B58EB40672EBBDACCF5CE8130A1D3C42FE7E6E25C7A1687ADD6748E40062244
    Session-ID-ctx: 01000000
    Resumption PSK: 54FB47D2656F5B3321B0595D1C4FAF927AC124AED1A49EEA0E76EAAF5A99341CEFB6645CE5B8A30265E6B2EA3F896039
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1560018096
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
0 items in the session cache
0 client connects (SSL_connect())
0 client renegotiates (SSL_connect())
0 client connects that finished
9 server accepts (SSL_accept())
0 server renegotiates (SSL_accept())
6 server accepts that finished
0 session cache hits
0 session cache misses
0 session cache timeouts
0 callback cache hits
0 cache full overflows (128 allowed)
---
no client certificate available
</pre></BODY></HTML>

Za usporedbu, povezivanje na tako pokrenuti poslužitelj opcijom s_client izveli bi na način:

$ openssl s_client -connect localhost:49152
CONNECTED(00000003)
Can't use SSL_get_servername
depth=0 C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
verify error:num=18:self signed certificate
verify return:1
depth=0 C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
verify return:1
---
Certificate chain
0 s:C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
  i:C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIUb2MoMCwrEPOrXJFQJ3aXfHL8oJwwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTA2MDgxNzEzNTdaFw0yMDA2
(...)

Opcija s_server može se ponašati kao pravi web poslužitelj i posluživati HTML datoteke iz trenutnog direktorija korištenjem parametra -WWW umjesto -www. Više o opciji s_server moguće je naći u pripadnoj stranici priručnika s_server(1ssl).