diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 853af318..215d00c6 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -495,7 +495,10 @@ CDoc2::CDoc2(const QString &path) CKey CDoc2::canDecrypt(const QSslCertificate &cert) const { - return keys.value(keys.indexOf(CKey(cert))); + auto key = keys.value(keys.indexOf(CKey(cert))); + if(key.unsupported || (!key.transaction_id.isEmpty() && cert.expiryDate() <= QDateTime::currentDateTimeUtc())) + return {}; + return key; } bool CDoc2::decryptPayload(const QByteArray &fmk) @@ -558,6 +561,7 @@ bool CDoc2::save(const QString &path) if(!cdoc20::checkConnection()) return false; QScopedPointer nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT)); + req.setRawHeader("x-expiry-time", QDateTime::currentDateTimeUtc().addMonths(6).toString(Qt::ISODate).toLatin1()); QEventLoop e; QNetworkReply *reply = nam->post(req, QJsonDocument({ {QLatin1String("recipient_id"), QLatin1String(recipient_id.toBase64())}, @@ -598,7 +602,7 @@ bool CDoc2::save(const QString &path) toVector(key.key), toVector(encrytpedKek)); recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, cdoc20::Recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); continue; } @@ -610,7 +614,7 @@ bool CDoc2::save(const QString &path) rsaKeyServer.Union(), toString(key.keyserver_id), toString(key.transaction_id)); recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, cdoc20::Recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); continue; } @@ -638,7 +642,7 @@ bool CDoc2::save(const QString &path) cdoc20::Recipients::EllipticCurve::secp384r1, toVector(key.key), toVector(ephPublicKeyDer)); recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, cdoc20::Recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); continue; } @@ -651,7 +655,7 @@ bool CDoc2::save(const QString &path) eccKeyServer.Union(), toString(key.keyserver_id), toString(key.transaction_id)); recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, cdoc20::Recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); } auto offset = cdoc20::Header::CreateHeader(builder, builder.CreateVector(recipients), diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 442ebf6a..6c90f740 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -250,6 +251,57 @@ void CKey::setCert(const QSslCertificate &c) isRSA = k.algorithm() == QSsl::Rsa; } +QUrlQuery CKey::fromKeyLabel() const +{ + if(!recipient.startsWith(QLatin1String("data:"), Qt::CaseInsensitive)) + return {}; + QString payload = recipient.mid(5); + QString mimeType; + QString encoding; + if(auto pos = payload.indexOf(','); pos != -1) + { + mimeType = payload.left(pos); + payload = payload.mid(pos + 1); + if(auto header = mimeType.split(';'); header.size() == 2) + { + mimeType = header.value(0); + encoding = header.value(1); + } + } + if(!mimeType.isEmpty() && mimeType != QLatin1String("application/x-www-form-urlencoded")) + return {}; + if(encoding == QLatin1String("base64")) + payload = QByteArray::fromBase64(payload.toLatin1()); + QUrlQuery query(payload); + if(!query.hasQueryItem(QStringLiteral("type")) || !query.hasQueryItem(QStringLiteral("v"))) + query.clear(); + return query; +} + +QString CKey::toKeyLabel() const +{ + if(cert.isNull()) + return recipient; + QDateTime exp = cert.expiryDate(); + if(Settings::CDOC2_USE_KEYSERVER) + exp = std::min(exp, QDateTime::currentDateTimeUtc().addMonths(6)); + auto escape = [](QString data) { return data.replace(',', QLatin1String("%2C")); }; + QString type = QStringLiteral("ID-card"); + if(auto t = SslCertificate(cert).type(); t & SslCertificate::EResidentSubType) + type = QStringLiteral("Digi-ID E-RESIDENT"); + else if(t & SslCertificate::DigiIDType) + type = QStringLiteral("Digi-ID"); + QUrlQuery q; + q.setQueryItems({ + {QStringLiteral("v"), QString::number(1)}, + {QStringLiteral("type"), type}, + {QStringLiteral("serial_number"), escape(cert.subjectInfo("serialNumber").join(','))}, + {QStringLiteral("cn"), escape(cert.subjectInfo("CN").join(','))}, + {QStringLiteral("server_exp"), QString::number(exp.toSecsSinceEpoch())}, + }); + return "data:" + q.query(QUrl::FullyEncoded); +} + CryptoDoc::CryptoDoc( QObject *parent ) diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index fc490392..8dab84ca 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -28,6 +28,7 @@ #include class QSslKey; +class QUrlQuery; class CKey { @@ -43,6 +44,8 @@ class CKey bool operator==(const CKey &other) const { return other.key == key; } void setCert(const QSslCertificate &c); + QUrlQuery fromKeyLabel() const; + QString toKeyLabel() const; QByteArray key, cipher, publicKey; QSslCertificate cert; diff --git a/client/translations/en.ts b/client/translations/en.ts index eb647402..48c3d14d 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -151,6 +151,14 @@ Unsupported cryptographic algorithm or recipient type Unsupported cryptographic algorithm or recipient type + + Decryption is possible until: + Decryption is possible until: + + + Decryption has expired + Decryption has expired + Application diff --git a/client/translations/et.ts b/client/translations/et.ts index d18fe4a1..65e00cf3 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -151,6 +151,14 @@ Unsupported cryptographic algorithm or recipient type Mittetoetatud krüptograafiline algoritm või adressaadi tüüp + + Decryption is possible until: + Dekrüpteerimine on võimalik kuni: + + + Decryption has expired + Dekrüpteerimine on aegunud + Application diff --git a/client/translations/ru.ts b/client/translations/ru.ts index 57a54905..f23f12f8 100644 --- a/client/translations/ru.ts +++ b/client/translations/ru.ts @@ -151,6 +151,14 @@ Unsupported cryptographic algorithm or recipient type Неподдерживаемый криптографический алгоритм или тип получателя + + Decryption is possible until: + Расшифровка возможна до: + + + Decryption has expired + Срок расшифровки истек + Application diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index 2d06df9a..0e202322 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -26,6 +26,8 @@ #include "Styles.h" #include "dialogs/KeyDialog.h" +#include + using namespace ria::qdigidoc4; class AddressItem::Private: public Ui::AddressItem @@ -62,11 +64,14 @@ AddressItem::AddressItem(CKey k, QWidget *parent, bool showIcon) ui->key.cert.subjectInfo("GN").join(' ') + " " + ui->key.cert.subjectInfo("SN").join(' ') : ui->key.cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); if(ui->label.isEmpty()) - ui->label = ui->key.recipient.toHtmlEscaped(); + { + if(QUrlQuery q = ui->key.fromKeyLabel(); !q.isEmpty()) + ui->label = q.queryItemValue(QStringLiteral("cn"), QUrl::FullyDecoded).toHtmlEscaped(); + else + ui->label = ui->key.recipient.toHtmlEscaped(); + } setIdType(); showButton(AddressItem::Remove); - if(ui->key.unsupported) - ui->idType->setText(tr("Unsupported cryptographic algorithm or recipient type")); } AddressItem::~AddressItem() @@ -152,33 +157,49 @@ void AddressItem::stateChange(ContainerState state) void AddressItem::setIdType() { - ui->idType->setHidden(ui->key.cert.isNull()); - if(ui->key.cert.isNull()) - return; - - QString str; + ui->expire->clear(); SslCertificate cert(ui->key.cert); SslCertificate::CertType type = cert.type(); - if(type & SslCertificate::DigiIDType) - str = tr("digi-ID"); + if(ui->key.unsupported) + ui->idType->setText(tr("Unsupported cryptographic algorithm or recipient type")); + else if(type & SslCertificate::DigiIDType) + ui->idType->setText(tr("digi-ID")); else if(type & SslCertificate::EstEidType) - str = tr("ID-card"); + ui->idType->setText(tr("ID-card")); else if(type & SslCertificate::MobileIDType) - str = tr("mobile-ID"); + ui->idType->setText(tr("mobile-ID")); else if(type & SslCertificate::TempelType) { if(cert.keyUsage().contains(SslCertificate::NonRepudiation)) - str = tr("e-Seal"); + ui->idType->setText(tr("e-Seal")); else if(cert.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) - str = tr("Authentication certificate"); + ui->idType->setText(tr("Authentication certificate")); else - str = tr("Certificate for Encryption"); + ui->idType->setText(tr("Certificate for Encryption")); + } + else + { + QUrlQuery q = ui->key.fromKeyLabel(); + ui->idType->setText(q.queryItemValue(QStringLiteral("type"), QUrl::FullyDecoded).toHtmlEscaped()); + if(QString server_exp = q.queryItemValue(QStringLiteral("server_exp"), QUrl::FullyDecoded); !server_exp.isEmpty()) + { + auto date = QDateTime::fromSecsSinceEpoch(server_exp.toLongLong()); + bool canDecrypt = QDateTime::currentDateTimeUtc() < date; + ui->expire->setProperty("label", canDecrypt ? QStringLiteral("good") : QStringLiteral("error")); + ui->expire->setText(canDecrypt ? QStringLiteral("%1 %2").arg( + tr("Decryption is possible until:"), DateTime(date.toLocalTime()).formatDate(QStringLiteral("dd. MMMM yyyy"))) : + tr("Decryption has expired")); + } + } + + if(!cert.isNull()) + { + ui->expire->setProperty("label", QStringLiteral("default")); + ui->expire->setText(QStringLiteral("%1 %2").arg( + cert.isValid() ? tr("Expires on") : tr("Expired on"), + DateTime(cert.expiryDate().toLocalTime()).formatDate(QStringLiteral("dd. MMMM yyyy")))); } - if(!str.isEmpty()) - str += QStringLiteral(" - "); - DateTime date(cert.expiryDate().toLocalTime()); - ui->idType->setText(QStringLiteral("%1%2 %3").arg(str, - cert.isValid() ? tr("Expires on") : tr("Expired on"), - date.formatDate(QStringLiteral("dd. MMMM yyyy")))); + ui->idType->setHidden(ui->idType->text().isEmpty()); + ui->expire->setHidden(ui->expire->text().isEmpty()); } diff --git a/client/widgets/AddressItem.ui b/client/widgets/AddressItem.ui index e32d134a..d5baac0e 100644 --- a/client/widgets/AddressItem.ui +++ b/client/widgets/AddressItem.ui @@ -33,12 +33,24 @@ font-weight: 700; #idType { color: #07142A; } -#expire { +QLabel[label="default"] { color: #07142A; background: #F3F5F7; padding: 2px 8px; border-radius: 8px; } +QLabel[label="error"] { +color: #AD2A45; +background: #F5EBED; +padding: 2px 8px; +border-radius: 8px; +} +QLabel[label="good"] { +color: #1A641B; +background: #EAF8EA; +padding: 2px 8px; +border-radius: 8px; +} QToolButton { font-weight: 700; border-radius: 2px; @@ -120,6 +132,9 @@ color: #727679; Expire + + default + @@ -214,7 +229,7 @@ color: #727679; QSvgWidget QWidget -
QtSvg/QSvgWidget
+
QSvgWidget
1