#include #include #include "eetsender.h" #include "eetsigner.h" #include "eettemplate.h" #include #include #include #include const QString EetSender::ms_nsDef = "declare namespace eet = \"http://fs.mfcr.cz/eet/schema/v3\";\n" "declare namespace ds = \"http://www.w3.org/2000/09/xmldsig#\";\n" "declare namespace wsu = \"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\";\n" "declare namespace wsse = \"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\";\n" "declare namespace senc = \"http://schemas.xmlsoap.org/soap/encoding/\";\n" "declare namespace senv = \"http://schemas.xmlsoap.org/soap/envelope/\";\n"; bool EetSender::m_online = true; EetSender::EetSender(QObject *parent) : QObject(parent) { m_signer = nullptr; m_resut = nullptr; m_checkSignature = true; m_resut = new EetResult(this); m_manager = new QNetworkAccessManager(this); connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); connect(m_manager, &QNetworkAccessManager::sslErrors, [this](QNetworkReply *rep, QList){ m_resut->setStatus(EetResult::SSL_ERROR); emit sendFinished(); rep->deleteLater(); }); m_serviceUrl = PRODUCTION_URL; m_timer = new QTimer(this); m_timer->setInterval(1000); m_timer->setSingleShot(true); } void EetSender::sendRequest(EetRequest *request) { if (m_signer == nullptr) { m_resut->setStatus(EetResult::USER_CERT_ERROR); emit sendFinished(); return; } request->setUuidZpravy(QUuid::createUuid()); EetTemplate tempBody(BODY_TEMPLATE); tempBody.setSigner(m_signer); tempBody.setResult(m_resut); QString strBody = tempBody.fillTemplate(request); QByteArray digest = m_signer->sha256HashData(strBody.toUtf8()); QMap val; val["digest"] = QString(digest.toBase64()); EetTemplate tempSignature(SIGNATURE_TEMPLATE); QString strSignature = tempSignature.fillTemplate(val); QByteArray sign = m_signer->signData(strSignature.toUtf8()); val["signature"] = QString(sign.toBase64()); val["soap:Body"] = strBody; val["certb64"] = m_signer->getCertificate(); EetTemplate tempRequest(REQUEST_TEMPLATE); QString strRequest = tempRequest.fillTemplate(val); QNetworkRequest req(QUrl(m_serviceUrl.toStdString().c_str())); qDebug() << strRequest; if (m_online) { QNetworkReply *rep = m_manager->post(req, strRequest.toUtf8()); m_timer->start(); connect(m_timer, &QTimer::timeout, [this, rep](){ rep->abort(); m_resut->setStatus(EetResult::TIMEDOUT); emit sendFinished(); }); } else { m_resut->setStatus(EetResult::OFFLINE); m_timer->stop(); emit sendFinished(); } } void EetSender::setupSigner(const QString &certPath, const QString &passwd) { if (m_signer != nullptr) { delete m_signer; } m_signer = new EetSigner(this); m_signer->setup(certPath, QCA::SecureArray(passwd.toUtf8())); } void EetSender::setCheckSignature(bool checkSignature) { m_checkSignature = checkSignature; } bool EetSender::checkSignature() const { return m_checkSignature; } void EetSender::setPlayground(bool pg) { m_serviceUrl = (pg ? PLAYGROUND_URL : PRODUCTION_URL); } EetResult *EetSender::resut() const { return m_resut; } void EetSender::setTimeout(int timeout) { m_timer->setInterval(timeout); } bool EetSender::verifySignature(const QByteArray &repData) { QString queryString("//senv:Envelope/senv:Header/wsse:Security/ds:Signature/ds:SignedInfo"); QString signedInfo, certB64, signatureB64; QXmlQuery q; q.setFocus(QString(repData)); q.setQuery(ms_nsDef + queryString); q.evaluateTo(&signedInfo); QStringList list = signedInfo.split("\n"); signedInfo = "\n"; for (int i = 1; i < list.length(); i++) { QString line = list[i]; line = line.replace(QRegExp("^(\\s+)<([A-Za-z]+)([A-Za-z0-9-\"=/#:\\.\\ ]+)/>"), "\\1<\\2\\3>"); signedInfo += line + "\n"; } signedInfo = signedInfo.trimmed(); signedInfo = signedInfo.replace(" ", " "); queryString = "//senv:Envelope/senv:Header/wsse:Security/wsse:BinarySecurityToken/text()"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&certB64); queryString = "//senv:Envelope/senv:Header/wsse:Security/ds:Signature/ds:SignatureValue/text()"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&signatureB64); QCA::ConvertResult res; QCA::Certificate cert = QCA::Certificate::fromDER(QByteArray::fromBase64(certB64.toUtf8()), &res); if (res != QCA::ConvertGood) { m_resut->setStatus(EetResult::SERVICE_CERT_ERROR); return false; } QCA::PublicKey pubKey = cert.subjectPublicKey(); if (!pubKey.canVerify()) { m_resut->setStatus(EetResult::SERVICE_CERT_ERROR); return false; } bool signValid = pubKey.verifyMessage(QCA::MemoryRegion(signedInfo.toUtf8()), QByteArray::fromBase64(signatureB64.toUtf8()), QCA::EMSA3_SHA256); if (!signValid) { m_resut->setStatus(EetResult::INVALID_SIGNATURE); return false; } return true; } void EetSender::replyFinished(QNetworkReply *reply) { m_timer->stop(); if (reply->error() != QNetworkReply::NoError) { m_resut->setStatus(EetResult::SERVER_ERROR); emit sendFinished(); reply->deleteLater(); return; } QByteArray repData = reply->readAll(); qDebug() << QString(repData); QXmlQuery q; q.setFocus(QString(repData)); QXmlResultItems items; QString result; QString queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Chyba"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&items); EetMessageList errors; QXmlItem item = items.next(); while (!item.isNull()) { EetMessage *mesg = new EetMessage(m_resut); queryString = "./@kod/data(.)"; q.setQuery(ms_nsDef + queryString); q.setFocus(item); q.evaluateTo(&result); result = result.trimmed(); mesg->setCode(result.toInt()); queryString = "./string()"; q.setQuery(ms_nsDef + queryString); q.setFocus(item); q.evaluateTo(&result); result = result.trimmed(); mesg->setMessage(result); errors.append(mesg); item = items.next(); } m_resut->setErrors(errors); if (m_checkSignature && errors.isEmpty() && !verifySignature(repData)) { emit sendFinished(); return; } queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Hlavicka/@uuid_zpravy/data(.)"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&result); result = result.trimmed(); m_resut->setUuid(QUuid(result)); queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Hlavicka/@dat_prij/data(.)"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&result); result = result.trimmed(); m_resut->setReciveDate(QDateTime::fromString(result)); queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Potvrzeni/@fik/data(.)"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&result); result = result.trimmed(); m_resut->setFik(result); queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Varovani"; q.setQuery(ms_nsDef + queryString); q.evaluateTo(&items); EetMessageList warnings; item = items.next(); while (!item.isNull()) { EetMessage *mesg = new EetMessage(m_resut); queryString = "./@kod_varov/data(.)"; q.setQuery(ms_nsDef + queryString); q.setFocus(item); q.evaluateTo(&result); result = result.trimmed(); mesg->setCode(result.toInt()); queryString = "./string()"; q.setQuery(ms_nsDef + queryString); q.setFocus(item); q.evaluateTo(&result); result = result.trimmed(); mesg->setMessage(result); warnings.append(mesg); item = items.next(); } m_resut->setWarnings(warnings); if (errors.isEmpty()) { m_resut->setReciveDate(QDateTime::currentDateTime()); } else { m_resut->setStatus(EetResult::DATA_ERROR); } emit sendFinished(); reply->deleteLater(); }