Verificación de la firma en el mensaje de compra en la aplicación de Android en Python en Google App Engine

La aplicación de ejemplo en el sitio de desarrolladores de android valida la compra json utilizando código java. ¿Alguien ha tenido alguna suerte trabajando en cómo validar la compra en python. En particular en GAE?

Los siguientes son los extractos relevantes del programa de ejemplo de facturación de Android en la aplicación. Esto es lo que tendría que ser convertido a python usando PyCrypto que fue reescrito para ser completamente python por Google y es la única librería de seguridad disponible en el motor de la aplicación. Espero que Google es genial conmigo utilizando los extractos a continuación.

private static final String KEY_FACTORY_ALGORITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; String base64EncodedPublicKey = "your public key here"; PublicKey key = Security.generatePublicKey(base64EncodedPublicKey); verified = Security.verify(key, signedData, signature); public static PublicKey generatePublicKey(String encodedPublicKey) { try { byte[] decodedKey = Base64.decode(encodedPublicKey); KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM); return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); } catch ... } } public static boolean verify(PublicKey publicKey, String signedData, String signature) { if (Consts.DEBUG) { Log.i(TAG, "signature: " + signature); } Signature sig; try { sig = Signature.getInstance(SIGNATURE_ALGORITHM); sig.initVerify(publicKey); sig.update(signedData.getBytes()); if (!sig.verify(Base64.decode(signature))) { Log.e(TAG, "Signature verification failed."); return false; } return true; } catch ... } return false; } 

Aquí es cómo lo hice:

 from Crypto.Hash import SHA from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 from base64 import b64decode def chunks(s, n): for start in range(0, len(s), n): yield s[start:start+n] def pem_format(key): return '\n'.join([ '-----BEGIN PUBLIC KEY-----', '\n'.join(chunks(key, 64)), '-----END PUBLIC KEY-----' ]) def validate_purchase(publicKey, signedData, signature): key = RSA.importKey(pem_format(publicKey)) verifier = PKCS1_v1_5.new(key) data = SHA.new(signedData) sig = b64decode(signature) return verifier.verify(data, sig) 

Esto supone que publicKey es su clave de Google Play Store codificada en base64 en una línea a medida que la obtiene de la Consola del programador.

Para las personas que utilizan m2crypto, validate_purchase() cambiará a:

 from M2Crypto import RSA, BIO, EVP from base64 import b64decode # pem_format() as above def validate_purchase(publicKey, signedData, signature): bio = BIO.MemoryBuffer(pem_format(publicKey)) rsa = RSA.load_pub_key_bio(bio) key = EVP.PKey() key.assign_rsa(rsa) key.verify_init() key.verify_update(signedData) return key.verify_final(b64decode(signature)) == 1 

Finalmente me di cuenta de que su clave pública codificada en base64 de Google Play es una SECCION DER de subjectPublicKeyInfo de X.509, y que el esquema de firma es RSASSA-PKCS1-v1_5 y no RSASSA-PSS. Si tiene instalado PyCrypto , en realidad es bastante fácil:

 import base64 from Crypto.Hash import SHA from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 # Your base64 encoded public key from Google Play. _PUBLIC_KEY_BASE64 = "YOUR_BASE64_PUBLIC_KEY_HERE" # Key from Google Play is a X.509 subjectPublicKeyInfo DER SEQUENCE. _PUBLIC_KEY = RSA.importKey(base64.standard_b64decode(_PUBLIC_KEY_BASE64)) def verify(signed_data, signature_base64): """Returns whether the given data was signed with the private key.""" h = SHA.new() h.update(signed_data) # Scheme is RSASSA-PKCS1-v1_5. verifier = PKCS1_v1_5.new(_PUBLIC_KEY) # The signature is base64 encoded. signature = base64.standard_b64decode(signature_base64) return verifier.verify(h, signature) 

Ahora que estamos en 2016, aquí es cómo hacerlo con cryptography :

 import base64 import binascii from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding class RSAwithSHA1: def __init__(self, public_key): # the public key google gives you is in DER encoding # let cryptography handle it for you self.public_key = serialization.load_der_public_key( base64.b64decode(public_key), backend=default_backend() ) def verify(self, data, signature): """ :param str data: purchase data :param str signature: data signature :return: True signature verification passes or False otherwise """ # note the signature is base64 encoded signature = base64.b64decode(signature.encode()) # as per https://developer.android.com/google/play/billing/billing_reference.html # the signature uses "the RSASSA-PKCS1-v1_5 scheme" verifier = self.public_key.verifier( signature, padding.PKCS1v15(), hashes.SHA1(), ) verifier.update(data.encode()) try: verifier.verify() except InvalidSignature: return False else: return True 
  • Programación Android de Python
  • ¿Puedo portar mis aplicaciones de python existentes en ASE?
  • ¿Plataforma para desarrollar todo el google de las cosas?
  • Pasar un parámetro en un Tornado HttpGet con varios parámetros
  • Cómo organizar Eclipse - Workspace VS Lenguajes de programación
  • ¿Por qué incorporar el comando repo en un script bash?
  • ¿Cómo utilizar mejor los datos GPS?
  • Sincronización de base de datos de aplicaciones de Android con base de datos remota
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.