How to authenticate the GKLocalPlayer on my 'third party server'?

KirylP picture KirylP · Jul 1, 2013 · Viewed 10.3k times · Source

iOS7 introduced new GKLocalPlayer method generateIdentityVerificationSignatureWithCompletionHandler().

Does anyone know how to use it for good? I assume there will be some public API at Apple server-side..

Answer

Cameron picture Cameron · Jan 2, 2014

Here is a C# WebApi server side version:

public class GameCenterController : ApiController
{
    // POST api/gamecenter
    public HttpResponseMessage Post(GameCenterAuth data)
    {
        string token;
        if (ValidateSignature(data, out token))
        {
            return Request.CreateResponse(HttpStatusCode.OK, token);
        }
        return Request.CreateErrorResponse(HttpStatusCode.Forbidden, string.Empty);
    }

    private bool ValidateSignature(GameCenterAuth auth, out string token)
    {
        try
        {
            var cert = GetCertificate(auth.PublicKeyUrl);
            if (cert.Verify())
            {
                var csp = cert.PublicKey.Key as RSACryptoServiceProvider;
                if (csp != null)
                {
                    var sha256 = new SHA256Managed();
                    var sig = ConcatSignature(auth.PlayerId, auth.BundleId, auth.Timestamp, auth.Salt);
                    var hash = sha256.ComputeHash(sig);

                    if (csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA256"), Convert.FromBase64String(auth.Signature)))
                    {
                        // Valid user.
                        // Do server related user management stuff.
                        return true;
                    }
                }
            }

            // Failure
            token = null;
            return false;
        }
        catch (Exception ex)
        {
            // Log the error
            token = null;
            return false;
        }
    }

    private static byte[] ToBigEndian(ulong value)
    {
        var buffer = new byte[8];
        for (int i = 0; i < 8; i++)
        {
            buffer[7 - i] = unchecked((byte)(value & 0xff));
            value = value >> 8;
        }
        return buffer;
    }

    private X509Certificate2 GetCertificate(string url)
    {
        var client = new WebClient();
        var rawData = client.DownloadData(url);
        return new X509Certificate2(rawData);
    }

    private byte[] ConcatSignature(string playerId, string bundleId, ulong timestamp, string salt)
    {
        var data = new List<byte>();
        data.AddRange(Encoding.UTF8.GetBytes(playerId));
        data.AddRange(Encoding.UTF8.GetBytes(bundleId));
        data.AddRange(ToBigEndian(timestamp));
        data.AddRange(Convert.FromBase64String(salt));
        return data.ToArray();
    }
}


public class GameCenterAuth
{
    public string PlayerId { get; set; }
    public string BundleId { get; set; }
    public string Name { get; set; }
    public string PublicKeyUrl { get; set; }
    public string Signature { get; set; }
    public string Salt { get; set; }
    public ulong Timestamp { get; set; }
}