Человеческим языком о цифровых сертификатах: ASN.1, X.509, PKI — Expertus metuit

Человеческим языком о цифровых сертификатах: ASN.1, X.509, PKI 
 — Expertus metuit Сертификаты

Ocsp stapling

Для решения этих проблем было предложено расширение протокола TLS, позволяющее прикреплять OCSP-ответы к сертификатам (

) и переносящее ответственность за выполнение OCSP на TLS-сервер.

На рисунке изображена схема использования OCSP stapling:

Схема описывает следующие шаги:

  1. TLS-сервер, выступая в роли OCSP-клиента, собирает информацию о статусе своей цепочки сертификатов, обращаясь к OCSP-серверам соответствующих УЦ;
  2. TLS-сервер кэширует полученные OCSP-ответы;
  3. При установке TLS-соединения сервер передаёт клиенту свою цепочку сертификатов вместе с соответствующими ей OCSP-ответами.

Таким образом:

«Но где же тут защита от атаки повторного воспроизведения?» — спросите вы. Тут её действительно нет, однако рассматриваемое расширение протокола TLS позволяет клиентам пересылать случайный одноразовый код при установке соединения:


Такой вариант использования OCSP stapling не позволяет кэшировать OCSP-ответы на сервере, из-за чего растёт время установки TLS-соединения и увеличивается нагрузка на серверы УЦ.

Следует отметить, что существует две версии OCSP stapling. Первая версия по неясной причине позволяет прикреплять OCSP-ответ только для сертификата самого сервера. При использовании этой версии ответственность за получение информации о статусе остальных сертификатов цепочки лежит на клиенте. Этот недостаток исправлен в новой версии.

Зачем нужны сертификаты¶

Итак, сертификат (certificate) — это файл, содержащий информацию о персоне или организации и подписанный цифровой подписью, нечто вроде паспорта персоны или организации. Главное содержимое сертификата — это публичный ключ шифрования.

У любого сертификата существует его автор-создатель и у этого автора есть секретный ключ шифрования. По сути сертификат — это всего лишь обёртка над публичным ключом, где помимо собственно ключа содержится разнообразная дополнительная информация: имя персоны или организации, страна, телефон, емейл и так далее.

Секретный и публичный ключи используются для асиметричной схемы шифрования: данные шифруются секретным ключом, а расшифровываются публичным. Или наоборот: шифруются публичным, а расшифровываются секретным. За подробностями традиционно отправляю в википедию, в статью Криптосистема с открытым ключом.

И ещё о сертификатах¶

Вот как выглядит попытка распечатать его как текст:

Инфраструктура открытых ключей. цепочка корневых сертификатов x509 v.3

Человеческим языком о цифровых сертификатах: ASN.1, X.509, PKI 
 — Expertus metuitНеумолимо приближается час «Ч»: «использование схемы подписи ГОСТ Р 34.10-2001 для формирования подписи после 31 декабря 2021 года не допускается!».

Однако потом что-то пошло не так, кто-то оказался не готов, и использование ГОСТ Р 34.10-2001 продлили на 2021 год. Но вдруг все бросились переводить УЦ на ГОСТ Р 34.10-2021, а простых граждан переводить на новые сертификаты. У людей на руках стало по нескольку сертификатов. При проверки сертификатов или электронной подписи стали возникать вопросы, а где взять корневые сертификаты, чтобы установить в хранилища доверенных корневых сертификатов.

Это касается как хранилища сертификатов в Windows, так и хранилища сертификатов в браузерах Firefox и Google Chrome, GnuPG, LibreOffice, почтовых клиентах и даже OpenSSL. Конечно, надо было озаботиться этим при получении сертификата в УЦ и записать цепочку сертификатов на флешку. А с другой стороны у нас же цифровое общество и в любой момент должна быть возможность получить эту цепочку из сети. Как это сделать на страницах Хабра показалsimpleadmin. Однако для рядового гражданина это все же сложновато (особенно, если иметь ввиду, что абсолютное их большинство сидит на Windows): нужно иметь «какой-то» openssl, утилиту fetch, которой и у меня не оказалось на компьютере, и далеко не каждый знает, что вместо нее можно использовать wget. А сколько действий нужно выполнить. Выход конечно есть, написать скрипт, но не просто скрипт поверх openssl и иже с ним, а упакованный в самодостаточный выполняемый модуль для различных платформ.

На чем писать никаких сомнений не было – на Tcl и Python. И начинаем с Tcl и вот почему:

* охренительной вики, где есть даже игрушки (там можно подсмотреть интересное 🙂
* шпаргалки
* нормальные сборки tclkit (1.5 — 2 Мб как плата за реальную кросс-платформенность)
* и моя любимая сборка eTcl от Evolane (бережно сохранённая с умершего сайта 🙁
сохраняют высокий рейтинг Tcl/Tk в моём личном списке инструментария
и, да, wiki.tcl.tk/16867 (мелкий web-сервер с cgi на Tcl, периодически используется с завидным постоянством под tclkit)
а ещё — это просто красиво и красиво 🙂

К этому бы я добавил наличии утилиты

freewrap

, которая нам и поможет собрать автономные (standalone) утилиты для Linux и MS Windows. В результате мы будем иметь утилиту chainfromcert:

bash-4.3$ ./chainfromcert_linux64  
Copyright(C)2021 
Usage: 
chainfromcert <file with certificate> <directory for chain certificate> 
Bad usage! 
bash-4.3$

В качестве параметров утилите задаются файл с пользовательским сертификатом (как в формате PEM, так и формате DER) и каталог, в котором будут сохранены сертификаты УЦ, входящие в цепочку:

bash-4.3$ ./chainfromcert_linux64  ./cert_test.der /tmp
Loading file: cert_test.der 
Directory for chain: . 
cert 1 from http://ca.ekey.ru/cdp/ekeyUC2021.cer 
cert 2 from http://reestr-pki.ru/cdp/guc_gost12.crt 
Goodby! 
Length chain=2 
Copyright(C) 2021
bash-4.3$

А теперь рассмотрим как работает утилита.

Информация о центре сертификации, выдавшем сертификат пользователю, хранится в расширении с oid-ом 1.3.6.1.5.5.7.1.1. В этом расширении может хранится как о местонахождении сертификата УЦ (oid 1.3.6.1.5.5.7.48.2), так и информация о службе OCSP УЦ (oid 1.3.6.1.5.5.7.48.1):

Человеческим языком о цифровых сертификатах: ASN.1, X.509, PKI 
 — Expertus metuit

А информация, например, о периоде использования ключа электронной подписи хранится в расширении с oid-ом 2.5.29.16.

Про сертификаты:  Подарочный сертификат дочки сыночки как проверить номенал?

Для разбора сертификата и доступа к расширениям сертификата воспользуемся пакетом pki:

#!/usr/bin/tclsh -f
package require pki

Нам также потребуются пакет base64:

package require base64

Пакет pki, а также подгружаемые им пакет asn, и пакет base64 и помогут нам преобразовывать сертификаты из PEM-кодировки в DER-кодировку, разобрать ASN-структуры и собственно получить доступ к информации о местонахождении сертификатов УЦ.

Работа утилиты начинается с проверки параметров и загрузки файла с сертификатом:

proc usage {use } {
    puts "Copyright(C)  2021-2021"
    if {$use == 1} {
	puts "Usage:nchainfromcert <file with certificate> <directory for chain certificate>n"
    }
}
if {[llength $argv] != 2 } {
    usage 1
    puts "Bad usage!"
    exit
}
set file [lindex $argv 0]
if {![file exists $file]} {
    puts "File $file not exist"
    usage 1
    exit
}
puts "Loading file: $file"
set dir [lindex $argv 1]
if {![file exists $dir]} {
    puts "Dir $dir not exist"
    usage 1
    exit
}
puts "Directory for chain: $dir"
set fd [open $file]
chan configure $fd -translation binary
set data [read $fd]
close $fd
if {$data == "" } {
    puts "Bad file with certificate=$file"
    usage 1
    exit
}

Здесь все понятно и отметим только одно – файл с сертификатом рассматривается как бинарный файл:

chan configure $fd -translation binary

Это связано с тем, что сертификат может хранится как в формате DER (двоичный код), так и в формате PEM (base64 — кодировка).

После того как файл загружен вызывается процедура chainfromcert:

set depth [chainfromcert $data $dir]

которая собственно и загружает корневые сертификаты:

proc chainfromcert {cert dir} {
    if {$cert == "" } {
	exit
    }
    set asndata [cert_to_der $cert]
    if {$asndata == "" } {
#Файл содержит все что угодно, только не сертификат
	return -1
    }
    array set cert_parse [::pki::x509::parse_cert $asndata]
    array set extcert $cert_parse(extensions)
    if {![info exists extcert(1.3.6.1.5.5.7.1.1)]} {
#В сертификате нет расширений
	return 0
    }
    set a [lindex $extcert(1.3.6.1.5.5.7.1.1) 0]
#    if {$a == "false"} {
#	puts $a
#    }
#Читаем ASN1-последовательность расширения в Hex-кодировке
    set b [lindex $extcert(1.3.6.1.5.5.7.1.1) 1]
#Переводим в двоичную кодировку
    set c [binary format H* $b]
#Sequence 1.3.6.1.5.5.7.1.1
    ::asn::asnGetSequence c c_par_first
#Цикл перебора значений в засширении 1.3.6.1.5.5.7.1.1
    while {[string length $c_par_first] > 0 } {
#Выбираем очередную последовательность (sequence)
	::asn::asnGetSequence c_par_first c_par
#Выбираем oid из последовательности
	::asn::asnGetObjectIdentifier c_par c_type
	set tas1 [::pki::_oid_number_to_name $c_type]
#Выбираем установленное значение
	::asn::asnGetContext c_par c_par_two
#Ищем oid с адресом корневого сертификата
	if {$tas1 == "1.3.6.1.5.5.7.48.2" } {
#Читаем очередной корневой сертификат
	    set certca [readca $c_par $dir]
	    if {$certca == ""} {
#Прочитать сертификат не удалось. Ищем следующую точку с сертификатом
		continue
	    } else {
		global count
#Сохраняем корневой сертификат в указанном каталоге
		set f [file join $dir [file tail $c_par]]
		set fd [open $f w]
		chan configure $fd -translation binary
		puts -nonewline $fd $certca 
		close $fd
		incr count
		puts "cert $count from $c_par"
#ПОДЫМАЕМСЯ по ЦЕПОЧКЕ СЕРТИФИКАТОВ ВВЕРХ
		chainfromcert $certca $dir
		continue
	    }
	} elseif {$tas1 == "1.3.6.1.5.5.7.48.1" } {
#	    puts "OCSP server (oid=$tas1)=$c_par"
	}
    }
# Цепочка закончилась
    return $count
}

К комментариям добавить нечего, но у нас осталась не рассмотренной процедура readca:

proc readca {url dir} {
    set cer ""
#Читаем сертификат в бинарном виде
    if {[catch {set token [http::geturl $url -binary 1]
#получаем статус выполнения функции
	set ere [http::status $token]
	if {$ere == "ok"} {
#Получаем код возврата с которым был прочитан сертификат
	    set code [http::ncode $token]
	    if {$code == 200} {
#Сертификат успешно прочитан и будет созвращен
                set cer [http::data $token]
    	    } elseif {$code == 301 || $code == 302} {
#Сертификат перемещен в другое место, получаем его 
         		set newURL [dict get [http::meta $token] Location]
#Читаем сертификат с другого сервера
          		set cer [readca $newURL $dir]
    	    } else {
#Сертификат не удалось прочитать
        	set cer ""
    	    }
        } 
    } error]} {
#Сертификат не удалось прочитать, нет узла в сети
	set cer ""
    }
    return $cer
}

Это процедура построена на использовании пакета http:

package require http

Для чтения сертификата мы используем следующую функцию:

set token [http::geturl $url -binary 1]

Назначение остальных используемых функции понятно из комментариев. Дадим только расшифровку кодов возврата для функции http::ncodel:

200 Запрос успешно выполнен
206 Запрос успешно выполнен, но удалось скачать только часть файла
301 Файл перемещен в другое место
302 Файл временно перемещен в другое место
401 Требуется аутентификация на сервере
403 Доступ к этому ресурсу запрещен
404 Указанный ресурс не может быть найден
500 Внутренняя ошибка

Осталось не рассмотренной одна процедура, а именно cert_to_der:

proc cert_to_der {data} {
    set lines [split $data n]
    set hlines 0
    set total 0
    set first 0
#Ищем PEM-сертификат в файле
    foreach line $lines {
        incr total
        if {[regexp {^-----BEGIN CERTIFICATE-----$} $line]} {
            if {$first} {
                incr total -1
                break
            } else {
                set first 1
                incr hlines
            }
        }        
        if {[regexp {^(.*):(.*)$} $line ]} {
            incr hlines
        } 
    }
    if { $first == 0 && [string range $data 0 0 ] == "0" } {
#Очень похоже на DER-кодировку "0" == 0x30 
	return $data
    }
    if {$first == 0} {return ""}
    set block [join [lrange $lines $hlines [expr {$total-1}]]]
#from PEM to DER
    set asnblock [base64::decode $block]
    return $asnblock
}

Схема процедуры очень простая. Если это PEM-файл с сертификатом («—–BEGIN CERTIFICATE—– »), то выбирается тело этого файла и преобразуется в бинаоный код:

set asnblock [base64::decode $block]

Если это не PEM-файл, то проверяется это «похожесть» на asn-кодировку (нулевой бит должен быть равен 0x30).

Вот собственно и все, осталось добавить завершающие строки:

if {$depth == -1} {
    puts "Bad file with certificate=$file"
    usage 1
    exit
}
puts "Goodby!nLength chain=$depth"
usage 0
exit

Теперь все собираем в один файл с именем

Проверить работу этого файла можно с помощью интерпретарора tclsh:

$ tclsh ./chainfromcert.tcl cert_orlov.der /tmp 
Loading file: cert_test.der 
Directory for chain: /tmp 
cert 1 from http://ca.ekey.ru/cdp/ekeyUC2021.cer 
cert 2 from http://reestr-pki.ru/cdp/guc_gost12.crt 
Goodby! 
Length chain=2 
Copyright(C) 2021 
$

В результате работы мы получили цепочку из двух сертификатов в каталоге /tmp.

Про сертификаты:  Сертификат на лекарство: какие еще потребуются документы? Как грамотно провести сертификацию лекарств? | ИнфоГОСТ

Но мы хотели получить выполняемые модули для платформ Linux и Windowsи и чтобы пользователи не задумывались о каких-то интерпретаторах.

Для этой цели мы воспользуемся утилитой freewrapTCLSH. С помощью этой утилиты мы сделаем выполняемые модули нашей утилиты для платформ Linux и Windows как 32-х разрядных так и 64-х. Сборку утилит можно проводить для всех платформ на любой из платформ. Извините за тавтологию. Я буду собирать на linux_x86_64 (Mageia).

Для сборки потребуется:

1. Утилита freewrapTCLSH для платформы linux_x86_64;
2. Файл freewrapTCLSH с этой утилитой для каждой платформы:
— freewrapTCLSH_linux32
— freewrapTCLSH_linux64
— freewrapTCLSH_win32
— freewrapTCLSH_win64
3. Исходный файл нашей утилиты: chainfromcert.tcl

Итак, собираемый выполняемый файл chainfromcerty_linuxx86 для платформы Linux x86:

$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_linux32 –o chainfromcerty_linuxx86
$

Сборка утилиты для платформы Windows 64-х битного выглядит так:

$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_win64 –o chainfromcerty_win64.exe
$

И т.д. Утилиты готовы к использованию. Все необходимое для их работы они вобрали в себя.

Аналогичным образом пишется код и на Python-е.

В ближайшие дни я думаю дополнить пакет fsb795 (а он написан на Python-е) функцией получения цепочки корневых сертификатов.

Как вручную проверить цепочку сертификатов¶

Иногда возникает необходимость проверить, подписан ли один сертификат другим. Это можно сделать через openssl.

Возьмём три сертификата из Go Daddy, вот они: GD_root.pem (это корневой сертификат Go Daddy), GD_good.pem, GD_bad.pem. Нам нужно проверить, подписаны ли сертификаты GD_good.pem и GD_bad.pem корневым сертификатом Go Daddy.

Для верификации используем команду openssl verify, в ней мы должны указать файл с «доверенными» сертификатами, в нашем случае это один сертификат GD_root.pem, указывается через аргумент -CAfile GD_root.pem. Также мы указываем заведомо несуществующий путь к главному хранилищу сертификатов удостоверяющих центров, чтобы в процессе верификации участвовал только один сертификат, это делается через аргумент -CApath /nope. В новых версиях openssl (начиная с 1.1.0) введён специальный новый параметр -no-CApath.

И альтернативный вариант (предыдущий в новых версиях не работает):

Кратко о протоколе tls и инфраструктуре открытых ключей x.509

Современный защищённый Веб стоит на двух китах:

. Для установки защищённого TLS-соединения сервер должен передать клиенту свой открытый ключ. Аутентичность ключа сервера, пересылаемого через незащищённые публичные сети, обеспечивается цепочкой сертификатов открытых ключей инфраструктуры

Удостоверяющий центр (УЦ, certification authority, CA) может отозвать подписанный (изданный) им ранее сертификат, например, в случае компрометации закрытого ключа, соответствующего этому сертификату. Поэтому, чтобы исключить возможность подключения к «человеку посередине», при установке TLS-соединения клиент должен не только проверять корректность подписей всей предоставленной сервером цепочки сертификатов, но и проверять статус предоставленных ему сертификатов (сертификат действителен или отозван).

Механизм crl

УЦ публикуют CRL, в которые вносятся серийные номера отозванных сертификатов, в точках распространения CRL (CRL distribution point, CDP). Адреса (URL) точек распространения CRL, к которым следует обращаться для получения информации о статусе проверяемого сертификата, как правило, указываются в самом сертификате.

Механизм ocsp

OCSP, как следует из его названия, предназначен для получения наиболее актуальной информации о статусе сертификата в режиме онлайн и не обладает приведёнными выше недостатками CRL.

Механизмы проверки статуса сертификатов


Применяющиеся на практике механизмы проверки статуса сертификатов основаны на

(certificate revocation list, CRL) и

(online certificate status protocol, OCSP).

Продолжение следует… а пока — отличная новость!

В этой статье мы рассмотрели три основных механизма проверки статуса сертификатов. Проверки, осуществляемые на практике, являются надстройками над этими механизмами или их комбинацией. Тема дальнейшего обсуждения и следующей статьи — каким образом проверки статуса сертификатов реализованы в Веб-браузерах?

Про проблему отозванных сертификатов и «Кошмар скомпрометированных ключей» мы говорили на «очной ставке» NeoQUEST-2021. У нас даже есть отличное видео, в котором автор статьи рассказывает про отозванные сертификаты:

Пока у нас активно ведется подготовка к очередной «очной ставке» NeoQUEST-2021, которая пройдет в Питере 29 июня (и на которую мы ждём ВСЕХ желающих!), рассказываем интересную новость:

Мы объявляем конкурс докладов на «очную ставку» NeoQUEST-2021!

Просмотр содержимого ключей и сертификатов

Мы можем подробно изучить содержимое всех созданных в OpenSSL файлов, а также при необходимости конвертировать их в другие форматы.


В следующих командах используются тестовые файлы со следующими именами:

Обратите внимание на расширения файлов — они могут отличаться от тех, которые используются в других инструкциях. Например, вместо .key и .crt может использоваться расширение .pem. Расширение файла не имеет особого значения кроме как служить подсказкой пользователю, что именно находится в этом файле. Это же самое касается и имён файлов — вы можете выбирать любые имена.

Все эти файлы являются текстовыми:

cat rootCA.key


Там мы увидим примерно следующее:

-----BEGIN PRIVATE KEY-----
MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDJBKkr6XzzAcXD
eyDQdvB0SWE2Fl3nqlX/c2RgqMgScXtgidEzOu9ms3Krju5UKLokkQJrZFPMtiIL
MuPJFdYjVyfkfnqlZiouBVgJ60s8NQBBI8KnyyAoJCIFdASoW4Kv5C5LT8pX9eRa
/huJaRJL5XsFUGnTOLvW2ZLN52iAux9CoZlmH6ZF4nuQpblwN0MHULAhze52VNFT
…………………………………………………..
…………………………………………………..
…………………………………………………..
…………………………………………………..
…………………………………………………..
…………………………………………………..
-----END PRIVATE KEY-----

Секретный ключ¶

Главный компонент всего — криптография, точнее криптографические алгоритмы.

Всё начинается с генерации пары ключей: секретного и публичного. Сначала при помощи математической и компьютерной магии генерируется секретный ключ, а затем из него вычисляется публичный. Чаще всего для сертификатов используется алгоритм RSA, вот как это делается в консоли через программу openssl:

В этом примере мы сгенерили секретный ключ для алгоритма RSA. Собственно сам ключ находится между специальными маркерами —–BEGIN RSA PRIVATE KEY—– и —–END RSA PRIVATE KEY—–. Чтобы при генерации ключа сохранить его сразу в файл, добавьте аргумент -out private.pem:

С подобным форматом представления криптографических данных вам предстоит сталкиваться постоянно, называется он PEM, что расшифровывается как Privacy-enhanced Electronic Mail. Его предназначение — кодировать бинарные криптографические данные в виде ASCII-текста.

Про сертификаты:  Экзамены GMAT/GRE |  | BritBridge

Мы можем при помощи openssl сконвертировать секретный ключ из текстового формата PEM в изначальный бинарный, сохраним его в файл private.der:

Имя формата данных DER расшифровывается как Distinguished Encoding Rules и является частью стандарта ASN.1. В стандарте ASN.1 описываются правила и структуры для кодирования, раскодирования и передачи данных по телекоммуникационным и компьютерным сетям.

Также существует консольная программа dumpasn1 для просмотра бинарных данных, в дебиане/убунте она ставится через sudo apt-get install dumpasn1, для макоси можно поставить через macports, через homebrew (инструкция), либо скомпилировать самостоятельно из исходников.

Вот так, например, выглядит дамп нашего созданного секретного ключа:

В нашем файле секретного ключа «упакованы» девять целых чисел, структура ключа для алгоритма RSA весьма простая и описана, например, в RFC 3447, в секции A.1.2 RSA private key syntax.

Ну и чтобы два раза не вставать, вытащим из секретного ключа публичный ключ в файл public.der, закодированный в бинарный формат DER, openssl умеет делать и это тоже:

И сразу же посмотрим на его структуру через dumpasn1:

Структура публичного ключа также описана в RFC3447, в секции A.1.1, если вы туда посмотрите, то увидите, что дамп файла public.der не соответствует этому описанию. Поздравляю с первыми граблями в openssl, с ними вам тоже придётся сталкиваться постоянно.

Для манипуляциями публичными и секретными ключами можно также использовать команду pkey, она позволяет работать с произвольными поддерживаемыми типами ключей. Вот как выглядит её вызов для вытаскивания публичного ключа из приватного:

Создание

В статье я буду рассматривать простую схему взаимодействия клиент-сервер, без удостоверяющего центра. Для взаимной проверки подлинности нам понадобится корневой самоподписанный сертификат caCert и два конечных clientCert и serverCert.

Технические подробности¶

А теперь самое главное, ради чего эта статья и писалась — технические подробности, стандарты и особенности реализации системы сертификатов.

В основе всего лежит множество индустриальных стандартов, созданных очень давно — в конце восьмидесятых и начале девяностых годов XX века. Благодаря этим стандартам, мы сейчас имеет вполне успешно функционирующую систему, несмотря на то, что отдельные её компоненты созданы совершенно разными компаниями.

Хранение

Теперь, когда у нас есть все сертификаты, необходимо их сохранить. В руководстве openssl обычно предлагается хранить сертификаты в формате pem, разбив их на открытую часть public.crt и private.key. Полученные на предыдущих этапах сертификаты являются экземплярами класса X509Certificate2, который имеет все необходимые для нас свойства и методы.

StringBuilder builder = new StringBuilder();
builder.AppendLine("-----BEGIN CERTIFICATE-----");
builder.AppendLine(Convert.ToBase64String(clientCert.RawData, Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine("-----END CERTIFICATE-----");
File.WriteAllText("public.crt", builder.ToString());

Также сохраним закрытый ключ:

string name = clientKey.SignatureAlgorithm.ToUpper();
StringBuilder builder = new StringBuilder();
builder.AppendLine($"-----BEGIN {name} PRIVATE KEY-----");
builder.AppendLine(Convert.ToBase64String(clientKey.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine($"-----END {name} PRIVATE KEY-----");
File.WriteAllText("private.key", builder.ToString());

Отмечу, что метод ExportRSAPrivateKey появился только в netcore 3.0, поэтому в предыдущих версиях могут возникнуть проблемы.

Еще один способ хранения сертификатов — формат pkcs12 или pfx, позволяющий уместить в одном файле открытую и закрытую часть. Класс X509Certificate2 содержит метод Export, принимающий в качестве аргумента необходимый нам ключ X509ContentType.

Pkcs12 или X509ContentType.Pfx. Однако на этом этапе возникают трудности, поскольку загруженный обратно из файла сертификат неожиданно не содержит приватного ключа, о чем свидетельствует флаг cert.HasPrivateKey == false.

Дело в том, что класс X509Certificate2 содержит внутреннюю метку, описывающую, куда и как экспортируется закрытый ключ. Эта метка инициализируется только с помощью конструктора и не может быть изменена после. Поэтому для создания файла p12 или pfx, необходимы дополнительные усилия:

var exportCert = new X509Certificate2(clientCert.Export(X509ContentType.Cert), (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet).CopyWithPrivateKey(clientKey);
File.WriteAllBytes("client.pfx", exportCert.Export(X509ContentType.Pfx));
File.WriteAllBytes("client.p12", exportCert.Export(X509ContentType.Pkcs12));

При желании можно использовать перегрузки метода Export с паролем.

Для работы с коллекциями сертификатов в .net core удобно использовать специализированное хранилище, реализуемого классом X509Store. Хранилище в простом случае инициализируется строковым именем:

X509Store store = new X509Store("test");
store.Open(OpenFlags.ReadWrite);
store.Add(cert);

В результате для OC Linux будет создана папка “~/.dotnet/corefx/cryptography/x509stores/ test/” и файл “827…E3E.pfx”, соответсвующий цифровому отпечатку (поле Thumbprint). Попытка что-то сделать с этим сертификатом с помощью openssl не увенчается успехом, так как он ожидаемо запаролен.

Цепочки удостоверяющих сертификатов¶

Удостоверяющий центр может выдать сертификат, в котором в поле X509v3 Basic Constraints разрешается его использование как удостоверяющего. Этим сертификатом тоже можно подписывать «доменные» сертификаты. Таким образом, все CA-сертификаты образуют цепочку, а в целом — дерево.

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

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

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

Корневой сертификат является самоподписанным, то есть он подписан тем же самым ключом, публичный ключ для которого находится в теле сертификата. У корневого сертификата совпадают поля issuer и subject.

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

Оцените статью
Мой сертификат
Добавить комментарий