mexico mac game descargar cuenta crear apple app ios authentication ios7 game-center

ios - mac - ¿Cómo autenticar GKLocalPlayer en mi ''servidor de terceros''?



ipad game center (10)

Agregando una respuesta para Python, pero utilizando PyCrypto 2.6 (que es la solución de Google App Engine). También tenga en cuenta que la verificación del certificado público después de la descarga no se realiza aquí, de forma similar a la respuesta de Python anterior utilizando OpenSSL. ¿Es este paso realmente necesario de todos modos? Si comprobamos que la URL de la clave pública va a un dominio de Apple y está usando ssl (https), ¿no significa eso que está protegido contra los ataques del hombre en el medio?

De todos modos, aquí está el código. Tenga en cuenta que el texto binario se reconvierte a binario antes de la concatenación y el uso. También tuve que actualizar mi instalación de python local para usar PyCrypto 2.6 antes de que esto funcionara:

from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA from base64 import b64decode from Crypto.Util.asn1 import DerSequence from binascii import a2b_base64 import struct import urlparse def authenticate_game_center_user(gc_public_key_url, app_bundle_id, gc_player_id, gc_timestamp, gc_salt, gc_unverified_signature): apple_cert = urllib2.urlopen(gc_public_key_url).read() #Verify the url is https and is pointing to an apple domain. parts = urlparse.urlparse(gc_public_key_url) domainName = ".apple.com" domainLocation = len(parts[1]) - len(domainName) actualLocation = parts[1].find(domainName) if parts[0] != "https" or domainName not in parts[1] or domainLocation != actualLocation: logging.warning("Public Key Url is invalid.") raise Exception cert = DerSequence() cert.decode(apple_cert) tbsCertificate = DerSequence() tbsCertificate.decode(cert[0]) subjectPublicKeyInfo = tbsCertificate[6] rsakey = RSA.importKey(subjectPublicKeyInfo) verifier = PKCS1_v1_5.new(rsakey) payload = gc_player_id.encode(''UTF-8'') payload = payload + app_bundle_id.encode(''UTF-8'') payload = payload + struct.pack(''>Q'', int(gc_timestamp)) payload = payload + b64decode(gc_salt) digest = SHA.new(payload) if verifier.verify(digest, b64decode(gc_unverified_signature)): print "The signature is authentic." else: print "The signature is not authentic."

iOS7 introdujo el nuevo método GKLocalPlayer generateIdentityVerificationSignatureWithCompletionHandler() .

¿Alguien sabe cómo usarlo para siempre? Supongo que habrá algunas API públicas en el servidor de Apple ...


Aquí es cómo se puede autenticar utilizando el objetivo c. Si lo necesitas en otro idioma debería ser trivial traducirlo.

-(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:&timestampBE 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!!!"); } }

EDITAR:

a partir del 2 de marzo de 2015, Apple ahora usa SHA256 en lugar de SHA1 en el certificado. https://devforums.apple.com/thread/263789?tstart=0


Aquí está mi implementación en Elixir.

def verify_login(player_id, public_key_url, timestamp, salt64, signature64, bundle_id) do salt = Base.decode64!(salt64) pay_load = <<player_id :: binary, bundle_id :: binary, timestamp :: big-size(64), salt :: binary>> pkey_cert = get_public_key_certificate(public_key_url) cert = :public_key.pkix_decode_cert(pkey_cert, :otp) case cert do {:OTPCertificate, {:OTPTBSCertificate, _, _, _, _, _, _, {:OTPSubjectPublicKeyInfo, _, key}, _, _, _}, _, _} -> signature = Base.decode64!(signature64) case :public_key.verify(pay_load, :sha256, signature, key) do true -> :ok false -> {:error, "apple login verify failed"} end end end def get_public_key_certificate(url) do case HTTPoison.get(url) do {:ok, %HTTPoison.Response{body: body}} -> body end end


Aquí hay una versión actualizada y mejorada de Ruby. Lo he probado con la caja de arena de Apple, pero todavía no con la producción. También he documentado dónde obtener el certificado de CA para verificar el certificado que recibe de la URL de la clave pública.

# iOS Game Center verifier for 3rd party game servers written in Ruby. # # *** Credits *** # Based off of code and comments at https://.com/questions/17408729/how-to-authenticate-the-gklocalplayer-on-my-third-party-server # # *** Improvements *** # This version uses Ruby''s built in HTTP client instead of a 3rd party gem. # It''s updated to use SHA256 instead of SHA1. # It Base64 decodes the salt and signature. If your client or server already does this then you will need to remove the calls to Base64.decode64(). # It validates that the public key URL is from apple.com. # It has been tested with Apple''s Game Center''s sandbox public key URL (https://sandbox.gc.apple.com/public-key/gc-sb-2.cer) and works as of June 24th, 2015. # # *** Notes on public key certificate validation *** # You will need the correct code signing CA to verify the certificate returned from the pubic key URL. # You can download/verify the CA certificate here: https://knowledge.symantec.com/support/code-signing-support/index?page=content&actp=CROSSLINK&id=AR2170 # I have embedded the CA certificate for convenience so that you don''t need to save it to your filesystem. # When the public key URL changes in the future, you may need to update the text in the ca_certificate_text() method. # # *** Usage *** # verified, reason = GameCenterVerifier.verify(...) class GameCenterVerifier # Verify that user provided Game Center data is valid. # False will be returned along with a reason if any validations fail. # Otherwise, it will return true and a nil reason if all validations pass. def self.verify(game_center_id, public_key_url, timestamp, salt, signature, bundle_id) salt = Base64.decode64(salt) signature = Base64.decode64(signature) payload = game_center_id.encode(''UTF-8'') + bundle_id.encode(''UTF-8'') + [timestamp.to_i].pack(''Q>'') + salt pkey_certificate = get_public_key_certificate(public_key_url) return false, ''Invalid public key url'' unless public_key_url_is_valid?(public_key_url) return false, ''Invalid public key certificate'' unless public_key_certificate_is_valid?(pkey_certificate) return false, ''OpenSSL errors (before signature check)'' unless OpenSSL.errors.empty? return false, ''Invalid signature'' unless signature_is_valid?(pkey_certificate, signature, payload) return false, ''OpenSSL errors (after signature check)'' unless OpenSSL.errors.empty? return true, nil end private def self.get_public_key_certificate(url) uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) request = Net::HTTP::Get.new(uri.request_uri) http.use_ssl = true http.open_timeout = 5 http.read_timeout = 5 cert = http.request(request).body OpenSSL::X509::Certificate.new(cert) end def self.public_key_url_is_valid?(public_key_url) uri = URI(public_key_url) tokens = uri.host.split(''.'') return false if uri.scheme != ''https'' return false if tokens[-1] != ''com'' || tokens[-2] != ''apple'' true end def self.public_key_certificate_is_valid?(pkey_cert) pkey_cert.verify(get_ca_certificate.public_key) end def self.signature_is_valid?(pkey_cert, signature, payload) pkey_cert.public_key.verify(OpenSSL::Digest::SHA256.new, signature, payload) end def self.get_ca_certificate OpenSSL::X509::Certificate.new(ca_certificate_text) end def self.ca_certificate_text data = <<EOF -----BEGIN CERTIFICATE----- MIIFWTCCBEGgAwIBAgIQPXjX+XZJYLJhffTwHsqGKjANBgkqhkiG9w0BAQsFADCB yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 aG9yaXR5IC0gRzUwHhcNMTMxMjEwMDAwMDAwWhcNMjMxMjA5MjM1OTU5WjB/MQsw CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxMDAuBgNVBAMTJ1N5bWFudGVjIENs YXNzIDMgU0hBMjU2IENvZGUgU2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAJeDHgAWryyx0gjE12iTUWAecfbiR7TbWE0jYmq0v1obUfej DRh3aLvYNqsvIVDanvPnXydOC8KXyAlwk6naXA1OpA2RoLTsFM6RclQuzqPbROlS Gz9BPMpK5KrA6DmrU8wh0MzPf5vmwsxYaoIV7j02zxzFlwckjvF7vjEtPW7ctZlC n0thlV8ccO4XfduL5WGJeMdoG68ReBqYrsRVR1PZszLWoQ5GQMWXkorRU6eZW4U1 V9Pqk2JhIArHMHckEU1ig7a6e2iCMe5lyt/51Y2yNdyMK29qclxghJzyDJRewFZS AEjM0/ilfd4v1xPkOKiE1Ua4E4bCG53qWjjdm9sCAwEAAaOCAYMwggF/MC8GCCsG AQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL3MyLnN5bWNiLmNvbTASBgNV HRMBAf8ECDAGAQH/AgEAMGwGA1UdIARlMGMwYQYLYIZIAYb4RQEHFwMwUjAmBggr BgEFBQcCARYaaHR0cDovL3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIw HBoaaHR0cDovL3d3dy5zeW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYf aHR0cDovL3MxLnN5bWNiLmNvbS9wY2EzLWc1LmNybDAdBgNVHSUEFjAUBggrBgEF BQcDAgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRow GAYDVQQDExFTeW1hbnRlY1BLSS0xLTU2NzAdBgNVHQ4EFgQUljtT8Hkzl699g+8u K8zKt4YecmYwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJKoZI hvcNAQELBQADggEBABOFGh5pqTf3oL2kr34dYVP+nYxeDKZ1HngXI9397BoDVTn7 cZXHZVqnjjDSRFph23Bv2iEFwi5zuknx0ZP+XcnNXgPgiZ4/dB7X9ziLqdbPuzUv M1ioklbRyE07guZ5hBb8KLCxR/Mdoj7uh9mmf6RWpT+thC4p3ny8qKqjPQQB6rqT og5QIikXTIfkOhFf1qQliZsFay+0yQFMJ3sLrBkFIqBgFT/ayftNTI/7cmd3/SeU x7o1DohJ/o39KK9KEr0Ns5cF3kQMFfo2KwPcwVAB8aERXRTl4r0nS1S+K4ReD6bD dAUK75fDiSKxH3fzvc1D1PFMqT+1i4SvZPLQFCE= -----END CERTIFICATE----- EOF end end


Aquí hay una versión del lado del servidor C # WebApi:

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; } }


Gracias a quienes brindaron soluciones en otros idiomas.

Aquí están los bits relevantes de la solución en Scala (trivial para convertir a Java):

private def verify( signatureAlgorithm: String, publicKey: PublicKey, message: Array[Byte], signature: Array[Byte]): Boolean = { val sha1Signature = Signature.getInstance(signatureAlgorithm) sha1Signature.initVerify(publicKey) sha1Signature.update(message) sha1Signature.verify(signature) } val x509Cert = Try(certificateFactory.generateCertificate(new ByteArrayInputStream(publicKeyBytes)).asInstanceOf[X509Certificate]) x509Cert.foreach { cert => signatureAlgorithm = Some(cert.getSigAlgName) } x509Cert.map(_.getPublicKey) match { case Success(pk) => log.debug("downloaded public key successfully") publicKey = Some(pk) } val buffer = r.id.getBytes("UTF-8") ++ bundleId.getBytes("UTF-8") ++ ByteBuffer.allocate(8).putLong(r.timestamp).array() ++ Base64.decode(r.salt) val result = verify(signatureAlgorithm.getOrElse("SHA256withRSA"), pk, buffer, Base64.decode(r.signature)) log.info("verification result {} for request {}", result, r)

donde r es una instancia de:

case class IOSIdentityVerificationRequest( id: PlayerIdentity, // String publicKeyURL: String, signature: String, // base64 encoded bytes salt: String, // base64 encoded bytes timestamp: Long, error: Option[String]) extends IdentityVerificationRequest


Gracias por los ejemplos de código, aquí viene la solución Golang:

func DownloadCert(url string) []byte { b, err := inet.HTTPGet(url) if err != nil { log.Printf("http request error %s", err) return nil } return b } func VerifySig(sSig, sGcId, sBundleId, sSalt, sTimeStamp string, cert []byte) (err error) { sig, err := base64.StdEncoding.DecodeString(sSig) if err != nil { return } salt, err := base64.StdEncoding.DecodeString(sSalt) if err != nil { return } timeStamp, err := strconv.ParseUint(sTimeStamp, 10, 64) if err != nil { return } payload := new(bytes.Buffer) payload.WriteString(sGcId) payload.WriteString(sBundleId) binary.Write(payload, binary.BigEndian, timeStamp) payload.Write(salt) return verifyRsa(cert, sig, payload.Bytes()) } func verifyRsa(key, sig, content []byte) error { cert, err := x509.ParseCertificate(key) if err != nil { log.Printf("parse cert error %s", err) return err } pub := cert.PublicKey.(*rsa.PublicKey) h := sha256.New() h.Write(content) digest := h.Sum(nil) err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, digest, sig) return err }

un pequeño ayudante de http

func HTTPGet(fullUrl string) (content []byte, err error) { log.Printf("http get url %s", fullUrl) resp, err := http.Get(fullUrl) if err != nil { log.Printf("url can not be reached %s,%s", fullUrl, err) return } if resp.StatusCode != http.StatusOK { return nil, errors.New("ERROR_STATUS_NOT_OK") } body := resp.Body content, err = ioutil.ReadAll(body) if err != nil { log.Printf("url read error %s, %s", fullUrl, err) return } body.Close() return }

código de prueba

func TestVerifyFull(t *testing.T) { cert := DownloadCert("https://sandbox.gc.apple.com/public-key/gc-sb-2.cer") if cert == nil { log.Printf("cert download error ") } sig := "sig as base64" salt := "salt as base64" timeStamp := "1442816155502" gcId := "G:12345678" bId := "com.xxxx.xxxx" err := VerifySig(sig, gcId, bId, salt, timeStamp, cert) log.Printf("result %v", err) }

Una pequeña función para validar el url de descarga de certificados. Impedir la descarga desde cualquier lugar

func IsValidCertUrl(fullUrl string) bool { //https://sandbox.gc.apple.com/public-key/gc-sb-2.cer uri, err := url.Parse(fullUrl) if err != nil { log.Printf("not a valid url %s", fullUrl) return false } if !strings.HasSuffix(uri.Host, "apple.com") { log.Printf("not a valid host %s", fullUrl) return false } if path.Ext(fullUrl) != ".cer" { log.Printf("not a valid ext %s, %s", fullUrl, path.Ext(fullUrl)) return false } return true }


Gracias, @odyth. Gracias, @Lionel. Quiero agregar la versión de Python (basada en la tuya) aquí. Tiene un defecto menor: el certificado de Apple no se ha verificado, no existe tal API en el enlace pyOpenSSL.

import urllib2 import OpenSSL import struct def authenticate_game_center_user(gc_public_key_url, app_bundle_id, gc_player_id, gc_timestamp, gc_salt, gc_unverified_signature): apple_cert = urllib2.urlopen(gc_public_key_url).read() gc_pkey_certificate = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, apple_cert) payload = gc_player_id.encode(''UTF-8'') + app_bundle_id.encode(''UTF-8'') + struct.pack(''>Q'', int(gc_timestamp)) + gc_salt try: OpenSSL.crypto.verify(gc_pkey_certificate, gc_unverified_signature, payload, ''sha1'') print ''Signature verification is done. Success!'' except Exception as res: print res public_key_url = ''https://sandbox.gc.apple.com/public-key/gc-sb.cer'' player_GC_ID = ''G:1870391344'' timestamp = ''1382621610281'' your_app_bundle_id = ''com.myapp.bundle_id'' with open(''./salt.dat'', ''rb'') as f_salt: with open(''./signature.dat'', ''rb'') as f_sign: authenticate_game_center_user(public_key_url, your_app_bundle_id, player_GC_ID, timestamp, f_salt.read(), f_sign.read())


Me tomó mucho tiempo implementarlo en PHP. Ahora me gustaría compartir mi resultado.

Documentación

Puede encontrar una documentación muy simple en Apple: https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKLocalPlayer_Ref/index.html#//apple_ref/occ/instm/GKLocalPlayer/generateIdentityVerificationSignatureWithCompletionHandler

[...]

  1. Use la publicKeyURL en el servidor de terceros para descargar la clave pública.
  2. Verifique con la autoridad de firma apropiada que la clave pública esté firmada por Apple.
  3. Recupera el ID del jugador y el ID del paquete.
  4. Concatene en un búfer de datos la siguiente información, en el orden indicado:
    • El parámetro playerID en formato UTF-8
    • El parámetro bundleID en formato UTF-8
    • El parámetro de marca de tiempo en formato Big-Endian UInt-64
    • El parametro de sal
  5. Generar un valor de hash SHA-256 para el búfer.
  6. Usando la clave pública descargada en el paso 3, verifique que el valor de hash generado en el paso 7 coincida con el parámetro de firma proporcionado por la API.

¡Darse cuenta! El número 7 es una trampa en PHP que me costó horas. Solo debe pasar la cadena concatenada sin formato a la función openssl_verify () .

La actualización del 9 de julio de 2014 en la pregunta .com/questions/24621839/… me ayudó a encontrar el problema

Fuente final

<?php // signature, publicKeyUrl, timestamp and salt are included in the base64/json data you will receive by calling generateIdentityVerificationSignatureWithCompletionHandler. $timestamp = $params["timestamp"]; // e.g. 1447754520194 $user_id = $params["user_id"]; // e.g. G:20010412315 $bundle_id = "com.example.test"; $public_key_url = $params["publicKeyUrl"]; // e.g. https://static.gc.apple.com/public-key/gc-prod-2.cer $salt = base64_decode($params["salt"]); // Binary $signature = base64_decode($params["signature"]); // Binary // Timestamp is unsigned 64-bit integer big endian $highMap = 0xffffffff00000000; $lowMap = 0x00000000ffffffff; $higher = ($timestamp & $highMap) >>32; $lower = $timestamp & $lowMap; $timestamp = pack(''NN'', $higher, $lower); // Concatenate the string $data = $user_id . $bundle_id . $timestamp . $salt; // ATTENTION!!! Do not hash it! $data = hash("sha256", $packed); // Fetch the certificate. This is dirty because it is neither cached nor verified that the url belongs to Apple. $ssl_certificate = file_get_contents($public_key_url); $pem = chunk_split(base64_encode($ssl_certificate), 64, "/n"); $pem = "-----BEGIN CERTIFICATE-----/n" . $pem . "-----END CERTIFICATE-----/n"; // it is also possible to pass the $pem string directly to openssl_verify if (($pubkey_id = openssl_pkey_get_public($pem)) === false) { echo "invalid public key/n"; exit; } // Verify that the signature is correct for $data $verify_result = openssl_verify($data, $signature, $pubkey_id, OPENSSL_ALGO_SHA256); openssl_free_key($pubkey_id); switch($verify_result) { case 1: echo "Signature is ok./n"; break; case 0: echo "Signature is wrong./n"; break; default: echo "An error occurred./n"; break; }


require ''base64'' require ''httparty'' module GameCenter include HTTParty # HHTTParty settings HTTPARTY_TIMEOUT = 10 def authenticate_game_center_user(gc_public_key_url, gc_player_id, gc_timestamp, gc_salt, gc_unverified_signature) # Get game center public key certificate gc_pkey_certificate = get_gc_public_key_certificate(gc_public_key_url) # Check public key certificate unless public_key_certificate_is_valid?(gc_pkey_certificate) do # Handle error end # Check SSL errors unless OpenSSL.errors.empty? do # Handle OpenSSL errors end # Payload building payload = build_payload(gc_player_id, gc_timestamp, gc_salt) # Test signature unless signature_is_valid?(gc_pkey_certificate, gc_unverified_signature, payload) do # Handle error end # Check SSL errors unless OpenSSL.errors.empty? do # Handle OpenSSL errors end # Return player ID gc_player_id end def build_payload(player_id, timestamp, salt) player_id.encode("UTF-8") + "com.myapp.bundle_id".encode("UTF-8") + [timestamp.to_i].pack("Q>") + salt end private def get_gc_public_key_certificate(url) cert = HTTParty.get(url, timeout: HTTPARTY_TIMEOUT, debug_output: Rails.env.production?) OpenSSL::X509::Certificate.new(cert) rescue SocketError => e puts "Key error: " + e.inspect.to_s end def get_ca_certificate OpenSSL::X509::Certificate.new(File.read(''./certs/apple/verisign_class_3_code_signing_2010_ca.cer'')) end def public_key_certificate_is_valid?(pkey_cert) pkey_cert.verify(get_ca_certificate.public_key) end def signature_is_valid?(pkey_cert, signature, payload) pkey_cert.public_key.verify(OpenSSL::Digest::SHA1.new, signature, payload) end end

Aquí está mi implementación ruby ​​(como módulo). Gracias a tu Objective-C, ha sido mucho más fácil.

Tenga en cuenta que me vi obligado a descargar el certificado de CA en un sitio web de servicio ssl de terceros, porque el certificado de clave pública no ha sido firmado Apple y Apple no proporciona ningún certificado de CA para validar el certificado de Sandbox Game Center hasta el momento.

No he probado esto en producción, pero funciona bien en el modo sandbox.