Generowanie samopodpisanego certyfikatu za pomocą openssl, który działa w Chrome 58

52

Od Chrome 58 nie akceptuje już certyfikatów z podpisem własnym, które polegają na Common Name: https://productforums.google.com/forum/#!topic/chrome/zVo3M8CgKzQ;context-place=topicsearchin/chrome/category $ 3ACanary% 7Csort: trafność% 7Cspell: false

Zamiast tego wymaga użycia Subject Alt Name. Wcześniej postępowałem zgodnie z tym przewodnikiem na temat generowania samopodpisanego certyfikatu: https://devcenter.heroku.com/articles/ssl-certificate-self, który działał świetnie, ponieważ wymagałem plików server.crti server.keyplików do tego, co robię. Muszę teraz wygenerować nowe certyfikaty, które obejmują SANjednak wszystkie moje próby, aby to zrobić, nie działały z Chrome 58.

Oto co zrobiłem:

Wykonałem kroki z wyżej wymienionego artykułu Heroku, aby wygenerować klucz. Następnie napisałem nowy plik konfiguracyjny OpenSSL:

[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name
req_extensions      = san
extensions          = san
[ req_distinguished_name ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com

Następnie wygenerowano za server.crtpomocą następującego polecenia:

openssl req \
-new \
-key server.key \
-out server.csr \
-config config.cnf \
-sha256 \
-days 3650

Jestem na komputerze Mac, więc otworzyłem server.crtplik za pomocą pęku kluczy i dodałem go do moich certyfikatów systemowych. Następnie ustawiłem na Always Trust.

Z wyjątkiem pliku konfiguracyjnego do ustawienia wartości SAN były to podobne kroki, których użyłem we wcześniejszych wersjach Chrome do wygenerowania i zaufania do samopodpisanego certyfikatu.

Jednak po tym nadal dostaję ERR_CERT_COMMON_NAME_INVALIDw Chrome 58.

bcardarella
źródło

Odpowiedzi:

62

Moje rozwiązanie:

openssl req \
    -newkey rsa:2048 \
    -x509 \
    -nodes \
    -keyout server.key \
    -new \
    -out server.crt \
    -subj /CN=dev.mycompany.com \
    -reqexts SAN \
    -extensions SAN \
    -config <(cat /System/Library/OpenSSL/openssl.cnf \
        <(printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')) \
    -sha256 \
    -days 3650

Status: działa dla mnie

bcardarella
źródło
2
świetne wykorzystanie podpowłoki. Myślę, że można to nieco uprościć:-config <(cat /System/Library/OpenSSL/openssl.cnf ; printf '[SAN]\nsubjectAltName=DNS:dev.mycompany.com')
jrwren
1
Nie pojawia się już błąd nazwy alternatywnej podmiotu, ale teraz pojawia się błąd dotyczący nazwy pospolitej, a ustawienie pobranego certyfikatu na „zawsze ufaj” nie działa. jakieś pomysły? @bcardarella
rugbert
2
W przypadku aktualizacji Chrome 59 certyfikat wyświetla następujący błąd: Występują problemy z łańcuchem certyfikatów witryny (net :: ERR_CERT_COMMON_NAME_INVALID).
theHarsh
1
Zmieniłem dev.company.namesię localhosti to pracował dla obsługujących lokalną witrynę rozwojowej z localhost. W systemie macOS musiałem również dodać certyfikat do pęku kluczy i ustawić SSL na „Zawsze ufaj”.
Daniel M.,
1
To zdecydowanie najprostsze rozwiązanie i nie wymaga wkręcania się za pomocą sslconf ani instalowania urzędu certyfikacji.
bp.
16

W systemie Windows zapisz ten skrypt w folderze SSL jako makeCERT.bat. Utworzy te pliki: example.cnf, example.crt, example.key

@echo off

REM IN YOUR SSL FOLDER, SAVE THIS FILE AS: makeCERT.bat
REM AT COMMAND LINE IN YOUR SSL FOLDER, RUN: makecert
REM IT WILL CREATE THESE FILES: example.cnf, example.crt, example.key
REM IMPORT THE .crt FILE INTO CHROME Trusted Root Certification Authorities
REM REMEMBER TO RESTART APACHE OR NGINX AFTER YOU CONFIGURE FOR THESE FILES

REM PLEASE UPDATE THE FOLLOWING VARIABLES FOR YOUR NEEDS.
SET HOSTNAME=example
SET DOT=com
SET COUNTRY=US
SET STATE=KS
SET CITY=Olathe
SET ORGANIZATION=IT
SET ORGANIZATION_UNIT=IT Department
SET EMAIL=webmaster@%HOSTNAME%.%DOT%

(
echo [req]
echo default_bits = 2048
echo prompt = no
echo default_md = sha256
echo x509_extensions = v3_req
echo distinguished_name = dn
echo:
echo [dn]
echo C = %COUNTRY%
echo ST = %STATE%
echo L = %CITY%
echo O = %ORGANIZATION%
echo OU = %ORGANIZATION_UNIT%
echo emailAddress = %EMAIL%
echo CN = %HOSTNAME%.%DOT%
echo:
echo [v3_req]
echo subjectAltName = @alt_names
echo:
echo [alt_names]
echo DNS.1 = *.%HOSTNAME%.%DOT%
echo DNS.2 = %HOSTNAME%.%DOT%
)>%HOSTNAME%.cnf

openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout %HOSTNAME%.key -days 3560 -out %HOSTNAME%.crt -config %HOSTNAME%.cnf
STWilson
źródło
13

Oto rozwiązanie, które działa dla mnie:

Utwórz klucz CA i certyfikat

# openssl genrsa -out server_rootCA.key 2048
# openssl req -x509 -new -nodes -key server_rootCA.key -sha256 -days 3650 -out server_rootCA.pem

Utwórz server_rootCA.csr.cnf

# server_rootCA.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=DE
ST=Berlin
L=NeuKoelln
O=Weisestrasse
OU=local_RootCA
[email protected]
CN = server.berlin

Utwórz plik konfiguracyjny v3.ext

# v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = server.berlin

Utwórz klucz serwera

# openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config <( cat server_rootCA.csr.cnf )

Utwórz certyfikat serwera

# openssl x509 -req -in server.csr -CA server_rootCA.pem -CAkey server_rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile v3.ext

Dodaj certyfikat i klucz do pliku strony Apache2, sekcja HTTPS (port 443)

SSLCertificateFile    /etc/apache2/ssl/server.crt
SSLCertificateKeyFile    /etc/apache2/ssl/server.key

Skopiuj server_rootCA.pem z serwera na komputer.

# scp [email protected]:~/server_rootCA.pem .

.. i dodaj go do przeglądarki Chromium

Chromium -> Setting -> (Advanced) Manage Certificates -> Import -> 'server_rootCA.pem'

WSZYSTKO ZROBIŁEŚ!

PS Zamiast tworzyć funkcjonalną parę certyfikatów CA i serwera (zgodnie z powyższymi instrukcjami) możesz po prostu wyłączyć nagłówki HSTS w konfiguracji serwera HTTP. Uniemożliwi to Chromium wymuszanie HTTPS i pozwoli użytkownikom kliknąć „Zaawansowane → przejdź do twojego.url (niebezpieczne)” bez konieczności uzyskiwania i instalowania niestandardowego certyfikatu CA (server_rootCA.pem). Innymi słowy - wyłączenie HSTS pozwoli na publiczne wyświetlanie Twojej witryny przez HTTP i / lub niezabezpieczone połączenie HTTPS (uwaga!).

W przypadku Apache2 dodaj następującą sekcję do pliku strony, sekcja HTTP (port 80)

Header unset Strict-Transport-Security
Header always set Strict-Transport-Security "max-age=0;includeSubDomains"

Testowane na Debian / Apache2.4 + Debian / Chromium 59

https://ram.k0a1a.net/self-signed_https_cert_after_chrome_58

binary.koala
źródło
Przebywanie trasy głównego urzędu certyfikacji, który później podpisuje poszczególne certyfikaty, jest jedynym sposobem, w jaki mogę uzyskać chrome do pełnego uwierzytelnienia; ma również tę zaletę, że potrzebowałem tylko, aby ludzie zainstalowali jeden certyfikat. Dzięki
geoff,
4
Czy ktoś może mi wyjaśnić, dlaczego wszyscy w tej dziedzinie wydają się używać bashism jak -config <( cat server_rootCA.csr.cnf )zamiast po prostu -config server_rootCA.csr.cnf?
Cezar
czy możesz zaktualizować swoją odpowiedź dotyczącą nagłówków apache, które mogą ominąć ten problem (nie mam nic przeciwko, że dotyczy to tylko lokalnych witryn w celach programistycznych i chciałbym uzyskać ogólne rozwiązanie bez konieczności generowania nowych certyfikatów za każdym razem). Czy możesz wskazać, gdzie powinna się znaleźć definicja wirtualnego hosta? Wypróbowałem kilka alternatyw i nadal nie mogę uzyskać dostępu do stron za pośrednictwem https. Dzięki
Nikos M.,
12

Istnieje kilka świetnych odpowiedzi, które podają przykłady, jak to zrobić, ale żadne nie wyjaśniają, co poszło nie tak podczas próby. OpenSSL może być czasami nieintuicyjny, dlatego warto przejść.

Po pierwsze, OpenSSL domyślnie ignoruje wszelkie wartości nazw wyróżniających podane w konfiguracji. Jeśli chcesz ich użyć, musisz dodać je prompt = no do swojej konfiguracji. Ponadto polecenie w formie pisemnej generuje tylko żądanie certyfikatu, a nie sam certyfikat, więc -dayspolecenie nic nie robi.

Jeśli generujesz żądanie certyfikatu za pomocą tego polecenia, które podałeś i sprawdzasz wynik, obecna jest alternatywna nazwa podmiotu:

$ openssl req -new -key server.key -out server.csr -config config.cnf -sha256
$ openssl req -text -noout -in server.csr
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:dev.mycompany.com
    Signature Algorithm: sha256WithRSAEncryption
         ...

Ale jeśli wygenerujesz certyfikat za pomocą polecenia w łączu heroku i sprawdzisz wynik, brak będzie alternatywnej nazwy podmiotu:

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
$ openssl x509 -text -noout -in server.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            89:fd:75:26:43:08:04:61
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Validity
            Not Before: Jan 21 04:27:21 2018 GMT
            Not After : Jan 21 04:27:21 2019 GMT
        Subject: C = US, ST = Massachusetts, L = Boston, O = MyCompany
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         ...

Powodem jest to, że domyślnie OpenSSL nie kopiuje rozszerzeń z żądania do certyfikatu. Zwykle certyfikat zostałby utworzony / podpisany przez urząd certyfikacji na podstawie żądania od klienta, a niektóre rozszerzenia mogłyby przyznać certyfikatowi większą moc niż zamierzał urząd certyfikacji, gdyby ślepo ufali rozszerzeniom zdefiniowanym w żądaniu.

Istnieją sposoby, aby powiedzieć OpenSSL, aby skopiowało rozszerzenia, ale IMHO to więcej pracy niż zwykłe udostępnianie rozszerzeń w pliku konfiguracyjnym podczas generowania certyfikatu.

Jeśli spróbujesz użyć istniejącego pliku konfiguracyjnego, nie zadziała, ponieważ sekcja najwyższego poziomu jest zaznaczona, [req]więc te ustawienia dotyczą tylko polecenia req, a nie polecenia x509. Nie jest konieczne posiadanie znacznika sekcji najwyższego poziomu, więc możesz po prostu usunąć ten pierwszy wiersz, a wtedy będzie on działał poprawnie zarówno w przypadku generowania żądań, jak i certyfikatu.

$ openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt -extfile config.cnf

Alternatywnie można użyć -x509argumentu reqpolecenia, aby wygenerować samopodpisany certyfikat w jednym poleceniu, zamiast najpierw utworzyć żądanie, a następnie certyfikat. W takim przypadku nie jest konieczne usuwanie [req]wiersza sekcji, ponieważ sekcja ta jest odczytywana i używana przez polecenie req.

$ openssl req -x509 -sha256 -days 365 -key server.key -out server.crt -config config.cnf

Podsumowując, oto zmodyfikowany plik konfiguracyjny użyty w powyższych poleceniach:

default_bits        = 2048
distinguished_name  = dn
x509_extensions     = san
req_extensions      = san
extensions          = san
prompt              = no
[ dn ]
countryName         = US
stateOrProvinceName = Massachusetts
localityName        = Boston
organizationName    = MyCompany
[ san ]
subjectAltName      = DNS:dev.mycompany.com
pavon
źródło
2
To jedyne wyjaśnienie, które pomogło mi zrozumieć, dlaczego certyfikat wyszedł bez SAN (w moim przypadku musiałem dołączyć x509_extensions do pliku konfiguracyjnego)
Daniel Beardsmore,
2

Moje rozwiązanie polega na tym, aby zachować openssl.cnftaką, jaka jest, i na samym końcu dodać nową sekcję, np. [ cert_www.example.com ]Gdzie www.example.com jest witryną, dla której chcę utworzyć certyfikat, i w nim wstawić to, subjectAltNameczego potrzebuję (i coś jeszcze). Oczywiście sekcja może mieć dowolną nazwę.

Następnie mogę uruchomić openssl reqpolecenie jak poprzednio, dodając tylko, -extensions cert_www.example.comaby jego zawartość została pobrana, i dodaję, -subjaby dodać bezpośrednio wszystkie informacje o nazwie DN.

Nie zapomnij zweryfikować zawartości certyfikatu po jego utworzeniu i przed użyciem za pomocą openssl x509 -text

Patrick Mevzek
źródło
1

Skrypt Bash z konfiguracją zapisaną

Jako skrypt powłoki, który powinien działać na różnych platformach z bash. Przyjmuje HOSTNAMEzestaw env dla powłoki lub podaje wybraną nazwę hosta, npself_signed_cert.sh test

set -e

if [ -z "$1" ]; then
  hostname="$HOSTNAME"
else
  hostname="$1"
fi

local_openssl_config="
[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = san_self_signed
[ req_distinguished_name ]
CN=$hostname
[ san_self_signed ]
subjectAltName = DNS:$hostname, DNS:localhost
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
extendedKeyUsage = serverAuth, clientAuth, timeStamping
"

openssl req \
  -newkey rsa:2048 -nodes \
  -keyout "$hostname.key.pem" \
  -x509 -sha256 -days 3650 \
  -config <(echo "$local_openssl_config") \
  -out "$hostname.cert.pem"
openssl x509 -noout -text -in "$hostname.cert.pem"

Powyżej mniej więcej wstrzykuje informacje o minimalnym pliku konfiguracyjnym, które potrzebuje openssl.

Uwaga, dołączony dodatkowo DNS:localhostjako SAN, aby ułatwić testowanie przez localhost. Usuń ten dodatkowy bit ze skryptu, jeśli go nie chcesz.

Kredyt

Odpowiedź bcardarelli jest świetna (nie można komentować / głosować z powodu niewystarczającej liczby powtórzeń). Jednak w odpowiedzi użyto istniejącej lokalizacji pliku konfiguracyjnego openssl, która jest specyficzna dla platformy ... stąd:

Pracuje dla mnie

Oczywiście trzeba po prostu znaleźć plik konfiguracyjny openssl dla własnej platformy i zastąpić poprawną lokalizację.

Test

Aby przetestować, zaimportuj test.cert.pemdo uprawnień Chrome w chrome://settings/certificates:

openssl s_server -key test.key.pem -cert test.cert.pem -accept 20443 -www &
openssl_pid=$!
google-chrome https://localhost:20443

I po testach

kill $openssl_pid
JPvRiel
źródło