forked from SherClockHolmes/webpush-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvapid.go
105 lines (86 loc) · 2.27 KB
/
vapid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package webpush
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"
"net/http"
"net/url"
"time"
jwt "github.com/dgrijalva/jwt-go"
)
// GenerateVAPIDKeys will create a private and public VAPID key pair
func GenerateVAPIDKeys() (privateKey, publicKey string, err error) {
// Get the private key from the P256 curve
curve := elliptic.P256()
private, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
if err != nil {
return
}
public := elliptic.Marshal(curve, x, y)
// Convert to base64
publicKey = base64.RawURLEncoding.EncodeToString(public)
privateKey = base64.RawURLEncoding.EncodeToString(private)
return
}
// Generates the ECDSA public and private keys for the JWT encryption
func generateVAPIDHeaderKeys(privateKey []byte) (*ecdsa.PublicKey, *ecdsa.PrivateKey) {
// Public key
curve := elliptic.P256()
px, py := curve.ScalarMult(
curve.Params().Gx,
curve.Params().Gy,
privateKey,
)
pubKey := ecdsa.PublicKey{
Curve: curve,
X: px,
Y: py,
}
// Private key
d := &big.Int{}
d.SetBytes(privateKey)
return &pubKey, &ecdsa.PrivateKey{
PublicKey: pubKey,
D: d,
}
}
// Sign the http.Request with the required VAPID headers
func vapid(req *http.Request, s *Subscription, options *Options) error {
// Create the JWT token
subURL, err := url.Parse(s.Endpoint)
if err != nil {
return err
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{
"aud": fmt.Sprintf("%s://%s", subURL.Scheme, subURL.Host),
"exp": time.Now().Add(time.Hour * 12).Unix(),
"sub": fmt.Sprintf("mailto:%s", options.Subscriber),
})
// ECDSA
b64 := base64.RawURLEncoding
decodedVapidPrivateKey, err := b64.DecodeString(options.VAPIDPrivateKey)
if err != nil {
return err
}
pubKey, privKey := generateVAPIDHeaderKeys(decodedVapidPrivateKey)
// Sign token with key
tokenString, err := token.SignedString(privKey)
if err != nil {
return err
}
// Set VAPID headers
req.Header.Set("Authorization", fmt.Sprintf("WebPush %s", tokenString))
vapidPublicKeyHeader := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)
req.Header.Set(
"Crypto-key",
fmt.Sprintf(
"%s;p256ecdsa=%s",
req.Header.Get("Crypto-Key"),
base64.RawURLEncoding.EncodeToString(vapidPublicKeyHeader),
),
)
return nil
}