以下是解析App Store服务器通知V2的步骤:
- 从JWS令牌中提取头部
- 使用应用商店密钥验证头部
- 从令牌中提取公钥以解析有效载荷数据
- 准备结构以绑定通知
- 解析有效载荷并将其绑定到结构中
还有使用Golang的示例代码
type Cert struct{}
func (c *Cert) extractCertByIndex(tokenStr string, index int) ([]byte, error) {
if index > 2 {
return nil, errors.New("invalid index")
}
tokenArr := strings.Split(tokenStr, ".")
headerByte, err := base64.RawStdEncoding.DecodeString(tokenArr[0])
if err != nil {
return nil, err
}
type Header struct {
Alg string `json:"alg"`
X5c []string `json:"x5c"`
}
var header Header
err = json.Unmarshal(headerByte, &header)
if err != nil {
return nil, err
}
certByte, err := base64.StdEncoding.DecodeString(header.X5c[index])
if err != nil {
return nil, err
}
return certByte, nil
}
func (c *Cert) verifyCert(rootCert, intermediaCert, leafCert *x509.Certificate) error {
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
return errors.New("failed to parse root certificate")
}
intermedia := x509.NewCertPool()
intermedia.AddCert(intermediaCert)
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: intermedia,
}
_, err := rootCert.Verify(opts)
if err != nil {
return err
}
_, err = leafCert.Verify(opts)
if err != nil {
return err
}
return nil
}
func (c *Cert) ExtractPublicKeyFromToken(token string) (*ecdsa.PublicKey, error) {
rootCertBytes, err := c.extractCertByIndex(token, 2)
if err != nil {
return nil, err
}
rootCert, err := x509.ParseCertificate(rootCertBytes)
if err != nil {
return nil, fmt.Errorf("appstore failed to parse root certificate")
}
intermediaCertBytes, err := c.extractCertByIndex(token, 1)
if err != nil {
return nil, err
}
intermediaCert, err := x509.ParseCertificate(intermediaCertBytes)
if err != nil {
return nil, fmt.Errorf("appstore failed to parse intermediate certificate")
}
leafCertBytes, err := c.extractCertByIndex(token, 0)
if err != nil {
return nil, err
}
leafCert, err := x509.ParseCertificate(leafCertBytes)
if err != nil {
return nil, fmt.Errorf("appstore failed to parse leaf certificate")
}
if err = c.verifyCert(rootCert, intermediaCert, leafCert); err != nil {
return nil, err
}
switch pk := leafCert.PublicKey.(type) {
case *ecdsa.PublicKey:
return pk, nil
default:
return nil, errors.New("appstore public key must be of type ecdsa.PublicKey")
}
}
用法示例
payload := &NotificationPayload{}
cert := Cert{}
_, err = jwt.ParseWithClaims(tokenStr, payload, func(token *jwt.Token) (interface{}, error) {
return cert.ExtractPublicKeyFromToken(tokenStr)
})
更多详情请参考https://github.com/richzw/appstore