在 Go Server 中为 IOS 生成订阅报价签名时出现问题

Posted

技术标签:

【中文标题】在 Go Server 中为 IOS 生成订阅报价签名时出现问题【英文标题】:Issue while generating subscription offer signature for IOS in Go Server 【发布时间】:2021-11-08 10:42:53 【问题描述】:

Go 版本:go 版本 go1.16.7 linux/amd64

进口:

import (
    "crypto/ecdsa"
    "crypto/rand"
    "crypto/sha256"
    "crypto/x509"
    "encoding/asn1"
    "encoding/base64"
    "encoding/pem"
    "errors"
    "fmt"
    "math/big"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
    uuid "github.com/satori/go.uuid"
    "go.mongodb.org/mongo-driver/mongo"
)

我正在使用的代码

var (
    // the id and private key below is got from here:
    // https://developer.apple.com/documentation/storekit/in-app_purchase/generating_a_subscription_offer_signature_using_node_js
    AppleKeyId      = "mykeyid"
    ApplePrivateKey = `-----BEGIN PRIVATE KEY-----
    MyKey
    -----END PRIVATE KEY-----`

    privateKey, _ = AuthKeyFromBytes([]byte(ApplePrivateKey))
    sep           = "\u2063"
    AppBundleID   = "mybundlename"
)

func AuthKeyFromBytes(key []byte) (*ecdsa.PrivateKey, error) 
    var err error

    // Parse PEM block
    var block *pem.Block
    if block, _ = pem.Decode(key); block == nil 
        return nil, errors.New("token: AuthKey must be a valid .p8 PEM file")
    

    // Parse the key
    var parsedKey interface
    if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil 
        return nil, err
    

    var pkey *ecdsa.PrivateKey
    var ok bool
    if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok 
        return nil, errors.New("token: AuthKey must be of type ecdsa.privateKey")
    

    return pkey, nil


type SignParams struct 
    ProductIdentifier   string `json:"productIdentifier"`
    OfferID             string `json:"offerID"`
    ApplicationUsername string `json:"applicationUsername"`


type SignResult struct 
    KeyID     string `json:"keyID"`
    Nonce     string `json:"nonce"`
    Timestamp int64  `json:"timestamp"`
    Signature string `json:"signature"`


func Sign(params *SignParams) (SignResult, error) 
    nonce := uuid.NewV4().String()
    timestamp := time.Now().UnixNano() / 1000000
    payload := AppBundleID + sep +
        AppleKeyId + sep +
        params.ProductIdentifier + sep +
        params.OfferID + sep +
        params.ApplicationUsername + sep +
        nonce + sep +
        fmt.Sprintf("%v", timestamp)
    hash := sha256.Sum256([]byte(payload))
    sig, err := privateKey.Sign(rand.Reader, hash[:], nil) Error Here
    if err != nil 
        return SignResult, err
    

    return SignResult
        KeyID:     AppleKeyId,
        Nonce:     nonce,
        Timestamp: timestamp,
        Signature: base64.StdEncoding.EncodeToString(sig),
    , nil

问题:sig, err := privateKey.Sign(rand.Reader, hash[:], nil) in Sign Function

控制台出错:

2021/09/13 09:47:59 [Recovery] 2021/09/13 - 09:47:59 panic recovered:
POST /signatures HTTP/1.1
Host: localhost:5000
Accept: */*
Accept-Encoding: gzip, deflate, br
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 114
Content-Type: application/json
Postman-Token: 12f5ac0a-3379-40ad-826e-78dc63579cbb
User-Agent: PostmanRuntime/7.28.4


runtime error: invalid memory address or nil pointer dereference
/usr/local/go/src/runtime/panic.go:212 (0x435cda)
    panicmem: panic(memoryError)
/usr/local/go/src/runtime/signal_unix.go:734 (0x44e852)
    sigpanic: panicmem()
/usr/local/go/src/crypto/ecdsa/ecdsa.go:204 (0x5652b8)
    Sign: entropylen := (priv.Curve.Params().BitSize + 7) / 16
/usr/local/go/src/crypto/ecdsa/ecdsa.go:116 (0x564ba4)
    (*PrivateKey).Sign: r, s, err := Sign(rand, priv, digest)
/usr/local/go/src/crypto/ecdsa/ecdsa.go:286 (0xd8fd0f)
    SignASN1: return priv.Sign(rand, hash, nil)
/home/zainkhan/GolandProjects/kotc-server/controllers/signature.go:121 (0xd8fcc1)
    Sign: sig, err := ecdsa.SignASN1(rand.Reader, privateKey, hash[:])
/home/zainkhan/GolandProjects/kotc-server/controllers/signature.go:39 (0xd8f284)
    (*signaturesController).post: final, err := Sign(req)
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/context.go:165 (0x992c99)
    (*Context).Next: c.handlers[c.index](c)
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/recovery.go:99 (0x992c80)
    CustomRecoveryWithWriter.func1: c.Next()
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/context.go:165 (0x991d73)
    (*Context).Next: c.handlers[c.index](c)
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/logger.go:241 (0x991d32)
    LoggerWithConfig.func1: c.Next()
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/context.go:165 (0x988109)
    (*Context).Next: c.handlers[c.index](c)
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/gin.go:489 (0x9880ef)
    (*Engine).handleHTTPRequest: c.Next()
/home/zainkhan/GolandProjects/pkg/mod/github.com/gin-gonic/gin@v1.7.3/gin.go:445 (0x987bdb)
    (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/go/src/net/http/server.go:2867 (0x6e2462)
    serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/go/src/net/http/server.go:1932 (0x6dd88c)
    (*conn).serve: serverHandlerc.server.ServeHTTP(w, w.req)
/usr/local/go/src/runtime/asm_amd64.s:1371 (0x46e5a0)
    goexit: BYTE    $0x90   // NOP

我不知道它为什么来, ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////////////////////// //////

【问题讨论】:

【参考方案1】:

实际的问题是func AuthKeyFromBytes(key []byte) 返回 nil 并带有一些错误或损坏的数据到 privateKey。然后,当您调用 privateKey.Sign 时,go 无法将 PrivateKey 类型的变量 privateKey 转换为指针 *PrivateKey

在使用privateKey之前不要省略错误赋值和检查错误

privateKey, privateKeyError = AuthKeyFromBytes([]byte(ApplePrivateKey))

【讨论】:

"error": "token: AuthKey must be a valid .p8 PEM file" 我正在尝试从 .pem 文件添加私钥,但仍然面临这个问题。如果您有任何解决方案,请告诉我 我不太擅长证书,但在我看来,您需要将 PEM 转换为 p8 证书。这可能会有所帮助***.com/a/39327439/12301864

以上是关于在 Go Server 中为 IOS 生成订阅报价签名时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 应用中保存数据

php [请求报价] YITH WooCommerce请求报价和WooCommerce订阅

如何在 SQL Server 2000 中为所有单笔表格生成脚本? [关闭]

在 SQL Server 中为默认值或绑定生成脚本

在 SQL Server 2005 中为数据库角色成员生成脚本

在 React Native 中为 iOS 生成调试 IPA