Initial commit

This commit is contained in:
2024-08-12 04:10:48 +00:00
commit a55ef32347
22 changed files with 2410 additions and 0 deletions

182
iap_notify_v2.go Normal file
View File

@ -0,0 +1,182 @@
package main
/*
* 来源于: https://github.com/izniburak/appstore-notifications-go
* 源码中 parseJwtSignedPayload 对出错直接用了 panic不能直接引用
* 修改代码以更健壮。
*
*
*/
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/golang-jwt/jwt"
)
func IAP_Notify_New(payload string, appleRootCert string) (*AppStoreServerNotification, error) {
asn := &AppStoreServerNotification{}
asn.IsValid = false
asn.IsTest = false
asn.appleRootCert = appleRootCert
if err := asn.parseJwtSignedPayload(payload); err != nil {
return nil, err
}
return asn, nil
}
func (asn *AppStoreServerNotification) extractHeaderByIndex(payload string, index int) ([]byte, error) {
// get header from token
payloadArr := strings.Split(payload, ".")
// convert header to byte
headerByte, err := base64.RawStdEncoding.DecodeString(payloadArr[0])
if err != nil {
return nil, err
}
// bind byte to header structure
var header NotificationHeader
err = json.Unmarshal(headerByte, &header)
if err != nil {
return nil, err
}
// decode x.509 certificate headers to byte
certByte, err := base64.StdEncoding.DecodeString(header.X5c[index])
if err != nil {
return nil, err
}
return certByte, nil
}
func (asn *AppStoreServerNotification) verifyCertificate(certByte []byte, intermediateCert []byte) error {
// create certificate pool
roots := x509.NewCertPool()
// parse and append apple root certificate to the pool
ok := roots.AppendCertsFromPEM([]byte(asn.appleRootCert))
if !ok {
return errors.New("root certificate couldn't be parsed")
}
// parse and append intermediate x5c certificate
interCert, err := x509.ParseCertificate(intermediateCert)
if err != nil {
return errors.New("intermediate certificate couldn't be parsed")
}
intermediate := x509.NewCertPool()
intermediate.AddCert(interCert)
// parse x5c certificate
cert, err := x509.ParseCertificate(certByte)
if err != nil {
return err
}
// verify X5c certificate using app store certificate resides in opts
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: intermediate,
}
if _, err := cert.Verify(opts); err != nil {
return err
}
return nil
}
func (asn *AppStoreServerNotification) extractPublicKeyFromPayload(payload string) (*ecdsa.PublicKey, error) {
// get certificate from X5c[0] header
certStr, err := asn.extractHeaderByIndex(payload, 0)
if err != nil {
return nil, err
}
// parse certificate
cert, err := x509.ParseCertificate(certStr)
if err != nil {
return nil, err
}
// get public key
switch pk := cert.PublicKey.(type) {
case *ecdsa.PublicKey:
return pk, nil
default:
return nil, errors.New("appstore public key must be of type ecdsa.PublicKey")
}
}
func (asn *AppStoreServerNotification) parseJwtSignedPayload(payload string) error {
// get root certificate from x5c header
rootCertStr, err := asn.extractHeaderByIndex(payload, 2)
if err != nil {
return errors.New("extractHeaderByIndex error, in rootCertStr")
}
// get intermediate certificate from x5c header
intermediateCertStr, err := asn.extractHeaderByIndex(payload, 1)
if err != nil {
return errors.New("extractHeaderByIndex error, in intermediateCertStr")
}
// verify certificates
if err = asn.verifyCertificate(rootCertStr, intermediateCertStr); err != nil {
fmt.Printf("verifyCertificate eror: %v\n", err)
return errors.New("verifyCertificate eror")
}
// payload data
notificationPayload := &NotificationPayload{}
_, err = jwt.ParseWithClaims(payload, notificationPayload, func(token *jwt.Token) (interface{}, error) {
return asn.extractPublicKeyFromPayload(payload)
})
if err != nil {
return errors.New("ParseWithClaims NotificationPayload error")
}
asn.Payload = notificationPayload
asn.IsTest = asn.Payload.NotificationType == "TEST"
if asn.IsTest {
asn.IsValid = true
return nil
}
// transaction info
transactionInfo := &TransactionInfo{}
payload = asn.Payload.Data.SignedTransactionInfo
_, err = jwt.ParseWithClaims(payload, transactionInfo, func(token *jwt.Token) (interface{}, error) {
return asn.extractPublicKeyFromPayload(payload)
})
if err != nil {
return errors.New("ParseWithClaims SignedTransactionInfo error")
}
asn.TransactionInfo = transactionInfo
// renewal info
renewalInfo := &RenewalInfo{}
payload = asn.Payload.Data.SignedRenewalInfo
_, err = jwt.ParseWithClaims(payload, renewalInfo, func(token *jwt.Token) (interface{}, error) {
return asn.extractPublicKeyFromPayload(payload)
})
if err != nil {
return errors.New("ParseWithClaims SignedRenewalInfo error")
}
asn.RenewalInfo = renewalInfo
// valid request
asn.IsValid = true
return nil
}