我一直在尝试制作自己的推送通知 API。我已经使令牌正常工作,并且端点不再有任何错误响应。该消息似乎发送到终点很好,但我在浏览器上从未收到任何内容。
我将展示我用来加密有效负载的代码和我制作的 Service Worker。
我收到 201 响应,表示已收到并接受。 以下是我用来创建此内容的资源:
https://web-push-book.gauntface.com/web-push-protocol/
这是我用来加密有效负载的方法。
$payload = [
'notification' => [
'title' => 'New Notification',
'body' => 'This is the body of the notification.',
'icon' => 'icon.png'
]
];
$localJWK = new JWK();
$localJWK->create();
$userJWK = new JWK();
[$x, $y] = $userJWK->decode_public_key($subscription->keys->p256dh);
$userJWK->create(
[
'x' => $x,
'y' => $y
]
);
$sharedSecret = openssl_pkey_derive($userJWK->public_to_pem(), $localJWK->private_to_pem(), 256);
$sharedSecret = str_pad($sharedSecret, 32, chr(0), STR_PAD_LEFT);
//AES-GCM-128
$decodedLocalPublicKey = base64url_decode($localJWK->public_to_base64());
$info = 'WebPush: info'.chr(0).base64url_decode($subscription->keys->p256dh).$decodedLocalPublicKey;
$ikm = hkdf(base64url_decode($subscription->keys->auth), $sharedSecret, $info, 32);
echo bin2hex($ikm).PHP_EOL;
//$length = chr(0).'A';
//$context = chr(0).$length.(0x04.$x.$y).$length.(0x04.$localJWK->getXandY());
$salt = base64url_encode(random_bytes(16));
$contentEncryptionKey = hkdf($salt, $ikm, 'Content-Encoding: aes128gcm'.chr(0), 16);
echo bin2hex($contentEncryptionKey).PHP_EOL;
$nonce = hkdf($salt, $ikm, 'Content-Encoding: nonce'.chr(0), 12);
echo bin2hex($nonce).PHP_EOL.PHP_EOL;
$tag = '';
$encryptedPayload = openssl_encrypt(json_encode($payload), 'aes-128-gcm', $contentEncryptionKey, OPENSSL_RAW_DATA, $nonce, $tag);
$content = $salt.pack('N*', 4096).pack('C*', mb_strlen($decodedLocalPublicKey, '8bit')).$decodedLocalPublicKey.$encryptedPayload;
$headers = [
'TTL: '.(60*60*12), //12-24 hours
'Urgency: normal', //<very-low | low | normal | high>
'Content-Type: application/octet-stream',
'Content-Encoding: aes128gcm',
'Content-Length: '.mb_strlen($content, '8bit'),
'Authorization: WebPush '.$token,
//'Encryption: salt='.base64url_encode($salt),
//'Authorization: vapid t='.$token.',k='.$publicKey,
//'Authorization: key=' . $subscription->keys->auth,
'Crypto-Key: p256ecdsa='.$keys['publicKey'],//.';dh='.$localJWK->public_to_base64(),//$subscription->keys->auth,
//'Crypto-Key: p256ecdsa='.$subscription->keys->p256dh.';dh='.$subscription->keys->auth,//$subscription->keys->p256dh,
'Content-Type: application/json',
];
$options = [
CURLOPT_URL => $subscription->endpoint,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $content,
CURLOPT_RETURNTRANSFER => true,
];
$ch = curl_init();
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
if ($result === false) {
echo 'Error: ' . curl_error($ch) . PHP_EOL;
} else {
echo 'Push notification sent successfully!' . PHP_EOL;
}
function hkdf($salt, $ikm, $info, $length){
$prk = hash_hmac('sha256', $ikm, $salt, true);
return mb_substr(hash_hmac('sha256', $info.chr(1), $prk, true), 0, $length, '8bit');
}
function base64url_encode($data){
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64url_decode($data){
return base64_decode(strtr($data, '-_', '+/'));
}
为了展示 JWK 类,我将发布它的要点。 https://gist.github.com/DrBrad/882d5a932df4a441ae7763ddc3b1f174
最后这是我的服务人员:
self.addEventListener('push', function (event) {
console.log(event);
});
事实证明问题出在我的盐、PKR 和内容填充上。
我在 GitHub 上有固定代码: https://github.com/DrBrad/PHP-Web-Push-Notifications/tree/main
此资源帮助我分配解决此问题: https://tests.peter.sh/push-encryption-verifier/