Skip to content

Commit

Permalink
bugfix:修复苹果交易签名信息未验证bug (#376)
Browse files Browse the repository at this point in the history
* bugfix:
1.修复苹果交易签名信息未验证bug

Co-authored-by: xjy <[email protected]>
  • Loading branch information
cdfr1 and xjy authored Jan 3, 2024
1 parent 9f51577 commit 1dc6c7c
Showing 1 changed file with 71 additions and 58 deletions.
129 changes: 71 additions & 58 deletions apple/unsign_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,93 +21,106 @@ func ExtractClaims(signedPayload string, tran jwt.Claims) (err error) {
if valueOf.Kind() != reflect.Ptr {
return errors.New("tran must be ptr struct")
}
tokenStr := signedPayload
rootCertStr, err := extractHeaderByIndex(tokenStr, 2)

_, err = jwt.ParseWithClaims(signedPayload, tran, func(token *jwt.Token) (any, error) {
return x5cCertVerify(signedPayload)
})
if err != nil {
return err
}
intermediaCertStr, err := extractHeaderByIndex(tokenStr, 1)
return nil
}

type header struct {
Alg string `json:"alg"`
X5c []string `json:"x5c"`
}

// 解析 x5c root intermedia user证书
// Per doc: https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6
func (h header) certParse() (*x509.Certificate, *x509.Certificate, *x509.Certificate, error) {

var certDecode = func(certStr string) (*x509.Certificate, error) {
certBytes, err := base64.StdEncoding.DecodeString(certStr)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}

return cert, err
}

if len(h.X5c) != 3 {
return nil, nil, nil, errors.New("invalid x5c format")
}

root, err := certDecode(h.X5c[2])
if err != nil {
return err
return nil, nil, nil, fmt.Errorf("failed to parse root certificate: %w", err)
}
if err = verifyCert(rootCertStr, intermediaCertStr); err != nil {
return err

interCert, err := certDecode(h.X5c[1])
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to parse intermedia certificate: %w", err)
}
_, err = jwt.ParseWithClaims(tokenStr, tran, func(token *jwt.Token) (any, error) {
return extractPublicKeyFromToken(tokenStr)
})

userCert, err := certDecode(h.X5c[0])
if err != nil {
return err
return nil, nil, nil, fmt.Errorf("failed to parse user certificate: %w", err)
}
return nil

return root, interCert, userCert, nil
}

// Per doc: https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6
func extractPublicKeyFromToken(tokenStr string) (*ecdsa.PublicKey, error) {
certStr, err := extractHeaderByIndex(tokenStr, 0)
func (h header) x5cCertVerify() (*ecdsa.PublicKey, error) {
_, i, u, err := h.certParse()
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(certStr)
var iPool = x509.NewCertPool()
iPool.AddCert(i)
opts := x509.VerifyOptions{
Roots: rootCertPool,
Intermediates: iPool,
}
_, err = u.Verify(opts)
if err != nil {
return nil, err
}
switch pk := cert.PublicKey.(type) {
switch pk := u.PublicKey.(type) {
case *ecdsa.PublicKey:
return pk, nil
default:
return nil, errors.New("appstore public key must be of type ecdsa.PublicKey")
}
}

func extractHeaderByIndex(tokenStr string, index int) ([]byte, error) {
if index > 2 {
return nil, errors.New("invalid index")
}
// 苹果根ca证书
var rootCertPool *x509.CertPool

func init() {
rootCertPool = x509.NewCertPool()
rootCertPool.AppendCertsFromPEM([]byte(rootPEM))
}

// 验证x5c证书链是否合法
func x5cCertVerify(tokenStr string) (*ecdsa.PublicKey, error) {
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"`
if len(tokenArr) != 3 {
return nil, errors.New("invalid jwt format")
}
header := &Header{}
err = json.Unmarshal(headerByte, header)
if err != nil {
return nil, err
}
if len(header.X5c) < index {
return nil, fmt.Errorf("index[%d] > header.x5c slice len(%d)", index, len(header.X5c))
}
certByte, err := base64.StdEncoding.DecodeString(header.X5c[index])
headerByte, err := base64.RawStdEncoding.DecodeString(tokenArr[0])
if err != nil {
return nil, err
}
return certByte, nil
}

func verifyCert(certByte, intermediaCertStr []byte) error {
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
return errors.New("failed to parse root certificate")
}
interCert, err := x509.ParseCertificate(intermediaCertStr)
h := &header{}
err = json.Unmarshal(headerByte, h)
if err != nil {
return errors.New("failed to parse intermedia certificate")
}
intermedia := x509.NewCertPool()
intermedia.AddCert(interCert)
cert, err := x509.ParseCertificate(certByte)
if err != nil {
return err
}
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: intermedia,
return nil, fmt.Errorf("invalid jwt.header: %w", err)
}
_, err = cert.Verify(opts)
return err
return h.x5cCertVerify()
}

0 comments on commit 1dc6c7c

Please sign in to comment.