Страницы

Поиск по вопросам

понедельник, 23 декабря 2019 г.

Проверка валидности покупки в Google Play

#java #android #android_studio #google_play


После покупки получаем JSON

        {
          "packageName":"*****",
          "productId":"test2",
          "purchaseTime":1449140071159,
          "purchaseState":0,
          "developerPayload":"subs:*****",
          "purchaseToken":"*******",
          "autoRenewing":false
        }


Стандартно можем проверить его на андроид устройстве на валидность (т.е. не подделка
ли покупка).
Данным методом:

        public boolean isValid(TransactionDetails transactionDetails);


используется библиотека https://github.com/anjlab/android-inapp-billing-v3

А необходимо проверять валидность покупки на стороне нашего сервера. т.е. отправить
туда полученный JSON и уже там проверить на валидность.. Как это можно реализовать?
Серверная часть пишется на c#

UP#1
Все работает..
один момент при отправке Сигнатуры его надо Энкодить

       URLEncoder.encode(_base64Signature, "UTF-8")

    


Ответы

Ответ 1



Когда приходит результат покупки, то в поле inapp_signed_data у Intent'а будет сигнатура. Теперь вам нужен RSA. Для C# вам надо его сгенерировать из публичного ключа из вашей консоли разработчика в GP: const string MY_BASE64_PUBLIC_KEY = "Ваш base64 Google публичный ключ"; RSACryptoServiceProvider provider = PEMKeyLoader.CryptoServiceProviderFromPublicKeyInfo(MY_BASE64_PUBLIC_KEY); string xmlPublicKey = provider.ToXmlString(false); Вот класс для этого: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography; using System.IO; namespace PublicKeyConvert { public class PEMKeyLoader { // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" static byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; private static bool CompareBytearrays(byte[] a, byte[] b) { if (a.Length != b.Length) return false; int i = 0; foreach (byte c in a) { if (c != b[i]) return false; i++; } return true; } public static RSACryptoServiceProvider CryptoServiceProviderFromPublicKeyInfo(byte[] x509key) { byte[] seq = new byte[15]; int x509size; if (x509key == null || x509key.Length == 0) return null; x509size = x509key.Length; // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ MemoryStream mem = new MemoryStream(x509key); BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading byte bt = 0; ushort twobytes = 0; try { twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; seq = binr.ReadBytes(15); //read the Sequence OID if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct return null; twobytes = binr.ReadUInt16(); if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8203) binr.ReadInt16(); //advance 2 bytes else return null; bt = binr.ReadByte(); if (bt != 0x00) //expect null byte next return null; twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; twobytes = binr.ReadUInt16(); byte lowbyte = 0x00; byte highbyte = 0x00; if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus else if (twobytes == 0x8202) { highbyte = binr.ReadByte(); //advance 2 bytes lowbyte = binr.ReadByte(); } else return null; byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order int modsize = BitConverter.ToInt32(modint, 0); int firstbyte = binr.PeekChar(); if (firstbyte == 0x00) { //if first byte (highest order) of modulus is zero, don't include it binr.ReadByte(); //skip this null byte modsize -= 1; //reduce modulus buffer size by 1 } byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data return null; int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) byte[] exponent = binr.ReadBytes(expbytes); // ------- create RSACryptoServiceProvider instance and initialize with public key ----- RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); RSAParameters RSAKeyInfo = new RSAParameters(); RSAKeyInfo.Modulus = modulus; RSAKeyInfo.Exponent = exponent; RSA.ImportParameters(RSAKeyInfo); return RSA; } finally { binr.Close(); } } public static RSACryptoServiceProvider CryptoServiceProviderFromPublicKeyInfo(String base64EncodedKey) { try { //see if the file is a valid Base64 encoded cert return CryptoServiceProviderFromPublicKeyInfo(Convert.FromBase64String(base64EncodedKey)); } catch (System.FormatException) { } return null; } public static byte[] X509KeyFromFile(String filename) { byte[] x509key; if (String.IsNullOrWhiteSpace(filename) || !File.Exists(filename)) return null; StreamReader sr = File.OpenText(filename); String filestr = sr.ReadToEnd(); sr.Close(); StringBuilder sb = new StringBuilder(filestr); sb.Replace("-----BEGIN PUBLIC KEY-----", ""); //remove headers/footers, if present sb.Replace("-----END PUBLIC KEY-----", ""); try { //see if the file is a valid Base64 encoded cert x509key = Convert.FromBase64String(sb.ToString()); } catch (System.FormatException) { //if not a b64-encoded publiccert, assume it's binary Stream stream = new FileStream(filename, FileMode.Open); int datalen = (int)stream.Length; x509key = new byte[datalen]; stream.Read(x509key, 0, datalen); stream.Close(); } return x509key; } } } Теперь можно проверять подпись сообщения: public static bool Verify(string message, string base64Signature, string xmlPublicKey){ // Create the provider and load the KEY RSACryptoServiceProvider provider = new RSACryptoServiceProvider(); provider.FromXmlString(xmlPublicKey); // The signature is supposed to be encoded in base64 and the SHA1 checksum // of the message is computed against the UTF-8 representation of the message byte[] signature = System.Convert.FromBase64String(base64Signature); SHA1Managed sha = new SHA1Managed(); byte[] data = System.Text.Encoding.UTF8.GetBytes(message); return provider.VerifyData(data, sha, signature); } message - это ваш JSON.

Комментариев нет:

Отправить комментарий