модуль OpenSSL::OCSP
OpenSSL::OCSP реализует запросы и ответы протокола Online Certificate Status Protocol.
Для создания и отправки запроса OCSP необходим сертификат субъекта, содержащий URL OCSP в расширении authorityInfoAccess, и сертификат издателя для сертификата субъекта. Сначала загрузите сертификаты издателя и субъекта:
subject = OpenSSL::X509::Certificate.new subject_pem issuer = OpenSSL::X509::Certificate.new issuer_pem
Для создания запроса нам нужно создать идентификатор сертификата субъекта, чтобы ЦС знал, о каком сертификате мы спрашиваем:
digest = OpenSSL::Digest::SHA1.new certificate_id = OpenSSL::OCSP::CertificateId.new subject, issuer, digest
Затем создайте запрос и добавьте в него идентификатор сертификата:
request = OpenSSL::OCSP::Request.new request.add_certid certificate_id
Добавление nonce в запрос защищает от атак повторного воспроизведения, но не все ЦС обрабатывают nonce.
request.add_nonce
Для отправки запроса ЦС на проверку нам нужно извлечь OCSP URI из сертификата субъекта:
authority_info_access = subject.extensions.find do |extension| extension.oid == 'authorityInfoAccess' end descriptions = authority_info_access.value.split "\n" ocsp = descriptions.find do |description| description.start_with? 'OCSP' end require 'uri' ocsp_uri = URI ocsp[/URI:(.*)/, 1]
Для отправки запроса мы отправим POST-запрос по OCSP URI (согласно RFC 2560). Обратите внимание, что в этом примере мы обрабатываем только HTTP-запросы и не обрабатываем переадресации, поэтому это недостаточно для серьезного использования.
require 'net/http'
http_response =
Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http|
http.post ocsp_uri.path, request.to_der,
'content-type' => 'application/ocsp-request'
end
response = OpenSSL::OCSP::Response.new http_response.body
response_basic = response.basic
Сначала проверим, имеет ли ответ действительную подпись. Без действительной подписи мы не можем ему доверять. Если здесь произошла ошибка, возможно, отсутствует хранилище системных сертификатов или промежуточные сертификаты.
store = OpenSSL::X509::Store.new store.set_default_paths unless response.verify [], store then raise 'response is not signed by a trusted certificate' end
Ответ содержит информацию о статусе (успех/неудача). Мы можем отобразить статус как строку:
puts response.status_string #=> successful
Далее нам нужна информация о деталях ответа, чтобы определить, соответствует ли ответ нашему запросу. Сначала проверим nonce. Опять же, не все ЦС поддерживают nonce. См. OpenSSL::OCSP::Request#check_nonce для значений возвращаемых значений.
p request.check_nonce basic_response #=> value from -1 to 3
Затем извлеките информацию о статусе из основного ответа. (В запросе можно проверить несколько сертификатов, но в этом примере был представлен только один.)
response_certificate_id, status, reason, revocation_time, this_update, next_update, extensions = basic_response.status
Затем проверим различные поля.
unless response_certificate_id == certificate_id then raise 'certificate id mismatch' end now = Time.now if this_update > now then raise 'update date is in the future' end if now > next_update then raise 'next update time has passed' end
Константы
- NOCASIGN
-
(Этот флаг не используется в OpenSSL 1.0.1g)
- NOCERTS
-
Не включать сертификаты в ответ
- NOCHAIN
-
Не проверять цепочку сертификатов в ответе
- NOCHECKS
-
Не выполнять дополнительные проверки сертификатов подписи
- NODELEGATED
-
(Этот флаг не используется в OpenSSL 1.0.1g)
- NOEXPLICIT
-
Не проверять доверие
- NOINTERN
-
Не искать сертификаты, содержащиеся в ответе, для подписывающего
- NOSIGS
-
Не проверять подпись в ответе
- NOTIME
-
Не включать время producedAt в ответ
- NOVERIFY
-
Не проверять ответ вообще
- RESPID_KEY
-
Идентифицировать ответ по подписи идентификатора ключа сертификата
- RESPONSE_STATUS_INTERNALERROR
-
Внутренняя ошибка у издателя
- RESPONSE_STATUS_MALFORMEDREQUEST
-
Незаконный запрос подтверждения
- RESPONSE_STATUS_SIGREQUIRED
-
Необходимо подписать запрос и переотправить его
- RESPONSE_STATUS_SUCCESSFUL
-
Ответ Response содержит действительные подтверждения
- RESPONSE_STATUS_TRYLATER
-
Повторите попытку позже
- RESPONSE_STATUS_UNAUTHORIZED
-
Ваш запрос не авторизован.
- REVOKED_STATUS_AFFILIATIONCHANGED
-
Изменилось имя субъекта сертификата или другая информация
- REVOKED_STATUS_CACOMPROMISE
-
Сертификат этого ЦС был отозван из-за компрометации ключа
- REVOKED_STATUS_CERTIFICATEHOLD
-
Сертификат приостановлен
- REVOKED_STATUS_CESSATIONOFOPERATION
-
Сертификат больше не нужен
- REVOKED_STATUS_KEYCOMPROMISE
-
Сертификат был отозван из-за компрометации ключа
- REVOKED_STATUS_NOSTATUS
-
Сертификат был отозван по неизвестной причине
- REVOKED_STATUS_REMOVEFROMCRL
-
Сертификат ранее был приостановлен и теперь должен быть удален из CRL
- REVOKED_STATUS_SUPERSEDED
-
Сертификат был заменен новым сертификатом
- REVOKED_STATUS_UNSPECIFIED
-
Сертификат был отозван по неопределенной причине
- TRUSTOTHER
-
Не проверять дополнительные сертификаты
- V_CERTSTATUS_GOOD
-
Указывает, что сертификат не отозван, но это не обязательно означает, что сертификат был выдан или что этот ответ находится в пределах срока действия сертификата
- V_CERTSTATUS_REVOKED
-
Указывает, что сертификат был отозван постоянно или временно (приостановлен).
- V_CERTSTATUS_UNKNOWN
-
Указывает, что отвечающий не знает о запрашиваемом сертификате.
- V_RESPID_KEY
-
Идентификатор отвечающего основан на открытом ключе.
- V_RESPID_NAME
-
Идентификатор отвечающего основан на имени ключа.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.