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..
Here is how you can authenticate using objective c. If you need it in another language should be trivial to translate.
-(void)authenticate
{
__weak GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if(viewController)
{
[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController:viewController animated:YES completion:nil];
}
else if(localPlayer.isAuthenticated == YES)
{
[localPlayer generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
if(error != nil)
{
return; //some sort of error, can't authenticate right now
}
[self verifyPlayer:localPlayer.playerID publicKeyUrl:publicKeyUrl signature:signature salt:salt timestamp:timestamp];
}];
}
else
{
NSLog(@"game center disabled");
}
};
}
-(void)verifyPlayer:(NSString *)playerID publicKeyUrl:(NSURL *)publicKeyUrl signature:(NSData *)signature salt:(NSData *)salt timestamp:(uint64_t)timestamp
{
//get certificate
NSData *certificateData = [NSData dataWithContentsOfURL:publicKeyUrl];
//build payload
NSMutableData *payload = [[NSMutableData alloc] init];
[payload appendData:[playerID dataUsingEncoding:NSASCIIStringEncoding]];
[payload appendData:[[[NSBundle mainBundle] bundleIdentifier] dataUsingEncoding:NSASCIIStringEncoding]];
uint64_t timestampBE = CFSwapInt64HostToBig(timestamp);
[payload appendBytes:×tampBE length:sizeof(timestampBE)];
[payload appendData:salt];
//sign
SecCertificateRef certificateFromFile = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData); // load the certificate
SecPolicyRef secPolicy = SecPolicyCreateBasicX509();
SecTrustRef trust;
OSStatus statusTrust = SecTrustCreateWithCertificates( certificateFromFile, secPolicy, &trust);
if(statusTrust != errSecSuccess)
{
NSLog(@"could not create trust");
return;
}
SecTrustResultType resultType;
OSStatus statusTrustEval = SecTrustEvaluate(trust, &resultType);
if(statusTrustEval != errSecSuccess)
{
NSLog(@"could not evaluate trust");
return;
}
if(resultType != kSecTrustResultProceed && resultType != kSecTrustResultRecoverableTrustFailure)
{
NSLog(@"server can not be trusted");
return;
}
SecKeyRef publicKey = SecTrustCopyPublicKey(trust);
uint8_t sha256HashDigest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([payload bytes], (CC_LONG)[payload length], sha256HashDigest);
//check to see if its a match
OSStatus verficationResult = SecKeyRawVerify(publicKey, kSecPaddingPKCS1SHA256, sha256HashDigest, CC_SHA256_DIGEST_LENGTH, (const uint8_t *)[signature bytes], [signature length]);
CFRelease(publicKey);
CFRelease(trust);
CFRelease(secPolicy);
CFRelease(certificateFromFile);
if (verficationResult == errSecSuccess)
{
NSLog(@"Verified");
}
else
{
NSLog(@"Danger!!!");
}
}
EDIT:
as of March 2nd 2015, apple now uses SHA256 instead of SHA1 on the certificate. https://devforums.apple.com/thread/263789?tstart=0