LoginSignup
0
0

More than 5 years have passed since last update.

Go + RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push

Posted at

Go + RFC 8219 Message Encryption for Web Push の続き。

RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push 理解のため読みつつ Go での Application Server 側の実装をなぞる。

実装コード

ここまでの実装により PushService への HTTP リクエストが送信できるようになる。

Application Server Self-Identification

openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
openssl ec -in vapid_private.pem -text -noout -conv_form uncompressed | grep '^priv:' -A 3 | tail -n 3 | tr -d ' \n:' | xxd -r -p | base64
openssl ec -in vapid_private.pem -text -noout -conv_form uncompressed | grep '^pub:' -A 5 | tail -n 5 | tr -d ' \n:' | xxd -r -p | base64

VAPID のため ApplicationServer のキーペアが必要となる。
公開鍵の方は ​PushManager.subscribeapplicationServerKey でも同じものを渡す。

func newJwt(endpoint, subject string, expiry int64, priv *ecdsa.PrivateKey) (string, error) {
    origin, err := url.Parse(endpoint)
    if err != nil {
        return "", err
    }
    audience := fmt.Sprintf("%s://%s", origin.Scheme, origin.Host)

    token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{
        "aud": audience,
        "exp": expiry,
        "sub": subject,
    })

    return token.SignedString(priv)
}

リクエストに含める JWT には audexp を最低限指定する。
Application Server Contact Information として sub を含めても良い。

Authorization: vapid
      t=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3
        B1c2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1ha
        Wx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_H
        LGkMlMuCGSK2rpiUfnK9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA,
      k=BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dR
        uU_RCPCfA5aq9ojSwk5Y2EmClBPs

これらの情報から上記のような Authorization ヘッダーを生成する。

func addAuthorizationHeader(req *http.Request, endpoint, subject string, expiry int64, pair *ApplicationServerKeyPair) error {
    t, err := newJwt(endpoint, subject, expiry, pair.priv)
    if err != nil {
        return err
    }

    k := pair.pub

    req.Header.Add("Authorization", fmt.Sprintf("vapid t=%s,k=%s", t, k))

    return nil
}

Token Parameter ("t") は生成した JWT を指定する。
Public Key Parameter ("k") は base64url エンコーディングされた公開鍵を指定する。

(Go) bytes to ecdsa.PrivateKey

func (pair *ApplicationServerKeyPair) SetBase64StdEncodingPrivateKey(key string) error {
    b, err := base64.StdEncoding.DecodeString(key)
    if err != nil {
        return err
    }

    d := big.Int{}

    pair.priv = &ecdsa.PrivateKey{
        PublicKey: ecdsa.PublicKey{Curve: curve},
        D:         d.SetBytes(b),
    }

    return nil
}

ecdsa.PrivateKey を既存のものから生成する方法がいまいち分からなかったが、なんとか動いた。
あるいは ParseECPrivateKeyFromPEM からファイルを pem ファイルからの読み込みこむことも可能そうだった。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0