Phi*_*ipp 22 authentication server-to-server cloudkit
Apple发布了一种新的方法来对服务器到服务器的CloudKit进行身份验证.https://developer.apple.com/library/content/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/SettingUpWebServices.html#//apple_ref/doc/uid/TP40015240-CH24-SW6
我尝试对CloudKit和此方法进行身份验证.起初我生成了密钥对并将公钥提供给CloudKit,到目前为止没问题.
我开始构建请求标头.根据文档,它应该如下所示:
X-Apple-CloudKit-Request-KeyID: [keyID]
X-Apple-CloudKit-Request-ISO8601Date: [date]
X-Apple-CloudKit-Request-SignatureV1: [signature]
Run Code Online (Sandbox Code Playgroud)
文件说:
在步骤1中创建的签名.
第1步说:
连接以下参数并用冒号分隔它们.
[Current date]:[Request body]:[Web Service URL]
我问自己"为什么我必须生成密钥对?".
但第2步说:
使用您的私钥计算此消息的ECDSA签名.
也许他们的意思是用私钥签名连接签名并将其放入标题?无论如何我试过两个......
我对此(无符号)签名值的示例如下所示:
2016-02-06T20:41:00Z:YTdkNzAwYTllNjI1M2EyZTllNDNiZjVmYjg0MWFhMGRiMTE2MjI1NTYwNTA2YzQyODc4MjUwNTQ0YTE5YTg4Yw==:https://api.apple-cloudkit.com/database/1/[iCloud Container]/development/public/records/lookup
Run Code Online (Sandbox Code Playgroud)
请求体值是SHA256哈希值,然后是base64编码的.我的问题是,我应该用":"连接,但是网址和日期也包含":".这是对的吗?(我还尝试对URL进行URL编码并删除日期中的":").
接下来,我用ECDSA签署了这个签名字符串,将其放入标题并发送.但我总是得到401"身份验证失败".为了签名,我使用ecdsa python模块,使用以下命令:
from ecdsa import SigningKey
a = SigningKey.from_pem(open("path_to_pem_file").read())
b = "[date]:[base64(request_body)]:/database/1/iCloud....."
print a.sign(b).encode('hex')
Run Code Online (Sandbox Code Playgroud)
也许python模块无法正常工作.但它可以从私钥生成正确的公钥.所以我希望其他功能也能奏效.
有没有人设法使用服务器到服务器方法对CloudKit进行身份验证?它是如何正常工作的?
编辑:正确的python版本工作
from ecdsa import SigningKey
import ecdsa, base64, hashlib
a = SigningKey.from_pem(open("path_to_pem_file").read())
b = "[date]:[base64(sha256(request_body))]:/database/1/iCloud....."
signature = a.sign(b, hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der)
signature = base64.b64encode(signature)
print signature #include this into the header
Run Code Online (Sandbox Code Playgroud)
Max*_*her 12
消息的最后部分
[Current date]:[Request body]:[Web Service URL]
Run Code Online (Sandbox Code Playgroud)
不得包含域(它必须包含任何查询参数):
2016-02-06T20:41:00Z:YTdkNzAwYTllNjI1M2EyZTllNDNiZjVmYjg0MWFhMGRiMTE2MjI1NTYwNTA2YzQyODc4MjUwNTQ0YTE5YTg4Yw==:/database/1/[iCloud Container]/development/public/records/lookup
Run Code Online (Sandbox Code Playgroud)
使用换行符提高可读性:
2016-02-06T20:41:00Z
:YTdkNzAwYTllNjI1M2EyZTllNDNiZjVmYjg0MWFhMGRiMTE2MjI1NTYwNTA2YzQyODc4MjUwNTQ0YTE5YTg4Yw==
:/database/1/[iCloud Container]/development/public/records/lookup
Run Code Online (Sandbox Code Playgroud)
确切的API调用取决于您使用的具体语言和加密库.
//1. Date
//Example: 2016-02-07T18:58:24Z
//Pitfall: make sure to not include milliseconds
date = isoDateWithoutMilliseconds()
//2. Payload
//Example (empty string base64 encoded; GET requests):
//47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
//Pitfall: make sure the output is base64 encoded (not hex)
payload = base64encode(sha256(body))
//3. Path
//Example: /database/1/[containerIdentifier]/development/public/records/lookup
//Pitfall: Don't include the domain; do include any query parameter
path = stripDomainKeepQueryParams(url)
//4. Message
//Join date, payload, and path with colons
message = date + ':' + payload + ':' + path
//5. Compute a signature for the message using your private key.
//This step looks very different for every language/crypto lib.
//Pitfall: make sure the output is base64 encoded.
//Hint: the key itself contains information about the signature algorithm
// (on NodeJS you can use the signature name 'RSA-SHA256' to compute a
// the correct ECDSA signature with an ECDSA key).
signature = base64encode(sign(message, key))
//6. Set headers
X-Apple-CloudKit-Request-KeyID = keyID
X-Apple-CloudKit-Request-ISO8601Date = date
X-Apple-CloudKit-Request-SignatureV1 = signature
//7. For POST requests, don't forget to actually send the unsigned request body
// (not just the headers)
Run Code Online (Sandbox Code Playgroud)
提取Apple的cloudkit.js实现并使用Apple示例代码node-client-s2s/index.js中的第一个调用,您可以构造以下内容:
你哈希请求主体的请求有sha256:
var crypto = require('crypto');
var bodyHasher = crypto.createHash('sha256');
bodyHasher.update(requestBody);
var hashedBody = bodyHasher.digest("base64");
Run Code Online (Sandbox Code Playgroud)
使用[Current date]:[Request body]:[Web Service URL]配置中提供的私钥对有效负载进行签名.
var c = crypto.createSign("RSA-SHA256");
c.update(rawPayload);
var requestSignature = c.sign(key, "base64");
Run Code Online (Sandbox Code Playgroud)
另一个注意事项是[Web Service URL]有效负载组件不得包含域,但它确实需要任何查询参数.
确保日期值X-Apple-CloudKit-Request-ISO8601Date与签名中的日期值相同.(这些细节没有完整记录,但通过查看CloudKit.js实现可以观察到).
更完整的nodejs示例如下所示:
(function() {
const https = require('https');
var fs = require('fs');
var crypto = require('crypto');
var key = fs.readFileSync(__dirname + '/eckey.pem', "utf8");
var authKeyID = 'auth-key-id';
// path of our request (domain not included)
var requestPath = "/database/1/iCloud.containerIdentifier/development/public/users/current";
// request body (GET request is blank)
var requestBody = '';
// date string without milliseconds
var requestDate = (new Date).toISOString().replace(/(\.\d\d\d)Z/, "Z");
var bodyHasher = crypto.createHash('sha256');
bodyHasher.update(requestBody);
var hashedBody = bodyHasher.digest("base64");
var rawPayload = requestDate + ":" + hashedBody + ":" + requestPath;
// sign payload
var c = crypto.createSign("sha256");
c.update(rawPayload);
var requestSignature = c.sign(key, "base64");
// put headers together
var headers = {
'X-Apple-CloudKit-Request-KeyID': authKeyID,
'X-Apple-CloudKit-Request-ISO8601Date': requestDate,
'X-Apple-CloudKit-Request-SignatureV1': requestSignature
};
var options = {
hostname: 'api.apple-cloudkit.com',
port: 443,
path: requestPath,
method: 'GET',
headers: headers
};
var req = https.request(options, (res) => {
//... handle nodejs response
});
req.end();
})();
Run Code Online (Sandbox Code Playgroud)
这也是一个要点:https://gist.github.com/jessedc/a3161186b450317a9cb5
可以使用此命令完成第一次散列:
openssl sha -sha256 -binary < body.txt | base64
Run Code Online (Sandbox Code Playgroud)
要签署请求的第二部分,您需要一个比OSX 10.11附带的更现代版本的openSSL并使用以下命令:
/usr/local/bin/openssl dgst -sha256WithRSAEncryption -binary -sign ck-server-key.pem raw_signature.txt | base64
Run Code Online (Sandbox Code Playgroud)
感谢下面的@maurice_vB和Twitter上的这个信息
从我正在Node中工作的项目中提取出来.也许你会发现它很有用.替换X-Apple-CloudKit-Request-KeyID和容器标识符requestOptions.path以使其工作.
私钥/ pem生成为:openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem并生成公钥以在CloudKit仪表板上注册openssl ec -in eckey.pem -pubout.
var crypto = require("crypto"),
https = require("https"),
fs = require("fs")
var CloudKitRequest = function(payload) {
this.payload = payload
this.requestOptions = { // Used with `https.request`
hostname: "api.apple-cloudkit.com",
port: 443,
path: '/database/1/iCloud.com.your.container/development/public/records/modify',
method: 'POST',
headers: { // We will add more headers in the sign methods
"X-Apple-CloudKit-Request-KeyID": "your-ck-request-keyID"
}
}
}
Run Code Online (Sandbox Code Playgroud)
签署请求:
CloudKitRequest.prototype.sign = function(privateKey) {
var dateString = new Date().toISOString().replace(/\.[0-9]+?Z/, "Z"), // NOTE: No milliseconds
hash = crypto.createHash("sha256"),
sign = crypto.createSign("RSA-SHA256")
// Create the hash of the payload
hash.update(this.payload, "utf8")
var payloadSignature = hash.digest("base64")
// Create the signature string to sign
var signatureData = [
dateString,
payloadSignature,
this.requestOptions.path
].join(":") // [Date]:[Request body]:[Web Service URL]
// Construct the signature
sign.update(signatureData)
var signature = sign.sign(privateKey, "base64")
// Update the request headers
this.requestOptions.headers["X-Apple-CloudKit-Request-ISO8601Date"] = dateString
this.requestOptions.headers["X-Apple-CloudKit-Request-SignatureV1"] = signature
return signature // This might be useful to keep around
}
Run Code Online (Sandbox Code Playgroud)
现在您可以发送请求:
CloudKitRequest.prototype.send = function(cb) {
var request = https.request(this.requestOptions, function(response) {
var responseBody = ""
response.on("data", function(chunk) {
responseBody += chunk.toString("utf8")
})
response.on("end", function() {
cb(null, JSON.parse(responseBody))
})
})
request.on("error", function(err) {
cb(err, null)
})
request.end(this.payload)
}
Run Code Online (Sandbox Code Playgroud)
所以给出以下内容:
var privateKey = fs.readFileSync("./eckey.pem"),
creationPayload = JSON.stringify({
"operations": [{
"operationType" : "create",
"record" : {
"recordType" : "Post",
"fields" : {
"title" : { "value" : "A Post From The Server" }
}
}
}]
})
Run Code Online (Sandbox Code Playgroud)
使用请求:
var creationRequest = new CloudKitRequest(creationPayload)
creationRequest.sign(privateKey)
creationRequest.send(function(err, response) {
console.log("Created a new entry with error", err, "and respone", response)
})
Run Code Online (Sandbox Code Playgroud)
为了您的复制粘贴乐趣:https://gist.github.com/spllr/4bf3fadb7f6168f67698(已编辑)
| 归档时间: |
|
| 查看次数: |
2976 次 |
| 最近记录: |