Apple App Attest服务端Nonce不匹配问题求助
Apple App Attest服务端Nonce不匹配问题求助
大家好,我现在正在尝试在我的应用中实现Apple的App Attest功能,后端用的是Go语言。我按照指南完成了证书和密钥验证的步骤,结果都正确,但卡在了clientDataHash和OID对象中获取的值不匹配的问题上,怎么都调不对。
我已经尝试过两种方式生成哈希:
- 直接对服务器挑战进行哈希
- 对挑战的Base64编码结果进行哈希
查了一些资料,比如W3C和WebAuthn的文档,提到需要用JSON对象来处理挑战,但Apple的官方文档里完全没提这一点。另外我也很困惑:既然客户端在调用DeviceCheck SDK之前就必须知道clientDataHash,那这个哈希对应的origin到底应该是什么?
我把服务端代码精简到了最小可运行示例,里面保留了Apple官方的示例attestation对象(这是公开示例,直接放进来确保大家不需要额外复制粘贴就能测试)。下面是我的代码,运行后生成的nonce始终和预期不匹配:
package main import ( "encoding/base64" "encoding/asn1" "fmt" "crypto/sha256" "crypto/x509" "github.com/fxamacker/cbor/v2" ) type CertificateChain struct { _ struct{} `cbor:",toarray"` Leaf []byte Intermediate []byte } type AttestationStatement struct { X5C CertificateChain `cbor:"x5c,toarray"` Receipt []byte `cbor:"receipt"` } type AttestationObject struct { Format string `cbor:"fmt"` Statement AttestationStatement `cbor:"attStmt"` AuthorizationData []byte `cbor:"authData"` } func main() { att_b64 := "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" // 解码attestation对象 attBytes, err := base64.RawURLEncoding.DecodeString(att_b64) if err != nil { fmt.Printf("Failed to decode base64: %v\n", err) return } var attObj AttestationObject if err := cbor.Unmarshal(attBytes, &attObj); err != nil { fmt.Printf("Failed to unmarshal CBOR: %v\n", err) return } // 生成clientDataHash的两种尝试 serverChallenge := []byte("my-test-challenge") // 示例挑战 // 尝试1:直接哈希原始挑战 hash1 := sha256.Sum256(serverChallenge) fmt.Printf("Direct hash of challenge: %x\n", hash1[:]) // 尝试2:哈希Base64编码后的挑战 b64Challenge := base64.RawURLEncoding.EncodeToString(serverChallenge) hash2 := sha256.Sum256([]byte(b64Challenge)) fmt.Printf("Hash of Base64 challenge: %x\n", hash2[:]) // 从receipt中解析出预期的clientDataHash var receiptPayload struct { ClientDataHash []byte `asn1:"tag:0"` } if _, err := asn1.Unmarshal(attObj.Statement.Receipt, &receiptPayload); err != nil { fmt.Printf("Failed to parse receipt ASN.1: %v\n", err) return } fmt.Printf("Expected clientDataHash from receipt: %x\n", receiptPayload.ClientDataHash) }
运行这段代码后,生成的两个哈希值都和从receipt中解析出的预期值不匹配。有没有朋友遇到过同样的问题?或者能指点一下Apple App Attest中clientDataHash的正确生成方式,以及对应的origin应该怎么设置吗?
内容来源于stack exchange
相关产品推荐
相关产品推荐

