После покупки получаем 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")
Ответ
Когда приходит результат покупки, то в поле 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.
Комментариев нет:
Отправить комментарий