Web Push Notifications出现InvalidSignature错误求助
解决Web Push Notifications的InvalidSignature错误
错误信息
收到推送服务返回的401未授权错误:
{"code":401,"errno":109,"error":"Unauthorized","message":"InvalidSignature","more_info":"http://autopush.readthedocs.io/en/latest/http.html#error-codes"}
核心问题排查与修复
1. 私钥重建方式错误
你在sendPushNotification函数中重建EC密钥对的参数格式不符合OpenSSL要求,导致生成的私钥与原始密钥不匹配,签名无效。
原错误代码:
$keyPair = openssl_pkey_new([ 'ec' => [ 'digest_alg' => 'sha256', 'private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1', 'x' => hex2bin($vapidKeys->x), 'y' => hex2bin($vapidKeys->y), 'd' => hex2bin($vapidKeys->d) ] ]);
修复方案:
直接从保存的d值导入私钥,无需重建整个密钥对:
// 从vapid.json读取私钥参数 $vapidKeys = json_decode(file_get_contents('vapid.json')); // 直接导入私钥 $privateKey = openssl_pkey_get_private([ 'private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1', 'ec' => [ 'd' => hex2bin($vapidKeys->d), ] ]); // 验证私钥是否导入成功 if (!$privateKey) { die('Failed to load private key: ' . openssl_error_string()); }
2. 优化密钥生成与存储
为避免后续密钥处理错误,建议在生成密钥时直接导出完整的PEM格式私钥和公钥,而非仅保存x/y/d值:
修改generateVapidKeys函数:
function generateVapidKeys(){ if(file_exists('vapid.json')){ $vapidKeys = json_decode(file_get_contents('vapid.json')); return $vapidKeys->publicKeyBase64Url; }else{ $keyPair = openssl_pkey_new([ 'private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1', ]); // 导出PEM私钥 openssl_pkey_export($keyPair, $privateKeyPem); // 获取公钥详情并生成base64url格式的公钥 $publicKeyDetails = openssl_pkey_get_details($keyPair); $publicKeyRaw = hex2bin('04' . bin2hex($publicKeyDetails['ec']['x']) . bin2hex($publicKeyDetails['ec']['y'])); $publicKeyBase64Url = rtrim(strtr(base64_encode($publicKeyRaw), '+/', '-_'), '='); // 保存完整密钥信息 file_put_contents('vapid.json', json_encode([ 'privateKeyPem' => $privateKeyPem, 'publicKeyBase64Url' => $publicKeyBase64Url ], JSON_PRETTY_PRINT)); return $publicKeyBase64Url; } }
在发送通知时直接加载PEM私钥:
$vapidKeys = json_decode(file_get_contents('vapid.json')); $privateKey = openssl_pkey_get_private($vapidKeys->privateKeyPem);
3. 验证JWT签名与Audience参数
- Audience(aud)检查:确保
generateVapidToken中的aud值是推送endpoint的协议+主机(如https://updates.push.services.mozilla.com),不要包含路径。 - JWT签名验证:在本地验证生成的JWT是否有效,确保签名算法为
ES256,且公钥与签名匹配。
4. 修正请求头与Payload
确保请求头中的Authorization格式正确,且Payload符合推送服务要求:
$headers = [ 'Authorization: vapid t='.$token.',k='.$vapidKeys->publicKeyBase64Url, 'Content-Type: application/json', ]; // Payload直接传入通知内容,无需嵌套在notification键中(部分服务要求直接结构) $options[CURLOPT_POSTFIELDS] = json_encode($payload);
验证步骤
- 删除旧的
vapid.json,重新生成密钥对 - 重新订阅推送服务,获取新的endpoint
- 运行发送代码,检查是否返回成功响应
内容的提问来源于stack exchange,提问作者DrBrad
相关产品推荐
相关产品推荐

