Verifying signature on android in-app purchase message in Python on Google App Engine

前端 未结 3 956
借酒劲吻你
借酒劲吻你 2021-01-30 18:56

The sample application on the android developers site validates the purchase json using java code. Has anybody had any luck working out how to validate the purchase in python.

相关标签:
3条回答
  • 2021-01-30 19:12

    Here's how i did it:

    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)
    

    This assumes that publicKey is your base64 encoded Google Play Store key on one line as you get it from the Developer Console.

    For people who rather use m2crypto, validate_purchase() would change to:

    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 讨论(0)
  • 2021-01-30 19:15

    I finally figured out that your base64 encoded public key from Google Play is an X.509 subjectPublicKeyInfo DER SEQUENCE, and that the signature scheme is RSASSA-PKCS1-v1_5 and not RSASSA-PSS. If you have PyCrypto installed, it's actually quite easy:

    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 讨论(0)
  • 2021-01-30 19:21

    Now that we're in 2016, here's how to do it with 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
    
    0 讨论(0)
提交回复
热议问题