How to authenticate the GKLocalPlayer on my 'third party server'?
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
How to authenticate GKLocalPlayer on my third-party-server using Java JDK 1.7?
The problem appears to be with the ByteBuffer you're passing to Signature.update(). If you pass the underlying array by changing
sig.update(dataBuffer);
to
sig.update(dataBuffer.array());
the verification appears to succeed. Based on the documentation for Signature.update(ByteBuffer), I suspect it's because it's trying to read from the last position you wrote to in the buffer, and not finding any data.
Setting up third-party server to interact with Game Center
Looks like as of iOS 7, this is possible with Game Center using:
[localPlayer generateIdentityVerificationSignatureWithCompletionHandler]
Once you have verified the identity of the player using the generateIdentity call,
- Associate the player id with a user on your server's db
- Use whatever access token / authentication pattern your REST framework provides for subsequent calls
https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKLocalPlayer_Ref/Reference/Reference.html
Also for reference, here is the dictionary that we end up sending off to our server based on the response from generateIdentityVerificationSignatureWithCompletionHandler
NSDictionary *paramsDict = @{
@"publicKeyUrl":[publicKeyUrl absoluteString],
@"timestamp":[NSString stringWithFormat:@"%llu", timestamp],
@"signature":[signature base64EncodedStringWithOptions:0],
@"salt":[salt base64EncodedStringWithOptions:0],
@"playerID":localPlayer.playerID,
@"bundleID":[[NSBundle mainBundle] bundleIdentifier]
};
Where to obtain public key for GameCenter 3rd party authentication on a server
To avoid CSRF attack I just check the url with uri.DnsSafeHost.EndsWith(".gc.apple.com", StringComparison.OrdinalIgnoreCase)
.
Related Topics
How to Convert a Nib-Based Project to a Storyboard-Based
Copy Nsattributedstring in Uipasteboard
Why Is Uitextfield.Text an Optional
Cannot Use Instance Member Within Property Initializer
Amazon Aws iOS Sdk: How to List All File Names in a Folder
Dynamic Link Object Has No Url
Swift Image Retrieving from Parse Sdk - Getting Crashed
How to Render a Uiview with Transparent Background on an Scnplane in Arkit
Combine Framework Serialize Async Operations
403 Error - Thats an Error. Error: Disallowed_Useragent
Wkwebview Content Loaded Function Never Get Called
Swift 2.0 Sorting Array of Objects by Property
Uicollectionview's Cell Registerclass in Swift
Creating Framework That Requires (Depends On) Another Framework
This Class Is Not Key Value Coding-Compliant with @Ibinspectable