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.
- Hacer que un servicio androide de Python funcione en estado de suspensión
- Comenzando con Android: Java o Python (SL4A)
- Instalar paquetes de python en android
- ¿Qué lenguajes de programación puedo usar en Android Dalvik?
- ¿Son compatibles zlib.compress en Python y Deflater.deflate en Java (Android)?
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; }
- Programación de aplicaciones de Android en jython
- ¿Cómo puedo dejar que el emulador de Android hable con el host local?
- REST / JSON / XML-RPC / SOAP
- Traducción de código Python a JVM
- Escribiendo python (o cualquier otro SL4A) en android
- ¿Hay un indicador de línea de comandos para establecer PYTHONHOME?
- ¿Cómo puedo pasar imágenes en la respuesta de un servidor usando JSON? Base64?
- Android: Java v. Python
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
- Java.lang.OutOfMemoryError en almacenar imágenes en sqlite db
- ¿Cómo mantener un IntentService funcionando incluso cuando la aplicación está cerrada?