CloudKit Server-to-Server authentication

后端 未结 6 1865
慢半拍i
慢半拍i 2020-12-07 19:11

Apple published a new method to authenticate against CloudKit, server-to-server. https://developer.apple.com/library/content/documentation/DataManagement/Conceptual/CloudKit

6条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-07 19:37

    In case someone else is trying to do this via Ruby, there's a key method alias required to monkey patch the OpenSSL lib to work:

    def signature_for_request(body_json, url, iso8601_date)
      body_sha_hash = Digest::SHA256.digest(body_json)
    
      payload_for_signature = [iso8601_date, Base64.strict_encode64(body_sha_hash), url].join(":")
    
      OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
    
      ec = OpenSSL::PKey::EC.new(CK_PEM_STRING)
      digest = OpenSSL::Digest::SHA256.new
      signature = ec.sign(digest, payload_for_signature)
      base64_signature = Base64.strict_encode64(signature)
    
      return base64_signature
    end
    

    Note that in the above example, url is the path excluding the domain component (starting with /database...) and CK_PEM_STRING is simply a File.read of the pem generated when setting up your private/public key pair.

    The iso8601_date is most easily generated using:

    Time.now.utc.iso8601
    

    Of course, you want to store that in a variable to include in your final request. Construction of the final request can be done with the following pattern:

    def perform_request(url, body, iso8601_date)
    
      signature = self.signature_for_request(body, url, iso8601_date)
    
      uri = URI.parse(CK_SERVICE_BASE + url)
    
      header = {
        "Content-Type" => "text/plain",
        "X-Apple-CloudKit-Request-KeyID" => CK_KEY_ID,
        "X-Apple-CloudKit-Request-ISO8601Date" => iso8601_date,
        "X-Apple-CloudKit-Request-SignatureV1" => signature
      }
    
      # Create the HTTP objects
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true
      request = Net::HTTP::Post.new(uri.request_uri, header)
      request.body = body
    
      # Send the request
      response = http.request(request)
    
      return response
    end
    

    Works like a charm now for me.

提交回复
热议问题