You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Web Crypto API签名CloudKit请求遇认证失败问题排查

CloudKit Web服务Web Crypto API认证失败问题

已验证可用的Node Crypto实现代码

import Crypto from 'crypto'

export async function cloudKitRequest(body, method, operationSubpath) {
    const subpath = `/database/1/${env.CLOUDKIT_CONTAINER}/${env.CLOUDKIT_ENVIRONMENT}/${operationSubpath}`
    const bodyString = JSON.stringify(body)
    const hash = Crypto.createHash('sha256')
    const sign = Crypto.createSign('SHA256')
    const date = new Date().toISOString().replace(/\.[0-9]+?Z/, 'Z')
    hash.update(bodyString, 'utf8')
    sign.update(`${date}:${hash.digest('base64')}:${subpath}`)
    return await fetch(`https://api.apple-cloudkit.com${subpath}`, {
        body: bodyString,
        method: method,
        headers: {
            'Content-Type': 'application/json',
            'X-Apple-Cloudkit-Request-KeyID': env.CLOUDKIT_KEY_ID,
            'X-Apple-CloudKit-Request-ISO8601Date': date,
            'X-Apple-CloudKit-Request-SignatureV1': sign.sign(
                env.CLOUDKIT_PRIVATE_KEY,
                'base64'
            ),
        },
    })
}

出现认证错误的Web Crypto实现代码

const encoder = new TextEncoder()

export async function cloudKitRequest(body: any, method: string, operationSubpath: string, env: any) {
    const subpath = `/database/1/${env.CLOUDKIT_CONTAINER}/${env.CLOUDKIT_ENVIRONMENT}/${operationSubpath}`

    const date = new Date().toISOString().replace(/\.[0-9]+?Z/, 'Z')
    const bodyHash = await hashBody(body)
    const message = `${date}:${bodyHash}:${subpath}`
    const privateKey = await loadPrivateKey(env.CLOUDKIT_PRIVATE_KEY)
    const signature = await signMessage(privateKey, message)

    const headers = new Headers({
        'Content-Type': 'application/json',
        'X-Apple-CloudKit-Request-KeyID': env.CLOUDKIT_KEY_ID,
        'X-Apple-CloudKit-Request-ISO8601Date': date,
        'X-Apple-CloudKit-Request-SignatureV1': signature,
    })

    const options = {
        method: method,
        headers: headers,
        body: JSON.stringify(body),
    }

    return await fetch(`https://api.apple-cloudkit.com${subpath}`, options)
}

const hashBody = async (requestBody: any) => {
    const encodedBody = encoder.encode(JSON.stringify(requestBody))
    const hashBuffer = await crypto.subtle.digest('SHA-256', encodedBody)
    return btoa(String.fromCharCode(...new Uint8Array(hashBuffer)))
}

const b642ab = (base64_string: string) => {
    return Uint8Array.from(atob(base64_string), (c) => c.charCodeAt(0))
}

const signMessage = async (privateKey: CryptoKey, message: string) => {
    const encoder = new TextEncoder()
    const encodedMessage = encoder.encode(message)

    const signature = await crypto.subtle.sign(
        {
            name: 'ECDSA',
            hash: { name: 'SHA-256' },
        },
        privateKey,
        encodedMessage
    )

    return btoa(String.fromCharCode(...new Uint8Array(signature)))
}

const loadPrivateKey = async (pem: string) => {
    const binaryDer = b642ab(pem)

    const importParams = {
        name: 'ECDSA',
        namedCurve: 'P-256',
    }

    return await crypto.subtle.importKey('pkcs8', binaryDer.buffer, importParams, true, ['sign'])
}

问题详情

通过Node Crypto API实现CloudKit Web服务请求认证已验证可用,但切换到Web Crypto API后始终返回“Authentication Failed”错误,已确认请求体哈希计算正确。

私钥通过命令 openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem 生成,Node环境直接使用该pem文件的完整内容即可正常工作;Web Crypto环境中,尝试过直接使用原pem内容,也尝试过通过 openssl pkcs8 -topk8 -nocrypt -in eckey.pem -out eckey_okcs8.pem 转换为PKCS8格式,移除头部、尾部和换行后使用,均无法通过认证。

示例调用代码

await cloudKitRequest({
        operations: {
            operationType: 'create',
            recordName: 'randomuuid',
            record: {
                recordType: 'MyRecord',
                fields: {
                    name: {
                        value: 'Test Name',
                    }
                },
            },
        },
    },
    'POST',
    'public/records/modify',
    context.env
)

内容的提问来源于stack exchange,提问作者Finn Voorhees

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:47:36