我正在尝试将JWT推送通知发送到pushSubscription端点。 当我回显结果时,我得到“无效的JWT提供”,我不知道为什么它不起作用。我在php中生成ECDSA签名,并且返回的JWT在jwt.io中经过了有效测试。 注意:密钥不在生产中,而是提供给上下文的
/*GET ENDPOINT*/
$endpoint_push = json_decode($subscription)->endpoint;
$public_key_push = 'BNQCrj2wbXHBAK1hyjvc9R5zjypBwWG6szD_STnDPy2ORVUqTWZD304JS5LTHK5ywYS2w-aRouH3EjxLG9bWla8';
$token_push = '';
//PREPARE PUSH//
// Create token header as a JSON string
$header = json_encode(['typ' => 'JWT', 'alg' => 'ES256']);
// Create token payload as a JSON string
$payload = json_encode(['aud' => 'https://fcm.googleapis.com', 'exp' => '1516239022', 'sub' => 'mailto:push@example.com']);
// Encode Header to Base64Url String
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
// Encode Payload to Base64Url String
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));
// Create Signature Hash
$privateKeyString =
"-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIHI6VMaMwvRag0foPp87+nhby3QrftcEsBHee6sdr0aZoAcGBSuBBAAK
oUQDQgAE91vCtp7tO4FyJbpgSS824PiuLR7LPNdwt+rcIe0uE19RUJz2Jgm8tRRD
HmBVzoQXNxcwVD1HfRMtU0wnUJOuAQ==
-----END EC PRIVATE KEY-----";
$privateKey = openssl_get_privatekey($privateKeyString);
$alg = OPENSSL_ALGO_SHA256;
$signature = null;
openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature, $privateKey, $alg);
// Encode Signature to Base64Url String
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
// Create JWT
$token_push = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
//SEND PUSH//
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint_push);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
$headers = array();
$headers[] = 'Ttl: 60';
$headers[] = 'Content-Length: 0';
$headers[] = 'Authorization: vapid t='.$token_push.', k='.$public_key_push.'';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
curl_close($ch);
echo json_encode($result);
答案 0 :(得分:0)
因此,我基本上尝试了完全相同的事情,我花了一段时间才意识到OpenSSL将其大部分输出包装为ASN.1格式的结构,JWT令牌需要您首先剥离它。
这是一个工作脚本,希望其他人会觉得有用。
请注意,此脚本未将任何数据附加到推送消息中,因此Web客户端将收到没有上下文的推送消息,并且他们无法访问(据我所知)JWT对象二者之一,因此也不能在其中放置任何有用的数据。
当然有一种将加密数据附加到请求的方法,这里不介绍。
<?php
/*
* If you get any part of this process wrong, Google gives the really helpful error message "invalid JWT provided".
*
* Mozilla (Firefox) gives a slightly just-as-useful error:
* {
* "code": 401, "errno": 109, "error": "Unauthorized",
* "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes",
* "message": "Request did not validate Invalid Authorization Header"
* }
*/
// Generate the keys like this, although you can probably do it in PHP.
// `openssl ecparam -genkey -name prime256v1 -noout -out server-push-ecdh-p256.pem &>/dev/null`;
// `openssl ec -in server-push-ecdh-p256.pem -pubout -out server-push-ecdh-p256.pub &>/dev/null`;
$privk = file_get_contents('server-push-ecdh-p256.pem');
$pubk = file_get_contents('server-push-ecdh-p256.pub');
$endpoint = "https://fcm.googleapis.com/fcm/send/some-really-long-unique-secret-string-that-your-web-client-gets-on-push-subscribe";
$contact = "mailto:your-admin-email-address@example.com";
function base64web_encode($a) {
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($a));
}
function base64web_decode($a) {
return base64_decode(str_replace(['-', '_', ''], ['+', '/', '='], $a));
}
$asn = new phpseclib\File\ASN1();
$header = [
"typ" => "JWT",
"alg" => "ES256"
];
$claims = [
// just the https://hostname part
"aud" => substr($endpoint, 0, strpos($endpoint, '/', 10)),
// this push message will be discarded after 24 hours of non-delivery
"exp" => time() + 86400,
// who the server can talk to if our push script is causing problems
"sub" => $contact
];
/*
* Note these need to be base64 url-safe encoded, not standard base64.
* @see https://tools.ietf.org/html/rfc4648#section-5
*/
$strHeader = base64web_encode(json_encode($header));
$strPayload = base64web_encode(json_encode($claims));
$toSign = $strHeader . '.' . $strPayload;
$signature = '';
if (!openssl_sign($toSign, $signature, $privk, OPENSSL_ALGO_SHA256)) {
trigger_error('sign failed: '. openssl_error_string());
}
/*
* openssl_sign produces a signature which is the hash wrapped in an
* ASN.1 structure, so we need to extract the 256-bit raw hash manually.
* There's no PHP function to do this, so we use a library.
*/
$xx = $asn->decodeBER($signature);
/** @var \phpseclib\Math\BigInteger $a */
/** @var \phpseclib\Math\BigInteger $b */
$a = $xx[0]['content'][0]['content']; // 128-bits
$b = $xx[0]['content'][1]['content']; // 128-bits
$signature = $a->toBytes() . $b->toBytes();
$strSignature = base64web_encode($signature);
/*
* This is now a complete JWT object.
*/
$jwt = $strHeader . '.' . $strPayload . '.' . $strSignature;
/*
* Our PEM formatted public key is wrapped in an ASN.1 structure, so just
* like our signature above, lets extract
* the raw public key part, which is the bit we need.
*/
$xx = $pubk;
$xx = str_replace(['-----BEGIN PUBLIC KEY-----','-----END PUBLIC KEY-----',"\n"], '', $xx);
$xx = base64_decode($xx);
$xx = $asn->decodeBER($xx);
$xx = $xx[0]['content'][1]['content'];
$xx = substr($xx, 1); // need to strip the first char, which is not part of the key
$xx = base64web_encode($xx);
$pubkey = $xx;
/*
* We need to append the public key used for signing this JWT object, so
* the server can validate the JWT and compare the public key against the
* push-registration by the client, where we said which public key we would
* accept pushes from.
*/
$headers = [
"Authorization: vapid t=$jwt,k=$pubkey",
"Content-length: 0",
"Ttl: 86400",
];
/**
* Push!
*/
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_exec($ch);
$ct = curl_multi_getcontent($ch);
echo curl_error($ch);
curl_close($ch);
echo $ct;