You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);

验证步骤

  1. 删除旧的vapid.json,重新生成密钥对
  2. 重新订阅推送服务,获取新的endpoint
  3. 运行发送代码,检查是否返回成功响应

内容的提问来源于stack exchange,提问作者DrBrad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:24:55