Назад | Перейти на главную страницу

Истечение срока действия и продление корневого сертификата центра сертификации

В 2004 году я создал небольшой центр сертификации, использующий OpenSSL в Linux и простые сценарии управления, поставляемые с OpenVPN. В соответствии с руководствами, которые я нашел в то время, я установил срок действия сертификата корневого ЦС на 10 лет. С тех пор я подписал много сертификатов для туннелей OpenVPN, веб-сайтов и серверов электронной почты, все из которых также имеют срок действия 10 лет (возможно, это было неправильно, но в то время я не знал лучшего).

Я нашел много руководств по настройке ЦС, но очень мало информации об управлении им, и, в частности, о том, что нужно сделать, когда истечет срок действия сертификата корневого ЦС, что произойдет где-то в 2014 году. Итак, у меня есть следующее вопросы:

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

Сохранение того же закрытого ключа в корневом ЦС позволяет всем сертификатам продолжать успешно проверяться в отношении нового корневого центра сертификации; все, что от вас требуется, - это доверять новому корню.

Отношения подписи сертификата основаны на подписи закрытого ключа; сохранение того же закрытого ключа (и, неявно, того же открытого ключа) при создании нового открытого сертификата с новым периодом действия и любыми другими новыми атрибутами, измененными по мере необходимости, сохраняет доверительные отношения на месте. CRL также могут переходить от старого сертификата к новому, как и сертификаты, подписанные закрытым ключом.


Итак, давайте проверим!

Сделайте корневой ЦС:

openssl req -new -x509 -keyout root.key -out origroot.pem -days 3650 -nodes

Сгенерируйте из него дочерний сертификат:

openssl genrsa -out cert.key 1024
openssl req -new -key cert.key -out cert.csr

Подпишите дочерний сертификат:

openssl x509 -req -in cert.csr -CA origroot.pem -CAkey root.key -create_serial -out cert.pem
rm cert.csr

Все там поставлено, нормальные сертификаты отношения. Проверим доверие:

# openssl verify -CAfile origroot.pem -verbose cert.pem
cert.pem: OK

Итак, теперь, допустим, прошло 10 лет. Давайте сгенерируем новый публичный сертификат из того же корневого закрытого ключа.

openssl req -new -key root.key -out newcsr.csr
openssl x509 -req -days 3650 -in newcsr.csr -signkey root.key -out newroot.pem
rm newcsr.csr

И .. это сработало?

# openssl verify -CAfile newroot.pem -verbose cert.pem
cert.pem: OK

Но почему? Это разные файлы, правда?

# sha1sum newroot.pem
62577e00309e5eacf210d0538cd79c3cdc834020  newroot.pem
# sha1sum origroot.pem
c1d65a6cdfa6fc0e0a800be5edd3ab3b603e1899  origroot.pem

Да, но это не означает, что новый открытый ключ криптографически не соответствует подписи на сертификате. Разные серийные номера, одинаковый модуль:

# openssl x509 -noout -text -in origroot.pem
        Serial Number:
            c0:67:16:c0:8a:6b:59:1d
...
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
                    3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
                    8f:7c:92:4d:8f:b3:cc:e9:56:8d:db:f7:fd:d3:57:
                    1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
                    4b:8d:23:fe:52:98:15:53:3a:91:e1:14:05:a7:7a:
                    9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
                    6b:73:5b:f1:dd:9e:70:2b:f7:6e:bd:dc:d1:39:98:
                    1f:84:2a:ca:6c:ad:99:8a:fa:05:41:68:f8:e4:10:
                    d7:a3:66:0a:45:bd:0e:cd:9d
# openssl x509 -noout -text -in newroot.pem
        Serial Number:
            9a:a4:7b:e9:2b:0e:2c:32
...
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
                    3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
                    8f:7c:92:4d:8f:b3:cc:e9:56:8d:db:f7:fd:d3:57:
                    1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
                    4b:8d:23:fe:52:98:15:53:3a:91:e1:14:05:a7:7a:
                    9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
                    6b:73:5b:f1:dd:9e:70:2b:f7:6e:bd:dc:d1:39:98:
                    1f:84:2a:ca:6c:ad:99:8a:fa:05:41:68:f8:e4:10:
                    d7:a3:66:0a:45:bd:0e:cd:9d

Давайте пойдем немного дальше, чтобы убедиться, что он работает при проверке сертификатов в реальном мире.

Запустите экземпляр Apache, и давайте попробуем (файловая структура debian, при необходимости измените):

# cp cert.pem /etc/ssl/certs/
# cp origroot.pem /etc/ssl/certs/
# cp newroot.pem /etc/ssl/certs/
# cp cert.key /etc/ssl/private/

Мы установим эти директивы на VirtualHost слушая 443 - помните, newroot.pem корневой сертификат даже не существовал, когда cert.pem был создан и подписан.

SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/cert.key
SSLCertificateChainFile /etc/ssl/certs/newroot.pem

Давайте посмотрим, как это видит openssl:

# openssl s_client -showcerts -CAfile newroot.pem -connect localhost:443

Certificate chain
 0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server.lan
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
 1 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
MIICHzCCAYgCCQCapHvpKw4sMjANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJB
...
-----END CERTIFICATE-----
(this should match the actual contents of newroot.pem)
...
Verify return code: 0 (ok)

Хорошо, а как насчет браузера, использующего криптографический API MS? Сначала нужно доверять корню, потом все в порядке, с серийным номером нового корня:

И мы все еще должны работать со старым корнем. Переключите конфигурацию Apache:

SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/cert.key
SSLCertificateChainFile /etc/ssl/certs/origroot.pem

Выполните полный перезапуск Apache, перезагрузка не приведет к правильному переключению сертификатов.

# openssl s_client -showcerts -CAfile origroot.pem -connect localhost:443

Certificate chain
 0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server.lan
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
 1 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
   i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
MIIC3jCCAkegAwIBAgIJAMBnFsCKa1kdMA0GCSqGSIb3DQEBBQUAMFQxCzAJBgNV
...
-----END CERTIFICATE-----
(this should match the actual contents of origroot.pem)
...
Verify return code: 0 (ok)

А с браузером MS Crypto API Apache представляет старый корень, но новый корень все еще находится в надежном корневом хранилище компьютера. Он автоматически найдет его и проверит сертификат относительно доверенного (нового) корня, несмотря на то, что Apache представляет другую цепочку (старый корень). После удаления нового корня из доверенных корней и добавления исходного корневого сертификата все в порядке:


Ну это все! Сохраните тот же закрытый ключ при обновлении, замените новый доверенный корень, и это почти все просто работает. Удачи!

Я заметил, что расширения ЦС могут отсутствовать в обновленном сертификате исходного ключа ЦС. Это сработало для меня более подходящим образом (это создает ./renewedselfsignedca.conf где определены расширения CA v3, и ca.key и ca.crt предполагается, что это исходный ключ CA и сертификат):

openssl x509 -x509toreq -in ca.crt -signkey ca.key -out renewedselfsignedca.csr
echo -e "[ v3_ca ]\nbasicConstraints= CA:TRUE\nsubjectKeyIdentifier= hash\nauthorityKeyIdentifier= keyid:always,issuer:always\n" > renewedselfsignedca.conf
openssl x509 -req -days 1095 -in renewedselfsignedca.csr -signkey ca.key -out renewedselfsignedca.crt -extfile ./renewedselfsignedca.conf -extensions v3_ca

@Bianconiglio plus -set_serial работал у меня. Мой сервер работает только в интрасети, поэтому я не особо беспокоюсь о побочных эффектах, и теперь у меня есть время для работы над «правильным» решением.

Я использовал следующий настраиваемый скрипт. Просто установите переменные CACRT, CAKEY и NEWCA.

# WF 2017-06-30
# https://serverfault.com/a/501513/162693
CACRT=SnakeOilCA.crt
CAKEY=SnakeOilCA.key
NEWCA=SnakeOilCA2017
serial=`openssl x509 -in $CACRT -serial -noout | cut -f2 -d=`
echo $serial
openssl x509 -x509toreq -in $CACRT -signkey $CAKEY -out $NEWCA.csr
echo -e "[ v3_ca ]\nbasicConstraints= CA:TRUE\nsubjectKeyIdentifier= hash\nauthorityKeyIdentifier= keyid:always,issuer:always\n" > $NEWCA.conf
openssl x509 -req -days 3650 -in $NEWCA.csr -set_serial 0x$serial -signkey $CAKEY -out $NEWCA.crt -extfile ./$NEWCA.conf -extensions v3_ca
openssl x509 -in $NEWCA.crt -enddate -serial -noout

Базовый режим для продления срока действия root (вам понадобится открытый X.509 и связанный с ним закрытый ключ):

Создайте CSR из открытого X.509 и закрытого ключа:

openssl x509 -x509toreq -in XXX.crt -signkey XXX.key -out XXX.csr

Повторно подпишите CSR закрытым ключом:

openssl x509 -in XXX.csr -out XXX.crt -signkey XXX.key -req -days 365

Когда срок действия вашего корневого сертификата истечет, истечет срок действия сертификатов, которые вы подписали с ним. Вам нужно будет создать новый корневой сертификат и подписать с ним новые сертификаты. Если вы не хотите повторять этот процесс каждые несколько лет, единственный реальный вариант - продлить действительную дату корневого сертификата примерно на десять или двадцать лет: корень, который я создал для собственного использования, я установил на двадцать лет.

Вы не можете «обновить» корневой сертификат. Все, что вы можете сделать, это создать новый.

Создайте новый корень, по крайней мере, за год или два до истечения срока действия старого, чтобы у вас было время для замены, не упираясь в стену времени, если что-то пойдет не так. Таким образом, вы всегда можете временно переключиться на старые сертификаты, пока не решите свои проблемы с прорезыванием зубов с новым.

Что касается туннелей VPN, я бы установил пару тестовых серверов для экспериментов, чтобы вы точно понимали, что вам нужно делать, прежде чем делать это с клиентской машиной.

У нас была такая же проблема, и это было в нашем случае, потому что сервер Debian был устаревшим, а у openSSL была эта проблема:

https://en.wikipedia.org/wiki/Year_2038_problem

Последняя версия OpenSSL, доступная для Debian 6, вызывает эту проблему. Все сертификаты, созданные после 23.01.2018 г., выдаёт Vality: на 1901 год!

Решение - обновить OpenSSL. Вы можете снова создать файлы конфигурации (с сертификатами) для клиентов.