Progressive Web App 推播协定 (26)

之前已经可以用後端的套件去实作推播的服务器,但那个套件实际上做了哪些事情?

  • 金钥对 Application server keys
  • 传输内容加密 Payload Encryption
  • Header 参数配置

流程如下图,相关定义可参考连结:
https://datatracker.ietf.org/doc/html/draft-ietf-webpush-encryption

    +-------+           +--------------+       +-------------+
    |  UA   |           | Push Service |       | Application |
    +-------+           +--------------+       +-------------+
        |                      |                      |
        |        Setup         |                      |
        |<====================>|                      |
        |           Provide Subscription              |
        |-------------------------------------------->|
        |                      |                      |
        :                      :                      :
        |                      |     Push Message     |
        |    Push Message      |<---------------------|
        |<---------------------|                      |
        |                      |                      |

也因为定义了标准协议的关系,所以有了各语言的实作版本:

金钥对 Application server keys

  • 密钥: 推播服务使用
  • 公钥: 用户端使用

密钥会用来检查订阅的用户身分公钥是否符合,pushManager.subscribe() 会用公钥来检查接收到的签章讯息是否由与公钥相关的私钥签出来的。

签章讯息的传递会透过 JWT 放在 header 进行资料交换,一个 JWT 会由三段字串组成并由 . 号分隔 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCJ9.zTWqeQdfDM0WKGBFig2-VmUpTLkIQ4DvAJN6_LzDZzU,前两个字串(JWT info 和 JWT data)是经过 base64 编码的 JSON ,所以其实是公开可阅读的。

想要解密 JWT,可以直接用官方网站提供的介面。
https://jwt.io/

  1. JWT 讯息: 会记录用哪种算法签章
{
  "//": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
  "alg": "HS256",
  "typ": "JWT"
}
  1. JWT 资料: 看程序设计上想要带什麽资料会放在这边,推播的话会有三个栏位 aud、exp、sub,透过这三个参数和 vapid 产生工具就能够产生金钥对
  • aud: 推播服务来源
  • exp: JWT 到期时间
  • sub: 推播讯息的人的联络讯息

https://github.com/web-push-libs/vapid

{
  "//": "`eyJuYW1lIjoidGVzdCJ9",
  "aud": "https://YourSiteHere.example",
  "sub": "mailto://[email protected]",
  "exp": 1457718878
}

最後的 header 就会像是 Authorization: 'WebPush <JWT Info>.<JWT Data>.<Signature>'

传输内容加密

推播的讯息不能赤裸裸的传递,所以在加密上也有定义相关规范,我也看不是很懂,总体来说就是套了加密、加盐、金钥对搭配使用,执行加密的时候最後也加上填充,避免被用长度推断。

  • ECDH (elliptic-curve Diffie-Hellman): 透过运算去发现原来我们是天生一对,数学假设如下
    • y^2 = x^3 + ax + b
    • 4a^3 + 27b^2 != 0

看不懂也没关系可以看影片,但我相信看完影片可能还是只懂概念。
https://youtu.be/F3zzNa42-tQ

const keyCurve = crypto.createECDH("prime256v1");
keyCurve.generateKeys();

const publicKey = keyCurve.getPublicKey();
const privateKey = keyCurve.getPrivateKey();
  • HKDF (Hashed Message Authentication Code): SHA-256
    • Salt: 16 byte authentication secret
    • IKM: shared secret
    • info
    • length
  • Nonce: 加密通信只能使用一次的数字,可能是一个随机或伪随机数,以避免重送攻击
function hkdf(salt, ikm, info, length) {
  // ikm 加盐
  const keyHmac = crypto.createHmac("sha256", salt);
  keyHmac.update(ikm);
  const key = keyHmac.digest();

  // info 加密
  const infoHmac = crypto.createHmac("sha256", key);
  infoHmac.update(info);

  const ONE_BUFFER = new Buffer(1).fill(1);
  infoHmac.update(ONE_BUFFER);

  // 长度控制
  return infoHmac.digest().slice(0, length);
}

const nonce = hkdf(salt, prk, nonceInfo, 12);
const contentEncryptionKey = hkdf(salt, prk, cekInfo, 16);
  • 使用者装置:

    • ecdh_secret = ECDH(ua_private, as_public)
    • auth_secret = random(16)
    • salt = header 来的
  • 服务器:

    • ecdh_secret = ECDH(as_private, ua_public)
    • auth_secret = user agent 来的
    • salt = random(16)

Header 参数配置

  • TTL header (time to live): 讯息能在推播服务上存活多久
  • Topic: 同个主题下可以实作旧讯息取代新讯息
  • Urgency: 讯息的重要程度

<<:  [Day 24] Android Studio 七日陨石开发:安装与创建第一个专案 (下)

>>:  Day-25 ImageView

Laravel Queue Job:深入理解 timeout 的运作

work 和 listen 的差别 让 queue work 开始执行任务的指令有两个:work 和...

DAY26 把这个Google maps 放在 APP 上(二)

看到标题有个(二)该不会又是很长的系列文了吧!? 并不会。 因为有点复杂。微懒。 抓取目前所在位子 ...

Day11_HTML语法8

标示超连结 < a>元素是用来标示超连结,常使用的属性为< href> &l...

Day 02创建LINE Messageing API频道

环境建置 LINE Provider 创建 在LINE Developers网站右上角登入LINE帐...

SQL与NoSQL的连结(一)

对於资料库管理员而言, 另一项重要任务是异质平台之间的资料沟通. 接下来实作从SQL到NSQL的资料...