如何使用 APNs Auth Key 和标准 CLI 工具发送 APNs 推送消息?
How to send APNs push messages using APNs Auth Key and standard CLI tools?
Apple 最近向 APNS 添加了一种新的身份验证方法 ()。
下载的密钥是一个 .p8
包含私钥的文件:
$ cat APNSAuthKey_3HHEB343FX.p8
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBH...Already.Revoked...lHEjCX1v51W
-----END PRIVATE KEY-----
我正在使用旧方法使用 APNs 消息 - 将它们添加到钥匙串,请求证书并使用 OpenSSL 将消息发送到 gateway.production.push.apple.com:2195
。
如何使用新格式使用标准 CLI Linux 工具(OpenSSL、Python 等)发送推送通知?
您可以使用 Apple 推送通知身份验证密钥(沙盒和生产)通过 NODE JS 发送推送通知。 this link
中有 Elad Nava 提供的教程
本教程包含创建 Apple 推送通知身份验证密钥和设置本地服务器到 运行 Node JS 代码以发送推送通知的所有步骤。您可以 运行 本地计算机中的代码并测试推送通知。
希望这会有所帮助。
如果您的计算机上安装了支持 HTTP/2 的 curl 和支持 ECDSA 的 openssl,您可以使用以下脚本通过 APNs 授权密钥测试推送通知:
#!/bin/bash
deviceToken=b27371497b85611baf9052b4ccfb9641ab7fea1d01c91732149c99cc3ed9342f
authKey="./APNSAuthKey_ABC1234DEF.p8"
authKeyId=ABC1234DEF
teamId=TEAM123456
bundleId=com.example.myapp
endpoint=https://api.development.push.apple.com
read -r -d '' payload <<-'EOF'
{
"aps": {
"badge": 2,
"category": "mycategory",
"alert": {
"title": "my title",
"subtitle": "my subtitle",
"body": "my body text message"
}
},
"custom": {
"mykey": "myvalue"
}
}
EOF
# --------------------------------------------------------------------------
base64() {
openssl base64 -e -A | tr -- '+/' '-_' | tr -d =
}
sign() {
printf "" | openssl dgst -binary -sha256 -sign "$authKey" | base64
}
time=$(date +%s)
header=$(printf '{ "alg": "ES256", "kid": "%s" }' "$authKeyId" | base64)
claims=$(printf '{ "iss": "%s", "iat": %d }' "$teamId" "$time" | base64)
jwt="$header.$claims.$(sign $header.$claims)"
curl --verbose \
--header "content-type: application/json" \
--header "authorization: bearer $jwt" \
--header "apns-topic: $bundleId" \
--data "$payload" \
$endpoint/3/device/$deviceToken
注意: 我使用此脚本的一个细微变化在 macOS 上使用 homebrew 版本的 curl 和 openssl 进行测试:http://thrysoee.dk/apns/
Apple 现在有 this 方法 Send a Push Notification Using a Token.
的文档
瞧 PHP curl 和 HTTP/2 的样子。此脚本 return 200 ok 状态代码以及生成的令牌 ID。
// THE FINAL SCRIPT WITHOUT DEPENDENCIES!!! ...except curl with http2
$device_token = "a0abd886etc...";
//echo $key;
$kid = "YOURKEYID";
$teamId = "YOURTEAMID";
$app_bundle_id = "your.app.bundle";
$base_url = "https://api.development.push.apple.com";
$header = ["alg" => "ES256", "kid" => $kid];
$header = base64_encode(json_encode($header));
$claim = ["iss" => $teamId, "iat" => time()];
$claim = base64_encode(json_encode($claim));
$token = $header.".".$claim;
// key in same folder as the script
$filename = "KeyFromApple.p8";
$pkey = openssl_pkey_get_private("file://{$filename}");
$signature;
openssl_sign($token, $signature, $pkey, 'sha256');
$sign = base64_encode($signature);
$jws = $token.".".$sign;
$message = '{"aps":{"alert":"You are welcome.","sound":"default"}}';
function sendHTTP2Push($curl, $base_url, $app_bundle_id, $message, $device_token, $jws) {
$url = "{$base_url}/3/device/{$device_token}";
// headers
$headers = array(
"apns-topic: {$app_bundle_id}",
'Authorization: bearer ' . $jws
);
// other curl options
curl_setopt_array($curl, array(
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
CURLOPT_URL => $url,
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $message,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_HEADER => 1
));
// go...
$result = curl_exec($curl);
if ($result === FALSE) {
throw new Exception("Curl failed: " . curl_error($curl));
}
print_r($result."\n");
// get response
$status = curl_getinfo($curl);
return $status;
}
// open connection
$curl = curl_init();
sendHTTP2Push($curl, $base_url, $app_bundle_id, $message, $device_token, $jws);
尼古拉斯·曼齐尼 (Nicolas Manzini) 提供的答案虽然有帮助,但对我来说并不完全适用。我还想使用命令行 curl。特别是,读取 .p8 文件会导致一些问题。修改版本:
<?php
$teamid = 'YOURTEAMID';
$keyid = 'A464FN6T93'; // in the name of the file downloaded from Apple Certs
// since it is a universal key, I wanted this in a location accessible to several accounts
$keyFile = '/path/to/file/AuthKey_A464FN6T93.p8';
$privateKey = openssl_pkey_get_private('file://' . $keyFile);
if (! $privateKey) {
die('could not find: ' . $keyFile);
}
$header = ['alg' => 'ES256', 'kid' => $keyid];
$header = base64_encode(json_encode($header));
$claim = ['iss' => $teamid, 'iat' => time()];
$claim = base64_encode(json_encode($claim));
$tok = $header . '.' . $claim;
// pass in empty $signature, 2nd line below fills it
$signature = '';
$result = openssl_sign($tok, $signature, $privateKey, OPENSSL_ALGO_SHA256); // 'sha256'
if (! $result) {
die('unable to create signature');
}
$sign = base64_encode($signature);
openssl_free_key($privateKey);
$jwt = $tok . '.' . $sign;
foreach($tokens as $token) {
$cmd = '\
/usr/local/bin/curl -v \
-d \'{"aps":{"alert":{"title":"Notification Title","body":"You are being notified!"},"sound":"default"}}\' \
-H "apns-topic: com.app.bundle.id" \
-H "authorization: bearer ' . $jwt . '" \
-H "content-type:application/json" \
--http2 \
https://api.push.apple.com/3/device/' . $token . ' 2>&1'; // ending w/ 2>&1, sends output to $output
exec($cmd, $output, $return);
if ($return != 0) {
// be notified of error
}
else {
foreach($output as $line) {
if (strpos($line, 'apns-id')) {
$apns = $line;
}
}
}
}
?>
Apple 最近向 APNS 添加了一种新的身份验证方法 (
下载的密钥是一个 .p8
包含私钥的文件:
$ cat APNSAuthKey_3HHEB343FX.p8
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBH...Already.Revoked...lHEjCX1v51W
-----END PRIVATE KEY-----
我正在使用旧方法使用 APNs 消息 - 将它们添加到钥匙串,请求证书并使用 OpenSSL 将消息发送到 gateway.production.push.apple.com:2195
。
如何使用新格式使用标准 CLI Linux 工具(OpenSSL、Python 等)发送推送通知?
您可以使用 Apple 推送通知身份验证密钥(沙盒和生产)通过 NODE JS 发送推送通知。 this link
中有 Elad Nava 提供的教程本教程包含创建 Apple 推送通知身份验证密钥和设置本地服务器到 运行 Node JS 代码以发送推送通知的所有步骤。您可以 运行 本地计算机中的代码并测试推送通知。
希望这会有所帮助。
如果您的计算机上安装了支持 HTTP/2 的 curl 和支持 ECDSA 的 openssl,您可以使用以下脚本通过 APNs 授权密钥测试推送通知:
#!/bin/bash
deviceToken=b27371497b85611baf9052b4ccfb9641ab7fea1d01c91732149c99cc3ed9342f
authKey="./APNSAuthKey_ABC1234DEF.p8"
authKeyId=ABC1234DEF
teamId=TEAM123456
bundleId=com.example.myapp
endpoint=https://api.development.push.apple.com
read -r -d '' payload <<-'EOF'
{
"aps": {
"badge": 2,
"category": "mycategory",
"alert": {
"title": "my title",
"subtitle": "my subtitle",
"body": "my body text message"
}
},
"custom": {
"mykey": "myvalue"
}
}
EOF
# --------------------------------------------------------------------------
base64() {
openssl base64 -e -A | tr -- '+/' '-_' | tr -d =
}
sign() {
printf "" | openssl dgst -binary -sha256 -sign "$authKey" | base64
}
time=$(date +%s)
header=$(printf '{ "alg": "ES256", "kid": "%s" }' "$authKeyId" | base64)
claims=$(printf '{ "iss": "%s", "iat": %d }' "$teamId" "$time" | base64)
jwt="$header.$claims.$(sign $header.$claims)"
curl --verbose \
--header "content-type: application/json" \
--header "authorization: bearer $jwt" \
--header "apns-topic: $bundleId" \
--data "$payload" \
$endpoint/3/device/$deviceToken
注意: 我使用此脚本的一个细微变化在 macOS 上使用 homebrew 版本的 curl 和 openssl 进行测试:http://thrysoee.dk/apns/
Apple 现在有 this 方法 Send a Push Notification Using a Token.
的文档瞧 PHP curl 和 HTTP/2 的样子。此脚本 return 200 ok 状态代码以及生成的令牌 ID。
// THE FINAL SCRIPT WITHOUT DEPENDENCIES!!! ...except curl with http2
$device_token = "a0abd886etc...";
//echo $key;
$kid = "YOURKEYID";
$teamId = "YOURTEAMID";
$app_bundle_id = "your.app.bundle";
$base_url = "https://api.development.push.apple.com";
$header = ["alg" => "ES256", "kid" => $kid];
$header = base64_encode(json_encode($header));
$claim = ["iss" => $teamId, "iat" => time()];
$claim = base64_encode(json_encode($claim));
$token = $header.".".$claim;
// key in same folder as the script
$filename = "KeyFromApple.p8";
$pkey = openssl_pkey_get_private("file://{$filename}");
$signature;
openssl_sign($token, $signature, $pkey, 'sha256');
$sign = base64_encode($signature);
$jws = $token.".".$sign;
$message = '{"aps":{"alert":"You are welcome.","sound":"default"}}';
function sendHTTP2Push($curl, $base_url, $app_bundle_id, $message, $device_token, $jws) {
$url = "{$base_url}/3/device/{$device_token}";
// headers
$headers = array(
"apns-topic: {$app_bundle_id}",
'Authorization: bearer ' . $jws
);
// other curl options
curl_setopt_array($curl, array(
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
CURLOPT_URL => $url,
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $message,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_HEADER => 1
));
// go...
$result = curl_exec($curl);
if ($result === FALSE) {
throw new Exception("Curl failed: " . curl_error($curl));
}
print_r($result."\n");
// get response
$status = curl_getinfo($curl);
return $status;
}
// open connection
$curl = curl_init();
sendHTTP2Push($curl, $base_url, $app_bundle_id, $message, $device_token, $jws);
尼古拉斯·曼齐尼 (Nicolas Manzini) 提供的答案虽然有帮助,但对我来说并不完全适用。我还想使用命令行 curl。特别是,读取 .p8 文件会导致一些问题。修改版本:
<?php
$teamid = 'YOURTEAMID';
$keyid = 'A464FN6T93'; // in the name of the file downloaded from Apple Certs
// since it is a universal key, I wanted this in a location accessible to several accounts
$keyFile = '/path/to/file/AuthKey_A464FN6T93.p8';
$privateKey = openssl_pkey_get_private('file://' . $keyFile);
if (! $privateKey) {
die('could not find: ' . $keyFile);
}
$header = ['alg' => 'ES256', 'kid' => $keyid];
$header = base64_encode(json_encode($header));
$claim = ['iss' => $teamid, 'iat' => time()];
$claim = base64_encode(json_encode($claim));
$tok = $header . '.' . $claim;
// pass in empty $signature, 2nd line below fills it
$signature = '';
$result = openssl_sign($tok, $signature, $privateKey, OPENSSL_ALGO_SHA256); // 'sha256'
if (! $result) {
die('unable to create signature');
}
$sign = base64_encode($signature);
openssl_free_key($privateKey);
$jwt = $tok . '.' . $sign;
foreach($tokens as $token) {
$cmd = '\
/usr/local/bin/curl -v \
-d \'{"aps":{"alert":{"title":"Notification Title","body":"You are being notified!"},"sound":"default"}}\' \
-H "apns-topic: com.app.bundle.id" \
-H "authorization: bearer ' . $jwt . '" \
-H "content-type:application/json" \
--http2 \
https://api.push.apple.com/3/device/' . $token . ' 2>&1'; // ending w/ 2>&1, sends output to $output
exec($cmd, $output, $return);
if ($return != 0) {
// be notified of error
}
else {
foreach($output as $line) {
if (strpos($line, 'apns-id')) {
$apns = $line;
}
}
}
}
?>