概要
rubyにはwebpush用のgemが作られているので、webpushを使いたい人はそちらを使うことをお勧めします。
https://github.com/zaru/webpush
以下はあくまで趣味として作った内容を記載しています。
Webpushを行う際の、PushManager.subscribeに渡すapplicationServerKey
。これを自分で作ってみたいなと思いました。
ここで生成してくれますが、やっぱどう作られてるかって気になりますし。
applicationServerKeyに渡す値は?
https://developer.mozilla.org/ja/docs/Web/API/PushManager/subscribe
プッシュサーバーがアプリケーションサーバーを認証するために使用する 楕円曲線 DSA P-256 公開鍵を含む、Base64 でエンコードされた DOMString または ArrayBuffer。指定した場合は、アプリケーションサーバーから発するすべてのメッセージで VAPID 認証スキームを使用しなければならず、また対応する秘密鍵で署名した JWT を含めなければなりません。この鍵は、データを暗号化するために使用する ECDH 鍵と同じではありません。詳しくは "Using VAPID with WebPush" をご覧ください。
Base64 でエンコードされた DOMString
とはURL SafeなBase64のことみたいです(それでうまくいったので)。
作成手順
まず楕円曲線暗号DSA P-256の鍵とのことなので、鍵を生成します。P-256とprime256v1は同じものの様です[参考]。
ec = OpenSSL::PKey::EC.new('prime256v1')
ec.generate_key
公開鍵は以下のように取り出せます。
public_key = ec.public_key
ここから先が困ったのですが、rubyのBase64.urlsafe_encode64
に渡すためにbinaryにしなければなりません(なんとなくそのまんま渡したら怒られました)。
なんとなくpuckしたら行けるかなと思ったのですが、この公開鍵がとても大きな整数でした。
ec.public_key.to_bn.to_i
=> 60261393456883804897223332611535112895220851064492348957771298788553930859852980720330767505752023124297821596288685360429179926033825531665063484302731885
64bit符号なし整数とかでも全然足りなません。しょうがないので2進数表現経由で.pack('B*')
で変換しました(もっといい方法がきっとある...)。
bitstring = ec.public_key.to_bn.to_i.to_s(2)
# => "10001111110100101111000111000110101001001110001100110100100110010101111001010010001000010001110110110110110001100111101001101100110110101001101111001110100010000011101000111100110000011001011001001101000001011000010011111100101111111101010110100111010011001000100000011101111000010000110000101111110110100001000001001101111101001110000001001101111101111001100101100100010001100100000010101010101001110011110110010110000101111000110001011001110111101100101101111000001000101011000001101110101111111101010101001101101"
# .pack('B*')は1byte毎に処理するので足りない分0を先頭に足す
bitstring = ('0' * (8 - (bitstring.length % 8))) + bitstring
# => "0000010001111110100101111000111000110101001001110001100110100100110010101111001010010001000010001110110110110110001100111101001101100110110101001101111001110100010000011101000111100110000011001011001001101000001011000010011111100101111111101010110100111010011001000100000011101111000010000110000101111110110100001000001001101111101001110000001001101111101111001100101100100010001100100000010101010101001110011110110010110000101111000110001011001110111101100101101111000001000101011000001101110101111111101010101001101101"
binary = [bitstring].pack('B*')
# => "\x04~\x97\x8E5'\x19\xA4\xCA\xF2\x91\b\xED\xB63\xD3f\xD4\xDEtA\xD1\xE6\f\xB2h,'\xE5\xFE\xAD:d@\xEF\ba~\xD0\x82o\xA7\x02o\xBC\xCB\"2\x05U9\xEC\xB0\xBCb\xCE\xF6[\xC1\x15\x83u\xFE\xAAm"
Base64.urlsafe_encode64(binary, padding: false)
# => "BH6XjjUnGaTK8pEI7bYz02bU3nRB0eYMsmgsJ-X-rTpkQO8IYX7Qgm-nAm-8yyIyBVU57LC8Ys72W8EVg3X-qm0"
上記で作成した文字列をapplicationServerKey
に渡すと無事エンドポイント情報などが帰ってきたので、たぶん成功だと思います。実際にpush通知を送るところまではまだ確認していません。