Skip to content

Commit

Permalink
Check OCSP status (open-eid#709)
Browse files Browse the repository at this point in the history
IB-2668

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma authored Mar 10, 2021
1 parent 7725922 commit 2d41be5
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 24 deletions.
93 changes: 93 additions & 0 deletions client/SslCertificate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "SslCertificate.h"

#include "Common.h"

#include <digidocpp/crypto/X509Cert.h>

#include <QtCore/QDataStream>
Expand All @@ -27,9 +29,12 @@
#include <QtCore/QHash>
#include <QtCore/QMap>
#include <QtCore/QStringList>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QSslKey>

#include <openssl/err.h>
#include <openssl/ocsp.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>

Expand Down Expand Up @@ -95,6 +100,31 @@ QString SslCertificate::subjectInfo( const QByteArray &tag ) const
QString SslCertificate::subjectInfo( QSslCertificate::SubjectInfo subject ) const
{ return QSslCertificate::subjectInfo(subject).join(' '); }

QMultiHash<SslCertificate::AuthorityInfoAccess, QString> SslCertificate::authorityInfoAccess() const
{
QMultiHash<AuthorityInfoAccess, QString> result;
SCOPE(AUTHORITY_INFO_ACCESS, info, extension(NID_info_access));
if(!info)
return result;
for(int i = 0; i < sk_ACCESS_DESCRIPTION_num(info.get()); ++i)
{
ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info.get(), i);
if(ad->location->type != GEN_URI)
continue;
switch(OBJ_obj2nid(ad->method))
{
case NID_ad_OCSP:
result.insertMulti(ad_OCSP, toQByteArray(ad->location->d.uniformResourceIdentifier));
break;
case NID_ad_ca_issuers:
result.insertMulti(ad_CAIssuers, toQByteArray(ad->location->d.uniformResourceIdentifier));
break;
default: break;
}
}
return result;
}

QByteArray SslCertificate::authorityKeyIdentifier() const
{
SCOPE(AUTHORITY_KEYID, id, extension(NID_authority_key_identifier));
Expand Down Expand Up @@ -343,6 +373,69 @@ SslCertificate::CertType SslCertificate::type() const
return UnknownType;
}

bool SslCertificate::validateOnline() const
{
QMultiHash<SslCertificate::AuthorityInfoAccess,QString> urls = authorityInfoAccess();
if(urls.isEmpty())
return false;

QEventLoop e;
QNetworkAccessManager m;
QNetworkAccessManager::connect(&m, &QNetworkAccessManager::finished, &e, &QEventLoop::quit);
QNetworkAccessManager::connect(&m, &QNetworkAccessManager::sslErrors, &m,
[](QNetworkReply *reply, const QList<QSslError> &errors){
reply->ignoreSslErrors(errors);
});

// Get issuer
QNetworkRequest r(urls.values(SslCertificate::ad_CAIssuers).first());
r.setRawHeader("User-Agent", QString("%1/%2 (%3)")
.arg(qApp->applicationName(), qApp->applicationVersion(), Common::applicationOs()).toUtf8());
QNetworkReply *repl = m.get(r);
e.exec();
QSslCertificate issuer(repl->readAll(), QSsl::Der);
repl->deleteLater();

// Build request
SCOPE(OCSP_REQUEST, ocspReq, OCSP_REQUEST_new());
if(!ocspReq)
return false;
OCSP_CERTID *certId = OCSP_cert_to_id(nullptr, (X509*)handle(), (X509*)issuer.handle());
if(!OCSP_request_add0_id(ocspReq.get(), certId))
return false;

// Send request
r.setUrl(urls.values(SslCertificate::ad_OCSP).first());
r.setHeader(QNetworkRequest::ContentTypeHeader, "application/ocsp-request");
repl = m.post(r, i2dDer(i2d_OCSP_REQUEST, ocspReq.get()));
e.exec();

// Parse response
QByteArray respData = repl->readAll();
repl->deleteLater();
const unsigned char *p = (const unsigned char*)respData.constData();
SCOPE(OCSP_RESPONSE, resp, d2i_OCSP_RESPONSE(nullptr, &p, respData.size()));
if(!resp || OCSP_response_status(resp.get()) != OCSP_RESPONSE_STATUS_SUCCESSFUL)
return false;

// Validate response
SCOPE(OCSP_BASICRESP, basic, OCSP_response_get1_basic(resp.get()));
if(!basic)
return false;
//OCSP_TRUSTOTHER - enables OCSP_NOVERIFY
//OCSP_NOSIGS - does not verify ocsp signatures
//OCSP_NOVERIFY - ignores signer(responder) cert verification, requires store otherwise crashes
//OCSP_NOCHECKS - cancel futurer responder issuer checks and trust bits
//OCSP_NOEXPLICIT - returns 0 by mistake
//all checks enabled fails trust bit check, cant use OCSP_NOEXPLICIT instead using OCSP_NOCHECKS
if(OCSP_basic_verify(basic.get(), nullptr, nullptr, OCSP_NOVERIFY) <= 0)
return false;
int status = -1;
if(OCSP_resp_find_status(basic.get(), certId, &status, nullptr, nullptr, nullptr, nullptr) <= 0)
return false;
return status == V_OCSP_CERTSTATUS_GOOD;
}



class PKCS12Certificate::Private: public QSharedData
Expand Down
8 changes: 8 additions & 0 deletions client/SslCertificate.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <QtCore/QCoreApplication>

template<class Key,class T> class QHash;
template<class Key,class T> class QMultiHash;

class SslCertificate: public QSslCertificate
{
Expand Down Expand Up @@ -53,6 +54,11 @@ class SslCertificate: public QSslCertificate
EncipherOnly,
DecipherOnly
};
enum AuthorityInfoAccess
{
ad_OCSP,
ad_CAIssuers,
};
enum CertType
{
UnknownType = 0,
Expand All @@ -75,6 +81,7 @@ class SslCertificate: public QSslCertificate
QString subjectInfo( const QByteArray &tag ) const;
QString subjectInfo( QSslCertificate::SubjectInfo subject ) const;

QMultiHash<AuthorityInfoAccess,QString> authorityInfoAccess() const;
QByteArray authorityKeyIdentifier() const;
QHash<EnhancedKeyUsage,QString> enhancedKeyUsage() const;
QString friendlyName() const;
Expand All @@ -95,6 +102,7 @@ class SslCertificate: public QSslCertificate
static QByteArray toHex( const QByteArray &in, QChar separator = ' ' );
QString toString( const QString &format ) const;
CertType type() const;
bool validateOnline() const;

private:
Qt::HANDLE extension( int nid ) const;
Expand Down
24 changes: 24 additions & 0 deletions client/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3352,6 +3352,30 @@ Additional licenses and components</translation>
<source>Unblock to reuse PIN%1.</source>
<translation>Unblock to reuse PIN%1.</translation>
</message>
<message>
<source>Check certificate status</source>
<translation>Check certificate status</translation>
</message>
<message>
<source>Your ID-card authentication certificate is valid. </source>
<translation>Your ID-card authentication certificate is valid. </translation>
</message>
<message>
<source>Your ID-card signing certificate is valid. </source>
<translation>Your ID-card signing certificate is valid. </translation>
</message>
<message>
<source>Your ID-card signing certificate is not valid. You need valid certificates to use your ID-card electronically. </source>
<translation>Your ID-card signing certificate is not valid. You need valid certificates to use your ID-card electronically. </translation>
</message>
<message>
<source>Your ID-card authentication certificate is not valid. You need valid certificates to use your ID-card electronically. </source>
<translation>Your ID-card authentication certificate is not valid. You need valid certificates to use your ID-card electronically. </translation>
</message>
<message>
<source>Read more &lt;a href=&quot;https://www.id.ee/en/article/validity-of-id-card-certificates/&quot;&gt;here&lt;/a&gt;.</source>
<translation>Read more &lt;a href=&quot;https://www.id.ee/en/article/validity-of-id-card-certificates/&quot;&gt;here&lt;/a&gt;.</translation>
</message>
</context>
<context>
<name>WarningDialog</name>
Expand Down
24 changes: 24 additions & 0 deletions client/translations/et.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3352,6 +3352,30 @@ Täiendavad litsentsid ja komponendid</translation>
<source>Unblock to reuse PIN%1.</source>
<translation>Tühista blokeering, et PIN%1 taas kasutada.</translation>
</message>
<message>
<source>Check certificate status</source>
<translation>Kontrolli sertifikaadi staatust</translation>
</message>
<message>
<source>Your ID-card authentication certificate is valid. </source>
<translation>Sinu ID-kaardi isikutuvastamise sertifikaat kehtib. </translation>
</message>
<message>
<source>Your ID-card signing certificate is valid. </source>
<translation>Sinu ID-kaardi allkirjastamise sertifikaat kehtib. </translation>
</message>
<message>
<source>Your ID-card signing certificate is not valid. You need valid certificates to use your ID-card electronically. </source>
<translation>Sinu ID-kaardi allkirjastamise sertifikaat ei kehti. ID-kaardi elektrooniliseks kasutamiseks on vaja kehtivaid sertifikaate. </translation>
</message>
<message>
<source>Your ID-card authentication certificate is not valid. You need valid certificates to use your ID-card electronically. </source>
<translation>Sinu ID-kaardi isikutuvastamise sertifikaat ei kehti. ID-kaardi elektrooniliseks kasutamiseks on vaja kehtivaid sertifikaate. </translation>
</message>
<message>
<source>Read more &lt;a href=&quot;https://www.id.ee/en/article/validity-of-id-card-certificates/&quot;&gt;here&lt;/a&gt;.</source>
<translation>Loe rohkem &lt;a href=&quot;https://www.id.ee/artikkel/id-kaardi-sertifikaatide-kehtivus/&quot;&gt;siit&lt;/a&gt;.</translation>
</message>
</context>
<context>
<name>WarningDialog</name>
Expand Down
24 changes: 24 additions & 0 deletions client/translations/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3355,6 +3355,30 @@ Additional licenses and components</source>
<source>Unblock to reuse PIN%1.</source>
<translation>Разблокируйте его для повторного использования PIN%1.</translation>
</message>
<message>
<source>Check certificate status</source>
<translation>Проверить статус сертификата</translation>
</message>
<message>
<source>Your ID-card authentication certificate is valid. </source>
<translation>Сертификат идентификации личности в вашей ID-карте действителен. </translation>
</message>
<message>
<source>Your ID-card signing certificate is valid. </source>
<translation>Сертификат подписи в вашей ID-карте действителен. </translation>
</message>
<message>
<source>Your ID-card signing certificate is not valid. You need valid certificates to use your ID-card electronically. </source>
<translation>Сертификат подписи вашей ID-карты недействителен. Для электронного использования ID-карты требуются действующие сертификаты. </translation>
</message>
<message>
<source>Your ID-card authentication certificate is not valid. You need valid certificates to use your ID-card electronically. </source>
<translation>Сертификат идентификации личности в вашей ID-карте недействителен. Для электронного использования ID-карты требуются действующие сертификаты. </translation>
</message>
<message>
<source>Read more &lt;a href=&quot;https://www.id.ee/en/article/validity-of-id-card-certificates/&quot;&gt;here&lt;/a&gt;.</source>
<translation>Подробнее читайте &lt;a href=&quot;https://www.id.ee/ru/artikkel/dejstvitelnost-sertifikatov-id-karty/&quot;&gt;здесь&lt;/a&gt;.</translation>
</message>
</context>
<context>
<name>WarningDialog</name>
Expand Down
20 changes: 19 additions & 1 deletion client/widgets/VerifyCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
#include "ui_VerifyCert.h"
#include "Styles.h"
#include "dialogs/CertificateDetails.h"
#include "dialogs/WarningDialog.h"

#include <common/DateTime.h>

#include <QtCore/QTextStream>
#include <QtNetwork/QSslKey>
#include <QSvgWidget>

VerifyCert::VerifyCert(QWidget *parent)
: StyledWidget(parent)
Expand All @@ -45,6 +45,19 @@ VerifyCert::VerifyCert(QWidget *parent)
CertificateDetails::showCertificate(c, this,
pinType == QSmartCardData::Pin1Type ? QStringLiteral("-auth") : QStringLiteral("-sign"));
});
connect(ui->checkCert, &QPushButton::clicked, this, [=]{
QString msg;
if(c.validateOnline())
msg = c.keyUsage().contains(SslCertificate::NonRepudiation) ?
tr("Your ID-card signing certificate is valid. ") :
tr("Your ID-card authentication certificate is valid. ");
else
msg = c.keyUsage().contains(SslCertificate::NonRepudiation) ?
tr("Your ID-card signing certificate is not valid. You need valid certificates to use your ID-card electronically. ") :
tr("Your ID-card authentication certificate is not valid. You need valid certificates to use your ID-card electronically. ");
msg += tr("Read more <a href=\"https://www.id.ee/en/article/validity-of-id-card-certificates/\">here</a>.");
WarningDialog::warning(this, msg);
});

ui->greenIcon->load(QStringLiteral(":/images/icon_check.svg"));
ui->orangeIcon->load(QStringLiteral(":/images/icon_alert_orange.svg"));
Expand All @@ -64,6 +77,9 @@ VerifyCert::VerifyCert(QWidget *parent)
ui->details->setFont( regular12 );
ui->details->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
ui->widgetLayout->setAlignment(ui->details, Qt::AlignCenter);
ui->checkCert->setFont(regular12);
ui->checkCert->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
ui->widgetLayout->setAlignment(ui->checkCert, Qt::AlignCenter);
ui->changePIN->setFont( Styles::font( Styles::Condensed, 14 ) );
ui->tempelText->setFont( Styles::font( Styles::Regular, 14 ) );
ui->tempelText->hide();
Expand Down Expand Up @@ -156,6 +172,7 @@ void VerifyCert::update()
ui->changePIN->setHidden((isBlockedPin && isBlockedPuk) || isTempelType);
ui->forgotPinLink->setText(tr("Forgot PIN%1?").arg(pinType));
ui->forgotPinLink->setHidden(isBlockedPin || isBlockedPuk || isTempelType);
ui->checkCert->setVisible(isValidCert);
ui->error->setText(
isRevoked ? tr("PIN%1 can not be used because the certificate has revoked. "
"You can find instructions on how to get a new document from <a href=\"https://www.politsei.ee/en/\"><span style=\"color: #006EB5; text-decoration: none;\">here</span></a>.").arg(pinType) :
Expand All @@ -173,6 +190,7 @@ void VerifyCert::update()
ui->changePIN->setHidden(cardData.version() == QSmartCardData::VER_USABLEUPDATER || isBlockedPuk);
ui->forgotPinLink->hide();
ui->details->hide();
ui->checkCert->hide();
ui->error->setText(
isBlockedPin ? tr("PUK code is blocked because the PUK code has been entered 3 times incorrectly. "
"You can not unblock the PUK code yourself. As long as the PUK code is blocked, all eID options can be used, except PUK code. "
Expand Down
Loading

0 comments on commit 2d41be5

Please sign in to comment.