Go中的iOS Game Center身份验证

I am trying to write a service in Go that takes the parameters given by GameCenter in

//GKLocalPlayer
- (void)generateIdentityVerificationSignatureWithCompletionHandler:(void (^)(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error))completionHandler

Inside the completionHandler of the method, I am sending the public key URL, base 64 encoded signature, base 64 encoded salt, timestamp and the user's game center ID to my Go service. Inside my Go (in Google App Engine), this is what I am doing:

  • Get the certificate from the public key URL
  • Decode signature and salt
  • Form the payload based on player ID, bundle ID, timestamp and salt
  • Use X509.CheckSignature to verify that the payload matches the signature when it's hased with the public key

*I know that I still need to verify with the certificate authority but I am skipping that for now (if you know how to do that in Go for this case, please please please share!)

Problem: CheckSignature is returning crypto/rsa: verification error and I really think that I am doing everything as instructed by Apple

https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKLocalPlayer_Ref/Reference/Reference.html#//apple_ref/doc/uid/TP40009587-CH1-SW25

And the code that I have so far:

func (v *ValidationRequest) ValidateGameCenter(publicKeyUrl string, playerId string, bundleId string, signature string, salt string, timestamp uint64) error {
    client := urlfetch.Client(v.Context)

    resp, err := client.Get(publicKeyUrl)
    if err != nil {
        v.Context.Errorf("%v", err.Error())
        return err
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        v.Context.Errorf("%v", err.Error())
        return err
    }

    cert, err := x509.ParseCertificate(body)
    if err != nil {
        v.Context.Errorf("%v", err.Error())
        return err
    }

    signatureBytes, err := base64.StdEncoding.DecodeString(signature)
    saltBytes, err:= base64.StdEncoding.DecodeString(salt)

    payload, err := formPayload(v, playerId, bundleId, timestamp, saltBytes)
    if err != nil {
        v.Context.Errorf("%v", err.Error())
        return err
    }

    err = cert.CheckSignature(cert.SignatureAlgorithm, payload, signatureBytes)
    if err != nil {
        v.Context.Errorf("%v", err.Error())
        return err
    }

    return nil
}

func formPayload(v *ValidationRequest, playerId string, bundleId string, timestamp uint64, salt []byte) ([]byte, error) {
    bundleIdBytes := []byte(bundleId)
    playerIdBytes := []byte(playerId)

    payloadBuffer := new(bytes.Buffer)

    written, err := payloadBuffer.Write(playerIdBytes)
    if err != nil {
        return nil, err
    }

    written, err = payloadBuffer.Write(bundleIdBytes)

    if err != nil {
        return nil, err
    }

    var bigEndianTimestamp []byte = make([]byte, 8)
    binary.BigEndian.PutUint64(bigEndianTimestamp, timestamp)


    if written != len(bundleIdBytes) {
        return nil, errors.New(fmt.Sprintf("Failed writing all bytes. Written: %d Length: %d", written, len(bundleIdBytes)))
    }

    written, err = payloadBuffer.Write(bigEndianTimestamp)

    if err != nil {
        return nil, err
    }
    if written != len(bigEndianTimestamp) {
        return nil, errors.New(fmt.Sprintf("Failed writing all bytes. Written: %d Length: %d", written, len(bigEndianTimestamp)))
    }

    written, err = payloadBuffer.Write(salt)
    if err != nil {
        return nil, err
    }

    if written != len(salt) {
        return nil, errors.New(fmt.Sprintf("Failed writing all bytes. Written: %d Length: %d", written, len(salt)))
    }

    return payloadBuffer.Bytes(), nil
}