2011-03-26 16 views
14

La aplicación de muestra en el sitio de desarrolladores de Android valida la compra json utilizando el código de Java. Alguien ha tenido suerte resolviendo cómo validar la compra en python. En particular en GAE?Verificación de la firma en un mensaje de compra de Android en la aplicación en Python en Google App Engine

Los siguientes son los extractos relevantes de la facturación en la aplicación de Android example program. Esto es lo que necesitaría convertirse a python usando PyCrypto, que fue reescrito completamente por Python para Google y es la única lib de seguridad disponible en el motor de la aplicación. Espero que Google sea 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; 
} 
+0

no voy a publicar esto como una responda porque no puedo decir si es exactamente lo que busca, pero pruebe este [enlace - Código de Google] (http://code.google.com/appengine/articles/rpc.html), que muestra cómo escribir un Aplicación GAE que responderá con JSON, que podría modificar para proporcionar su validación Supongo – Timbermar

Respuesta

15

Así es como 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 es publicKey sus base64 clave de Google Play Store en una línea que se obtiene de la consola de desarrollo.

Para las personas que prefieren utilizar M2Crypto, validate_purchase() cambiaría 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 
+0

Tengo que agregar la siguiente línea en la función 'validate_purchase (publicKey, signedData, signature)' para que funcione: 'signedData = signedData.encode (" utf8 ")' – Saqib

+0

¡Gracias, me alegraste el día! También puede transformar la licencia de Google en una clave pública válida como la siguiente: 'openssl enc -base64 -d -en publickey.base64 -A | openssl rsa -inform DER -pubin> publickey.pem' – Moritz

7

que finalmente cuenta de que sus base64 clave pública de Google Play es una secuencia X.509 subjectPublicKeyInfo DER, y que el esquema de firma es RSASSA-PKCS1-v1_5 y no RSASSA-PSS. Si tiene PyCrypto instalado, de hecho 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) 
+0

Esta respuesta evita mezclar el formato de clave y funciona bien. –

1

Ahora que estamos en el año 2016, aquí está 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 
Cuestiones relacionadas