Since I asked the question, Apple has introduced a new API and the answer is available on: Setting up third-party server to interact with Game Center (thank you, user2949759) and on a few other places.
Specifically, since iOS 7 (Apple documentation on Wayback Machine):
-[GKLocalPlayer generateIdentityVerificationSignatureWithCompletionHandler:]
Generates a signature that allows a third party server to authenticate the local player.
The relevant callback block's arguments include NSURL *publicKeyUrl
, NSData *signature
, NSData *salt
, uint64_t timestamp
. These, along with player's playerID
and bundleID
, should be shipped off to the server as the 'login information'.
- At this point, one should, server-side, use
publicKeyURL
to obtain the public key
- serverside, verify that this public key has been signed by Apple
- serverside, concatenate UTF-8-encoded
playerID
, bundleID
, big-endian uint64
timestamp, and verbatim salt
- serverside, generate SHA-256 of the above to produce
digest
- serverside, verify the
signature
that was shipped to the server is correct, using the public key downloaded earlier, the signature
and the digest
There's an example in pseudo-PHP, an example of how one would implement this in Objective-C (which makes little sense to use verbatim), a Go implementation, a Ruby implementation and there is an assortment of implementations in other languages on that same question.
Unsurprisingly, the implementation in Go seems particularly readable, but it doesn't verify that the public key was issued by Apple. Linked Ruby implementation contains a rather clear example of how to do that.