Своё Certificate Authority — в 5 OpenSSL команд

Зачем это нужно?


Представим, у нас есть два сервера, работают они себе, и переодически они хотят, что-то друг у друга спросить по протоколу HTTP/HTTPS.

Протокол HTTP не безопасен и логично использовать протокол HTTPS для общения меду серверами.

Для организации такого общения нам нужно 2 SSL сертификата.

Если сервера пренадлежат одной организации, то может быть проще и безопасней подписывать сертификаты самостоятельно, а не покупать.

Создаем наше CA


Первая команда создаёт корневой ключ

openssl genrsa -out rootCA.key 2048

Для меня ключ 2048 bit достаточен, если вам хочется, вы можете использовать ключ 4096 bit.

Вторая команда создаёт корневой сертификат.

openssl req -x509 -new -key rootCA.key -days 10000 -out rootCA.crt

Отвечать на вопросы тут можно как душе угодно.

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 []:

10000 дней срок его годности, примерно столько живет сертификат, которым google требует подписывать андроид приложения для Google Play. Если вы паникер, подписывайте на год или два.

Все! Теперь мы можем создавать сертификаты для наших серверов и устанавливать корневой сертификат на наши клиентские машины.

Создаем сертификат подписаный нашим СА


Генерируем ключ.

openssl genrsa -out server101.mycloud.key 2048

Создаем запрос на сертификат.

openssl req -new -key server101.mycloud.key -out server101.mycloud.csr

Тут важно указать имя сервера: домен или IP (например домен server101.mycloud)

Common Name (eg, YOUR name) []: server101.mycloud

и подписать запрос на сертификат нашим корневым сертификатом.

openssl x509 -req -in server101.mycloud.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server101.mycloud.crt -days 5000

Теперь на клиенты нужно установить корневой сертификат rootCA.crt

rootCA.crt — можно давать друзьям, устанавливать, копировать не сервера, выкладывать в публичный доступ
rootCA.key — следует держать в тайне

Установка корневого сертификата


Windows

IE, Chrome — используют репозиторий сертификатов Windows.

Мой путь к нему таков:

Chrome — Settings — Manage Certificates…
Выбрать таб Trusted Root Certificate Authorities — Import — rootCA.crt
перезапустить Chrome

FireFox на виндоус имеет свой репозиторий.

Java имеет свой репозиторий.

Mac OS X

Safari, FireFox, Chrome — используют системный репозиторий.

Запускаем KeyChain Access.
Идём в меню File — Import Items (login или System) — выбираем файл rootCA.crt.
Когда нас спросят, отвечаем — Always Trust.



Для вашего личного Safari достаточно выбрать login.


В Ubuntu

sudo mkdir /usr/share/ca-certificates/extra
sudo cp rootCA.crt /usr/share/ca-certificates/extra/rootCA.crt
sudo dpkg-reconfigure ca-certificates
sudo update-ca-certificates

Программа сервера на Go

Программа сервера на Go myserver.go, которая использует наш подписаный сертификат.
package main

import (
	"log"
	"net/http"
)

func main() {
	http.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir("./files/"))))
	go func() {
		log.Fatal(http.ListenAndServeTLS(":8443", "server101.mycloud.crt", "server101.mycloud.key", nil))
	}()
	http.ListenAndServe(":8080", nil)
}

go run myserver.go

запустив программу на сервере server101.mycloud, ваш броузер не будет ругаться на страничку https://server101.mycloud:8443/, и откроет её как родную, если перед этим вы установили rootCA.crt в систему как корневой сертификат.

Сервер на Питоне


import BaseHTTPServer, SimpleHTTPServer, ssl

httpd = BaseHTTPServer.HTTPServer(('localhost', 8443), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket, certfile='server101.mycloud.pem', server_side=True)
httpd.serve_forever()


# скопируем клуч и сертификат в один файл
cat server101.mycloud.key server101.mycloud.crt > server101.mycloud.pem
# запустим сервер на питоне
python myserver.py

PS


Считаю важным упомянуть, что wildcard сертификаты не безопасны, если злоумышленник завладеет wildcard сертификатом с одного сервера, то этим он поставит под угрозу все остальные сервера. Виртуальные облачные сервера популярны как никогда. Часто фоновые задачи запускают на отдельных виртуальных серверах. Количество таких серверов постоянно растет. Своё Certificate Authority является важным элементом безопасности всей системы.
Поделиться публикацией
Комментарии 29
    +3
    openssl req -x509 -new -nodes -key rootCA.key -days 10000 -out rootCA.crt

    Ключ -nodes вроде лишний.
      +2
      Вы правы, он тут лишний
      +4
      Также, можно подписать сразу несколько доменов.
      Для этого создайте ini файл (напр. cert.ini) с такими дефолтами (пример для вилдкардов, но см. замечание в конце статьи)
      [req]
      distinguished_name = req_distinguished_name
      req_extensions = v3_req
      
      [req_distinguished_name]
      countryName = Country Name (2 letter code)
      countryName_default = US
      stateOrProvinceName = State or Province Name (full name)
      stateOrProvinceName_default = MN
      localityName = Locality Name (eg, city)
      localityName_default = Minneapolis
      organizationalUnitName  = Organizational Unit Name (eg, section)
      organizationalUnitName_default  = Domain Control Validated
      commonName = Internet Widgits Ltd
      commonName_max  = 64
      
      [ v3_req ]
      # Extensions to add to a certificate request
      basicConstraints = CA:FALSE
      keyUsage = nonRepudiation, digitalSignature, keyEncipherment
      subjectAltName = @alt_names
      
      [alt_names]
      DNS.1 = *.ev.dev
      DNS.2 = *.*.ev.dev
      DNS.3 = *.*.*.ev.dev
      DNS.4 = system.dev
      IP.1 = 192.168.1.200
      
      

      и скормите в ключе -config так -config cert
        +1
        Подборка ссылочек. gist.github.com/garethrees/5480560
          0
          Что то у меня не получается. На последнем этапе:
          openssl x509 -req -in server101.mycloud.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server101.mycloud.crt -days 5000
          

          Получаю сертификат без SAN

          OpenSSL 1.0.2k-fips 26 Jan 2017
            0

            А где вы тут конфиг указываете с [alt_names]? CA не подписывает то, о чём не знает, в том числе игнорирует расширение subjectAltName

              0
              Он у меня в переменной окружения:
              set OPENSSL_CONF=C:\Program Files (x86)\stunnel\config\openssl.cnf
              


              Я его скопировал как есть
              openssl.cnf
              [req]
              distinguished_name = req_distinguished_name
              req_extensions = v3_req
              
              [req_distinguished_name]
              countryName = Country Name (2 letter code)
              countryName_default = US
              stateOrProvinceName = State or Province Name (full name)
              stateOrProvinceName_default = MN
              localityName = Locality Name (eg, city)
              localityName_default = Minneapolis
              organizationalUnitName  = Organizational Unit Name (eg, section)
              organizationalUnitName_default  = Domain Control Validated
              commonName = Internet Widgits Ltd
              commonName_max  = 64
              
              [ v3_req ]
              # Extensions to add to a certificate request
              basicConstraints = CA:FALSE
              keyUsage = nonRepudiation, digitalSignature, keyEncipherment
              subjectAltName = @alt_names
              
              [alt_names]
              DNS.1 = *.ev.dev
              DNS.2 = *.*.ev.dev
              DNS.3 = *.*.*.ev.dev
              DNS.4 = system.dev
              IP.1 = 192.168.1.200
              



              В запрове SAN есть
              openssl.exe req -text -noout -in server101.mycloud.csr
              Certificate Request:
                  Data:
                      Version: 0 (0x0)
                      Subject: C=US, ST=MN, L=Minneapolis, OU=Domain Control Validated, CN=test.test
                      Subject Public Key Info:
                          Public Key Algorithm: rsaEncryption
                              Public-Key: (2048 bit)
                              Modulus:
                                  00:ba:ae:bd:fc:14:66:ab:66:34:00:6c:ed:98:e6:
              ...
                                  0a:79
                              Exponent: 65537 (0x10001)
                      Attributes:
                      Requested Extensions:
                          X509v3 Basic Constraints:
                              CA:FALSE
                          X509v3 Key Usage:
                              Digital Signature, Non Repudiation, Key Encipherment
                          X509v3 Subject Alternative Name:
                              DNS:*.ev.dev, DNS:*.*.ev.dev, DNS:*.*.*.ev.dev, DNS:system.dev, IP Address
              :192.168.1.200
                  Signature Algorithm: sha256WithRSAEncryption
                       32:e9:16:13:1d:76:f6:6e:17:28:9a:dd:64:b7:cd:7d:d7:df:
              ...
                       f2:11:0c:3b
              



              А в сертификате уже нет
              openssl.exe x509 -text -noout -in server101.mycloud.crt
              Certificate:
                  Data:
                      Version: 1 (0x0)
                      Serial Number:
                          c5:49:2a:14:58:48:e5:8a
                  Signature Algorithm: sha256WithRSAEncryption
                      Issuer: C=US, ST=MN, L=Minneapolis, OU=Domain Control Validated, CN=test.test
                      Validity
                          Not Before: Jun 18 13:20:39 2017 GMT
                          Not After : Feb 25 13:20:39 2031 GMT
                      Subject: C=US, ST=MN, L=Minneapolis, OU=Domain Control Validated, CN=test.test
                      Subject Public Key Info:
                          Public Key Algorithm: rsaEncryption
                              Public-Key: (2048 bit)
                              Modulus:
                                  00:ba:ae:bd:fc:14:66:ab:66:34:00:6c:ed:98:e6:
              ...
                                  0a:79
                              Exponent: 65537 (0x10001)
                  Signature Algorithm: sha256WithRSAEncryption
                       5b:c2:19:da:4d:55:15:e0:b4:d1:aa:08:3e:a8:59:92:4b:7c:
              ...
                       9a:a5:f9:d4
              

                0

                Добавьте что-то вроде :


                server_cert
                [ 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
                subjectAltName = @alt_names

                и вызывайте с ключиком -extensions server_cert

                  0
                  Я нашёл проблему (subjectAltName removed from CSR when signing):
                  openssl x509 -req 
                  

                  не читает кофиг.

                  Говорят надо использовать:
                  openssl ca
                  


                  И добавить в конфиг:
                  [ ca ]
                  default_ca      = CA_default
                  
                  [ CA_default ]
                  copy_extensions = copy
                  


                  Потом требует ещё кучу параметров задать.

          +1
          вот еще в тему — JavaScript Certification Authority
            +2
            Спасибо.
            Может будет полезно — давно пользуюсь бесплатной GUI-утилитой TInyCA — вся база по сертификатам хранится упорядоченно, создание нового CA — в два клика мыши.
              0
              Пользуюсь GUI-утилитой — XCA. Так же очень удобна.
              –5
              Зачем писать статьи на тему того, что уже много раз было описано?
                –6
                Топик = openssl /? или openssl -help
                  0
                  Мой Web-сервер просит ключи в PEM-формате. Как быть?
                    +1
                    Ищи подстроку '.pem' в статье выше.
                      0
                      pem это обычно key+crt в одном файле
                      в моем примере для сервера на питоне есть
                      cat server101.mycloud.key server101.mycloud.crt > server101.mycloud.pem
                      
                        0
                        В общем получается, что PEM, это и есть X.509 (спасибо pyra), и не обязательно содержащий public_key+private_key. Генерируемые ключи по данной статье и есть в PEM-формате.
                        Всё заработало. Спасибо.
                        0
                        Все же FireFox не используют системный репозиторий.

                        Статья полезная, не задумывался, что для себя не обязательно выписывать сертификаты, пусть даже и на startssl.
                          0
                          Всё же на OSX Firefox тоже имеет свой репозиторий.
                          0
                          OpenSSL 0.9.8o 01 Jun 2010 не создаёт CA с длительностью 10000 дней. Вернее создаёт, но даты получаются такие:
                          # openssl req -x509 -new -key rootCA.key -days 10000 -out rootCA.crt
                                  Validity
                                      Not Before: Sep  9 03:39:46 2013 GMT
                                      Not After : Dec 19 21:11:30 1904 GMT
                          

                          Опытным путём было выяснено, что максимальное число, которое openssl воспринимает корректно — 8897 дней:
                          # openssl req -x509 -new -key rootCA.key -days 8897 -out rootCA.crt 
                                  Validity
                                      Not Before: Sep  9 03:40:43 2013 GMT
                                      Not After : Jan 18 03:40:43 2038 GMT
                          

                          Может кто знает почему?
                            0
                            Возможно, используется знаковый 32-битный счётчик секунд, который переполняется как раз в 2038.
                              0
                              Да, именно так и есть. Причём на Wheezy всё нормально, а на Squeeze такой ньюанс… Обе ОС 32-х битные.
                            0
                            Тут следует добавить, что Chrome (по крайней мере под ubuntu) требует PKCS#12 сертификат, в связи с чем следует выполнить еще следующие шаги:

                            openssl pkcs12 -export -out server101.mycloud.p12 -inkey server101.mycloud.key -in server101.mycloud.crt -certfile rootCA.crt
                            

                            Вводим пароль два раза.

                            Теперь импортируем в chrome этот файл, указав пароль, что ввели выше.
                              0
                              Кстати, а можно как-то самому веб-серверу (апачу или никсу) указать, что отвечать можно _ТОЛЬКО_ тем, у кого сертификат подписанный нашим СА, а всех остальных посылать в лес? Или требуются танцы с внешними модулями?
                              0

                              Хорошая статья, как раз то, что надо, чтобы быстро приступить к делу. Но одного я все же не понял: при генерации ключа openssl нам выдает только приватный ключ, а где публичный? Ведь, насколько я помню, получить один ключ из другого должно быть невозможно за разумное время

                                0
                                Ведь, насколько я помню, получить один ключ из другого должно быть невозможно за разумное время


                                Это наоборот, приватный из публичного.

                                Публичный ключ можно получить из приватного:
                                openssl rsa -in server101.mycloud.key -pubout
                                

                                https://openssl.org/docs/manmaster/apps/rsa.html

                                  0
                                  Приватный включает в себя публичный.

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое