以前寫過幾篇 APNs 筆記,但好像都沒很完整,結果每年要更新憑證時,都要再找資料複習一次 Orz 所以就把它筆記下來吧!
- iOS 開發筆記 - 使用 Apple Push Notification service (APNs)
- iOS 開發筆記 - 驗證 Apple Push Notification PEM File 以及 Remove PEM Password
如果,當初的 Certificate Signing Request (CSR) 以及 private key(.p12) 還留著的話,可以繼續拿來用,若沒有的話,就重新建立。整個就像第一次建立一樣 :P
Step 1:
iOS Developer Website -> Certificate -> All -> [+] -> Production -> Apple Push Notification service SSL (Sandbox & Production) -> App ID: -> Your App ID -> About Creating a Certificate Signing Request (CSR) -> Upload CRS file
若之前的 CSR 跟 p12 都還在,就可以省去製作 CSR,直接拿舊的上傳;若要新建立就從 Keychain -> 憑證輔助程式 -> 從憑證授權要求憑證 -> 輸入 email -> 預設將產生 CertificateSigningRequest.certSigningRequest 檔案,把他拿去上傳吧。另外,記得要把下載到的 aps.cer 點擊匯入至 keychain 中。
Step 2:
在 keychain -> 類別 -> 我的憑證 -> 可以看到 Apple Push Service 資訊,展開後,點擊專用密鑰,在點擊上方的憑證後,可以以憑證為單位輸出 p12 資料,這是跟 APN server 溝通的通行證。
Step 3:
使用 openssl 將通行證轉檔,從 p12 成 pem 檔
$ openssl pkcs12 -in App.p12 -out App.pem -nodes
轉換後,可以用文字編輯器打開 App.pem 檔案,會看到兩段:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
而 CERTIFICATE 上頭也會標記是哪個 App ID 的,未來忘記都可以翻來看
Step 4:
做簡單的 pem 驗證即可。
Production:
$ openssl s_client -connect gateway.push.apple.com:2195 -cert App.pem
Sandbox:
$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert App.pem
若 pem 是正常的,應該連線完就停在那邊:
...
---
SSL handshake has read 3385 bytes and written 2515 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : AES256-SHA
Session-ID:
Session-ID-ctx:
Master-Key: #######
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1476026971
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
^C
Step 5:
可以拿 pem 跟 APNs 互動了!以下是片段 PHP 程式碼,有興趣可再試試 changyy/codeigniter-library-notification 包好的 library。
<?php
$ssl_ctx = stream_context_create();
stream_context_set_option($ssl_ctx, 'ssl', 'local_cert', $pem_file_path);
if( is_resource( $fp = stream_socket_client(
$use_sansbox ? 'ssl://gateway.sandbox.push.apple.com:2195' : 'ssl://gateway.push.apple.com:2195',
$err,
$errstr,
60,
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,
$ssl_ctx
) ) ) {
//$payload = array(
// 'aps' => array(
// 'alert' => array(
// 'title' => $title,
// 'body' => $message,
// )
// )
//);
$packed_data =
// Command (Simple Notification Format)
chr(0)
// device token
. pack('n', 32) . pack('H*', $notification_token)
// payload
//. pack('n', strlen($raw_cmd)) . $raw_cmd;
. $raw_data;
if( @fwrite($fp, $packed_data, strlen($packed_data)) !== false ) {
echo "SUCCESS";
} else {
echo "FAILURE";
}
fclose($fp);
}