-1

Go Verion : go version go1.16.7 linux/amd64

Imports:

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"
)

Code I'm using

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
}

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

Error on the console:

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: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/go/src/runtime/asm_amd64.s:1371 (0x46e5a0)
    goexit: BYTE    $0x90   // NOP

I don't know why its coming, ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Zain Ur Rehman
  • 287
  • 3
  • 14

1 Answers1

0

The actual problem is that func AuthKeyFromBytes(key []byte) is returning nil with some error or corrupted data into privateKey. Then while you are calling privateKey.Sign go is not able to conver variable privateKey of type PrivateKey into pointer *PrivateKey.

Do not omit error assignment and check for error before using privateKey

privateKey, privateKeyError = AuthKeyFromBytes([]byte(ApplePrivateKey))
MenyT
  • 1,653
  • 1
  • 8
  • 19