Skip to content

Commit

Permalink
Merge pull request #228 from richzw/master
Browse files Browse the repository at this point in the history
feat(appstore): add ParseJWSEncodeString to decode jws string
  • Loading branch information
takecy authored Jul 26, 2023
2 parents 0e0a8ea + a4be3e1 commit ce89958
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 14 deletions.
4 changes: 4 additions & 0 deletions appstore/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ type JWSRenewalInfoDecodedPayload struct {
SignedDate int64 `json:"signedDate"`
}

func (J JWSRenewalInfoDecodedPayload) Valid() error {
return nil
}

// JWSDecodedHeader https://developer.apple.com/documentation/appstoreserverapi/jwsdecodedheader
type JWSDecodedHeader struct {
Alg string `json:"alg,omitempty"`
Expand Down
59 changes: 45 additions & 14 deletions appstore/api/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -421,48 +422,78 @@ func (a *StoreClient) ParseSignedTransactions(transactions []string) ([]*JWSTran
return result, nil
}

// ParseSignedTransaction parse one jws singed transaction for API like GetTransactionInfo
func (a *StoreClient) ParseSignedTransaction(transaction string) (*JWSTransaction, error) {
tran := &JWSTransaction{}
// ParseJWSEncodeString parse the jws encode string, such as JWSTransaction and JWSRenewalInfoDecodedPayload
func (a *StoreClient) ParseJWSEncodeString(jwsEncode string) (interface{}, error) {
// Split the JWS format string into its three parts
parts := strings.Split(jwsEncode, ".")

rootCertBytes, err := a.cert.extractCertByIndex(transaction, 2)
// Decode the payload part of the JWS format string
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, err
}

// Determine which struct to use based on the payload contents
if strings.Contains(string(payload), "transactionId") {
transaction := &JWSTransaction{}
err = a.parseJWS(jwsEncode, transaction)
return transaction, err
} else if strings.Contains(string(payload), "renewalDate") {
renewalInfo := &JWSRenewalInfoDecodedPayload{}
err = a.parseJWS(jwsEncode, renewalInfo)
return renewalInfo, err
}

return nil, nil
}

func (a *StoreClient) parseJWS(jwsEncode string, claims jwt.Claims) error {
rootCertBytes, err := a.cert.extractCertByIndex(jwsEncode, 2)
if err != nil {
return err
}
rootCert, err := x509.ParseCertificate(rootCertBytes)
if err != nil {
return nil, fmt.Errorf("appstore failed to parse root certificate")
return fmt.Errorf("appstore failed to parse root certificate")
}

intermediaCertBytes, err := a.cert.extractCertByIndex(transaction, 1)
intermediaCertBytes, err := a.cert.extractCertByIndex(jwsEncode, 1)
if err != nil {
return nil, err
return err
}
intermediaCert, err := x509.ParseCertificate(intermediaCertBytes)
if err != nil {
return nil, fmt.Errorf("appstore failed to parse intermediate certificate")
return fmt.Errorf("appstore failed to parse intermediate certificate")
}

leafCertBytes, err := a.cert.extractCertByIndex(transaction, 0)
leafCertBytes, err := a.cert.extractCertByIndex(jwsEncode, 0)
if err != nil {
return nil, err
return err
}
leafCert, err := x509.ParseCertificate(leafCertBytes)
if err != nil {
return nil, fmt.Errorf("appstore failed to parse leaf certificate")
return fmt.Errorf("appstore failed to parse leaf certificate")
}
if err = a.cert.verifyCert(rootCert, intermediaCert, leafCert); err != nil {
return nil, err
return err
}

pk, ok := leafCert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("appstore public key must be of type ecdsa.PublicKey")
return fmt.Errorf("appstore public key must be of type ecdsa.PublicKey")
}

_, err = jwt.ParseWithClaims(transaction, tran, func(token *jwt.Token) (interface{}, error) {
_, err = jwt.ParseWithClaims(jwsEncode, claims, func(token *jwt.Token) (interface{}, error) {
return pk, nil
})
return err
}

// ParseSignedTransaction parse one jws singed transaction for API like GetTransactionInfo
func (a *StoreClient) ParseSignedTransaction(transaction string) (*JWSTransaction, error) {
tran := &JWSTransaction{}

err := a.parseJWS(transaction, tran)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit ce89958

Please sign in to comment.