2

我需要使用 .p8 文件通过 PHP 脚本发送推送通知,并在此处提出的类似问题中找到以下代码。

<?php

  $keyfile = 'AuthKey_AABBCC1234.p8';               # <- Your AuthKey file
  $keyid = 'AABBCC1234';                            # <- Your Key ID
  $teamid = 'AB12CD34EF';                           # <- Your Team ID (see Developer Portal)
  $bundleid = 'com.company.YourApp';                # <- Your Bundle ID
  $url = 'https://api.development.push.apple.com';  # <- development url, or use http://api.push.apple.com for production environment
  $token = 'e2c48ed32ef9b018........';              # <- Device Token

  $message = '{"aps":{"alert":"Hi there!","sound":"default"}}';

  $key = openssl_pkey_get_private('file://'.$keyfile);

  $header = ['alg'=>'ES256','kid'=>$keyid];
  $claims = ['iss'=>$teamid,'iat'=>time()];

  $header_encoded = base64($header);
  $claims_encoded = base64($claims);

  $signature = '';
  openssl_sign($header_encoded . '.' . $claims_encoded, $signature, $key, 'sha256');
  $jwt = $header_encoded . '.' . $claims_encoded . '.' . base64_encode($signature);

  // only needed for PHP prior to 5.5.24
  if (!defined('CURL_HTTP_VERSION_2_0')) {
      define('CURL_HTTP_VERSION_2_0', 3);
  }

  $http2ch = curl_init();
  curl_setopt_array($http2ch, array(
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
    CURLOPT_URL => "$url/3/device/$token",
    CURLOPT_PORT => 443,
    CURLOPT_HTTPHEADER => array(
      "apns-topic: {$bundleid}",
      "authorization: bearer $jwt"
    ),
    CURLOPT_POST => TRUE,
    CURLOPT_POSTFIELDS => $message,
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HEADER => 1
  ));

  $result = curl_exec($http2ch);
  if ($result === FALSE) {
    throw new Exception("Curl failed: ".curl_error($http2ch));
  }

  $status = curl_getinfo($http2ch, CURLINFO_HTTP_CODE);
  echo $status;

  function base64($data) {
    return rtrim(strtr(base64_encode(json_encode($data)), '+/', '-_'), '=');
  }

?>

但是,我发现它openssl_pkey_get_private不会读取密钥文件,并且在调试时会出现以下错误。

$key = openssl_pkey_get_private('file://'.$keyfile);
if ($key === false) {
    var_dump(openssl_error_string());
}

错误 :

'error:02001002:system library:fopen:No such file or directory'

请注意,启用curlas没有问题,我正在使用. 在测试阶段,我使用同一文件夹中的脚本和文件来避免任何路径问题。HTTP2curlPHP7

任何线索哪里出错了?

4

2 回答 2

1

此脚本可用于使用 .p8 证书向 IOS 发送推送。确保证书的位置正确


<?php

      $keyfile = 'AuthKey_AABBCC1234.p8';               // Your p8 Key file
      $keyid = 'AABBCC1234';                            // Your Key ID
      $teamid = 'AB12CD34EF';                           // Your Team ID (see Developer Portal)
      $bundleid = 'com.company.YourApp';                // Your Bundle ID
      $url = 'https://api.development.push.apple.com';  // development url, or use http://api.push.apple.com for production environment
      $token = 'e2c48ed32ef9b018........';              // Device Token

      $message = '{"aps":{"alert":"Hi there!","sound":"default"}}';

      $key = openssl_pkey_get_private('file://'.$keyfile);

      $header = ['alg'=>'ES256','kid'=>$keyid];
      $claims = ['iss'=>$teamid,'iat'=>time()];

      $header_encoded = base64($header);
      $claims_encoded = base64($claims);

      $signature = '';
      openssl_sign($header_encoded . '.' . $claims_encoded, $signature, $key, 'sha256');
      $jwt = $header_encoded . '.' . $claims_encoded . '.' . base64_encode($signature);

      // only needed for PHP prior to 5.5.24
      if (!defined('CURL_HTTP_VERSION_2_0')) {
          define('CURL_HTTP_VERSION_2_0', 3);
      }

      $http2ch = curl_init();
      curl_setopt_array($http2ch, array(
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
        CURLOPT_URL => "$url/3/device/$token",
        CURLOPT_PORT => 443,
        CURLOPT_HTTPHEADER => array(
          "apns-topic: {$bundleid}",
          "authorization: bearer $jwt"
        ),
        CURLOPT_POST => TRUE,
        CURLOPT_POSTFIELDS => $message,
        CURLOPT_RETURNTRANSFER => TRUE,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HEADER => 1
      ));

      $result = curl_exec($http2ch);
      if ($result === FALSE) {
        throw new Exception("Curl failed: ".curl_error($http2ch));
      }

      $status = curl_getinfo($http2ch, CURLINFO_HTTP_CODE);
      echo $status;

      function base64($data) {
        return rtrim(strtr(base64_encode(json_encode($data)), '+/', '-_'), '=');
      }

    ?>```
于 2019-08-28T12:13:47.033 回答
1

如果还没有阅读,请参考下一个 URL。

https://www.php.net/manual/en/function.openssl-pkey-get-private.php

要缩小您的问题范围,请为您的 php 文件和密钥文件使用相同的目录并尝试使用此工作代码。

工作代码

$keyfile="file://".__DIR__.DIRECTORY_SEPARATOR."key.p8"; //absolute path
$key = openssl_pkey_get_private($keyfile);

if ($key === false) {
    var_dump(openssl_error_string());
}else{
    var_dump($key);
}

以下可能是一个问题。

  1. 小路

以下路径样式应该可以工作。

$keyfile="file:///home/john/php/key.p8"; // unix absoulute path
$keyfile="file://C:\\users\\john\\php\\key.p8"; // windows absoulute path
$keyfile="file://".__DIR__.DIRECTORY_SEPARATOR."key.p8"; //absoulute path for unix, windows
$keyfile="file://key.p8"; // relative path, unix, windows, (php,key files in same directory)

$key = openssl_pkey_get_private($keyfile);

如果路径不存在,错误会像

“错误:02001002:系统库:fopen:没有这样的文件或目录”

  1. 网络环境

    检查您的 Web 根和 Web 用户对文件夹和密钥文件的访问权限。

    为了减少问题,请在 php 内置 Web 服务器环境而不是 WAMP 环境上对其进行测试。

>php -S localhost:80
  1. 损坏的密钥文件

保存为包含空格的特定类型。这可能会发生类似下一个错误。

“错误:0906D06C:PEM 例程:PEM_read_bio:没有起始行”

in my case, key file was saved as UTF-8 with BOM(whitespaces)

调试密钥文件 1 - 从变量中读取

这段代码应该可以工作。我从 http://micmap.org/php-by-example/en/function/openssl_pkey_get_private获得了密钥文件

请将 $str 替换为您的。

$str = <<<EOF
-----BEGIN RSA PRIVATE KEY----- 
MIIEogIBAAKCAQEA0llCeBjy18RylTdBih9GMUSZIC3GzeN0vQ9W8E3nwy2jdeUn 
H3GBXWpMo3F43V68zM2Qz5epRNmlLSkY/PJUfJIC8Yc1VEokT52q87hH/XJ5eS8h 
eZnjuSlPAGi8oZ3ImVbruzV7XmlD+QsCSxJW7tBv0dqJ71e1gAAisCXK2m7iyf/u 
l6rT0Zz0ptYH4IZfwc/hQ9JcMg69uM+3bb4oBFsixMmEQwxKZsXk3YmO/YRjRbay 
+6+79bSV/frW+lWhknyGSIJp2CJArYcOdbK1bXx1dRWpbNSExo7dWwuPC0Y7a5AE 
eoZofieQPPBhXlp1hPgLYGat71pDqBjKLvF5GwIDAQABAoIBACPItYsSy3UzYT7L 
OKYTrfBBuD8GKpTqBfkHvAWDa1MD15P92Mr7l0NaCxGfAy29qSa6LdFy/oPM9tGY 
9TxKyV6rxD5sfwEI3+Z/bw6pIe4W5F1eTDaQnHHqehsatkRUQET9yXp+na8w/zRF 
0C0PQKS95tfvcpm59RGCdGQ8+aZw+cIy/xez75W8IS/hagMxe7xYPjpkOkSCCEJU 
zmbVq6AyWodASV0p4H9p8I+c0vO2hJ/ELJ167w6T+2/GlZg979rlyHoTW8jK2BbG 
IRGaPo+c2GANXa686tdpbkPd6oJliXwBSNolxmXShvlveBbPFAJJACzCmbXNj9kH 
6/K+SWkCgYEA7FNudcTkRPV8TzKhJ1AzDjw3VcnraYhY8IlNxbk7RVHLdkoUtwk/ 
mImeBlEfCoz9V+S/gRgeQ+1Vb/BCbS24+bN/+IGoNRFMRcOieFt6lQUpj7a9NeSo 
IEclGgUiU7QR3xH73SB4GC3rgSPeHJhJZC5EJq5TzYjXTPGPpBD3zicCgYEA49wz 
zfMDYIH8h4L65r/eJYIbLwpvgktgaYvhijO3qfZSWW+Y19jCBn55f65YOhPGQBHA 
my0f+tVxFNZ/OupbrAIIzogxlCIYHNBawDhoHN/sB3/lSBAjifySNLyRlA62oA0w 
wXvXVLVWMa3aXim3c9AlnLF1fHwcvwpOKSfdye0CgYBb1mBKq+T5V1yjek1d9bCh 
i40FbZ5qOG43q2Ppvn3mBk9G/KroJlPsdy5NziB9/SRGj8JL7I92Xjihc4Cc5PPJ 
NZQ5gklXtg0p30i39PTCDGuGScFlvCIJyRwF7JDWblezlE2INSH2Y4HtgX7DJfr/ 
T2t0jLJMYS0p3YWwgFeMaQKBgHUIe/8y6zAdc5QynSX5tGL1gXrW1FFK39k2RICU 
cag1YTSYkhuDNJzbRxJifORPlcsAkzngooVWLb+zMCQVjUI6xUU3RKe+Hz5lccc6 
8ZarGHL9qMkrqOVNudamZ+tw5zIrtDgcoIvcm8nmbrtgl94/MaJar2ph4O3qoByZ 
Ylw9AoGAIdS79s0VKkj4VVXqK47ZcI7jGL4V4C8ujU8YcMNV88xwCoDg9ZIFprWA 
P5p/cnvj6aHnqL58XiH0+bE0Lt3J+U6N6JelQQevgBHooMFh4FpDXcVda7xB3rK3 
woqbi8fNhr827H2maxIZPtVG95/mvR4k5z1Jrdnr34ZUmtC6U5Q= 
-----END RSA PRIVATE KEY-----
EOF;

$key = openssl_pkey_get_private($str);
if ($key === false) {
    var_dump(openssl_error_string());
}else{
    var_dump($key);
}

输出

resource(4) 类型(OpenSSL 密钥)

调试密钥文件 2 - 从文件中读取

将您的密钥字符串($str)复制到“key.p8”之类的密钥文件中。

$str = <<<EOF
-----BEGIN RSA PRIVATE KEY----- 
...YOUR KEY STINGS HERE...
-----END RSA PRIVATE KEY-----
EOF;

$str2 = file_get_contents("key.p8");

$len1 = strlen ($str);
$len2 = strlen ($str2);
if($len1 !== $len2) echo "File has been corrupted.";

$key = openssl_pkey_get_private($str2);

if ($key === false) {
    var_dump(openssl_error_string());
}else{
    var_dump($key);
}

于 2019-08-28T02:14:00.870 回答