Как заставить WebPush (с использованием Qt) работать с браузером Windows Edge?C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Как заставить WebPush (с использованием Qt) работать с браузером Windows Edge?

Сообщение Anonymous »

Моя реализация Web-Push отлично работает с Chrome и Firefox, но не с MS Edge (даже после % декодирования).
Вопрос: Как заставить это работать?
К вашему сведению, их URL-адреса отображаются в разных форматах:

URL-адрес Chrome:
"https://fcm.googleapis.com/fcm/send/eBt ... 0GS69gx0ZK bnbrOD4FB7m8Ou8ZpS0X0hcEeSLjxYdsYKi896YibgFrocO7L8qDU2SeZfr6L7Pqc2DZ7A_82Qik7PINAF_S2rskKofe"
Пограничный URL:
"https://wns2-pn1p.notify.windows.com/w/ ... wUFnM6fJEy SP1Jr0Fi3OU2rgTICDfl2Bsj6jS4eNZLo0FQK1diyqN1v6zi7k9yBijIksEvKegd2q2Z%2bGAIoIt0QfnQyluOGSNgXCrF0jr4h3 Ka5aJUVdl7aBSkkULq5PI7wvGl3mGMD9I3xk71jG%2bjBAwoF2ThvYfefeEpd5xMAJ%2bWLBd3FD56kD1zTplOhwS4Leysw3SFBX h393%2b8MfDFJCAi%2f0mfKLBy%2fTCuha50GJT7oBJHemhFCi5E2CliZ9dFB2IPCpEa%2bEfgVWHC6GkpRMjUSCs0%3d"

Ошибка напечатана на стороне клиента:

400: «Ошибка передачи – сервер ответил: "

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

Для Chrome он просто возвращает 201, и уведомление отображается в панель мониторинга.

Обратите внимание, что та же комбинация ключей VAPID, Endpoint, P256DH, AUTH и т. д. работает с известным API push-уведомлений C# (github).

Исходный код: Из библиотеки pusha(github) я извлек соответствующую часть и реализовал минимальный рабочий пример с помощью Qt framework. Возможно, придется добавить несколько файлов библиотеки ecec (github). После этого все работает нормально только с одним исходным файлом ниже!
web-push.pro

Код: Выделить всё

TEMPLATE = app
CONFIG += console c++17
CONFIG -= app_bundle
QT += network
LIBS += -lcrypto -lssl #ssl is optional

SOURCES += \
ecec/encrypt.c \
ecec/keys.c \
ecec/trailer.c \
main.cpp

HEADERS += \
ecec/ece.h \
ecec/keys.h \
ecec/trailer.h
main.cpp

Код: Выделить всё

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ENDPOINT "https://fcm.googleapis.com/fcm/send/eBt-ioGXt4A:APA91bGkIsaIeVWAG-cU-UysXKl5pevjnZzIRT0GS69gx0ZKbnbrOD4FB7m8Ou8ZpS0X0hcEeSLjxYdsYKi896YibgFrocO7L8qDU2SeZfr6L7Pqc2DZ7A_82Qik7PINAF_S2rskKofe"
#define P256DH "BM8Md9QY7egq1UqZytneixPkITMK5556EHBQD0yTZY6eW6eeK89TsdpymT79rIc9xfouL1FLH-ACb9kEuf6iea0"
#define AUTH "w7o5Sip9yBm6ME1C88pebg"
#define VAPID_PRIVATE_KEY "T2blCzCxRzFl2yjI-Q6WLxW1PoiTxSLPExtP9yOxkgM"
#define VAPID_PUBLIC_KEY  "BMRAeeyEY6imWuCktv6NX3o5prv4UWndTUWTEO4dCgmzX8YDgjuIbPslpSM2fdfTbOjmnLIBkJKch2wGnTm8_sY"

#define WEBPUSH_PAYLOAD_KEY_AUD      "aud"
#define WEBPUSH_PAYLOAD_KEY_EXP      "exp"
#define WEBPUSH_PAYLOAD_KEY_SUB      "sub"
#define WEBPUSH_VAPID_KEY_ALG        "alg"
#define WEBPUSH_VAPID_KEY_TYP        "typ"

#define HTTP_DH               ";dh="
#define HTTP_AUTHORIZATION    "authorization"
#define HTTP_CONTENT_ENCODING "content-encoding"
#define HTTP_CONTENT_TYPE     "content-type"
#define HTTP_CONTENT_LENGTH   "content-length"
#define HTTP_CRYPTO_KEY       "crypto-key"
#define HTTP_ENCRYPTION       "encryption"
#define HTTP_P256             "p256ecdsa="
#define HTTP_RS_SALT          "rs=4096;salt="
#define HTTP_TTL              "ttl"

#define QT_URL_ENCODING QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals
#define MAKE_UNIQUE(MALLOC, FREE) std::unique_ptr{MALLOC, &FREE}

static const QByteArray HTTP_AUTHORIZATION_v = "webPush ",
HTTP_ENCODING_v = "aesgcm",
HTTP_CONTENT_TYPE_v = "application/octet-stream",
HTTP_TTL_v = "3600",
WEBPUSH_VAPID_ALG_v = "ES256", // must be in capital
WEBPUSH_VAPID_TYP_v = "jwt";

#define SUBSCRIBER "mailto:root@saarathy.in"
#define MY_PAYLOAD "{ \
\"body\": \"Shree Vallabh!\", \
\"tag\": \"AAHLAAD\", \
\"data\": {\"tag\": \"test\"}, \
\"title\": \"Saarathy\" \
}"
#define SYMBOL_DOT "."

struct Subscription
{
uint8_t m_P256dh[ECE_WEBPUSH_PUBLIC_KEY_LENGTH];
uint8_t m_Auth[ECE_WEBPUSH_AUTH_SECRET_LENGTH];
};

struct Payload
{
uint8_t   m_Salt[ECE_SALT_LENGTH], m_PublicKeySender[ECE_WEBPUSH_PUBLIC_KEY_LENGTH];
QByteArray m_Cipher;
size_t m_CipherLength;
};

auto
GetJson (QVariantMap map)
{
auto object = QJsonObject::fromVariantMap(map);
return QJsonDocument(object).toJson(QJsonDocument::Compact);
}

auto
CreateECKey (const QByteArray& rawKey)
{
auto pECKey = MAKE_UNIQUE(::EC_KEY_new_by_curve_name(NID_X9_62_prime256v1), ::EC_KEY_free);
::EC_KEY_oct2priv(pECKey.get(),
reinterpret_cast(rawKey.data()), rawKey.size());
const ::EC_GROUP* const pGroup = ::EC_KEY_get0_group(pECKey.get());

auto point = MAKE_UNIQUE(::EC_POINT_new(pGroup), ::EC_POINT_free);
::EC_POINT_mul(pGroup, point.get(),
::EC_KEY_get0_private_key(pECKey.get()), nullptr, nullptr, nullptr);
::EC_KEY_set_public_key(pECKey.get(), point.get());
return pECKey;
}

QByteArray
VapidSign (EC_KEY& ecKey,
const QByteArray&  sign)
{
unsigned char digest[SHA256_DIGEST_LENGTH];
::SHA256(reinterpret_cast(sign.data()), sign.size(), digest);

const auto pSign = MAKE_UNIQUE(::ECDSA_do_sign(digest, SHA256_DIGEST_LENGTH, &ecKey),
::ECDSA_SIG_free);
const ::BIGNUM *pR = nullptr, *pS = nullptr;
::ECDSA_SIG_get0(pSign.get(), &pR, &pS);

const auto r_Size = BN_num_bytes(pR), s_Size = BN_num_bytes(pS);
QByteArray signValue(r_Size + s_Size, 0);
::BN_bn2bin(pR, reinterpret_cast(signValue.data()));
::BN_bn2bin(pS, reinterpret_cast(&signValue[r_Size]));
return signValue;
}

QByteArray
VapidAuthorize (const QString& endpoint,
const QString& subscriber,
const int expiration,
EC_KEY& ecKey)
{
const auto audience = endpoint.section('/', 0, 2);
const auto params = GetJson({{WEBPUSH_PAYLOAD_KEY_AUD, audience},
{WEBPUSH_PAYLOAD_KEY_EXP, expiration},
{WEBPUSH_PAYLOAD_KEY_SUB, subscriber}}),
header = GetJson({{WEBPUSH_VAPID_KEY_ALG, WEBPUSH_VAPID_ALG_v},
{WEBPUSH_VAPID_KEY_TYP, WEBPUSH_VAPID_TYP_v}}),
sign = header.toBase64(QT_URL_ENCODING) + SYMBOL_DOT + params.toBase64(QT_URL_ENCODING);
return sign + SYMBOL_DOT + VapidSign(ecKey, sign).toBase64(QT_URL_ENCODING);
}

size_t
GetCipherLength (const uint32_t rs,
const size_t padSize,
const size_t padLen,
const size_t plaintextLen)
{
const size_t overhead = padSize + ECE_TAG_LENGTH, dataLen = plaintextLen + padLen,
maxBlockLen = rs - overhead, numRecords = (dataLen / maxBlockLen) + 1;
if(rs  SIZE_MAX - plaintextLen or
numRecords > (SIZE_MAX - dataLen) / overhead)
return 0;
return dataLen + (overhead * numRecords);
}

void
WebPush (const QString& endpoint,
const QByteArray& authorization,
const QByteArray& vapidPublicKey,
const Payload& payload)
{
int argc;
QCoreApplication app{argc, nullptr};
const auto dh = QByteArray(reinterpret_cast(payload.m_PublicKeySender),
ECE_WEBPUSH_PUBLIC_KEY_LENGTH).toBase64(QT_URL_ENCODING),
salt = QByteArray(reinterpret_cast(payload.m_Salt),
ECE_SALT_LENGTH).toBase64(QT_URL_ENCODING);

QNetworkAccessManager networkManager;
QNetworkRequest request(QUrl{endpoint});
request.setRawHeader(HTTP_AUTHORIZATION, HTTP_AUTHORIZATION_v + authorization);
request.setRawHeader(HTTP_CONTENT_ENCODING, HTTP_ENCODING_v);
request.setRawHeader(HTTP_CONTENT_TYPE, HTTP_CONTENT_TYPE_v);
request.setRawHeader(HTTP_CONTENT_LENGTH, QByteArray::number(payload.m_Cipher.size()));
request.setRawHeader(HTTP_CRYPTO_KEY, HTTP_P256 + vapidPublicKey + HTTP_DH + dh);
request.setRawHeader(HTTP_ENCRYPTION, HTTP_RS_SALT + salt);
request.setRawHeader(HTTP_TTL, HTTP_TTL_v);

QNetworkReply* pReply = networkManager.post(request, payload.m_Cipher);
QObject::connect(pReply, &QNetworkReply::finished, pReply, &QNetworkReply::deleteLater);
QObject::connect(pReply, &QNetworkReply::finished, pReply,
[=] ()
{
qDebug() attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()


Подробнее здесь: [url]https://stackoverflow.com/questions/79322818/how-to-make-webpush-using-qt-work-with-windows-edge-browser[/url]
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»