You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
9.4 KiB
221 lines
9.4 KiB
using System;
|
|
using System.Text;
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
using LitJson;
|
|
|
|
namespace Sog.Crypto
|
|
{
|
|
public static class GooglePaySecurity
|
|
{
|
|
public static bool CheckSignatureCS(string purchaseData, string signature, string publicKeyBase64)
|
|
{
|
|
try
|
|
{
|
|
byte[] purchaseInfoBytes = Encoding.UTF8.GetBytes(purchaseData);
|
|
byte[] signatureBytes = Convert.FromBase64String(signature);
|
|
byte[] publicKeyBytes = Convert.FromBase64String(publicKeyBase64);
|
|
|
|
bool result = VerifySignature_2048_Bit_PKCS1_v1_5(
|
|
purchaseInfoBytes,
|
|
signatureBytes,
|
|
publicKeyBytes);
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//TraceLog.Exception(ex);
|
|
TraceLog.Error("GooglePaySecurity.CheckSignature, exception message {0} signature {1}", ex.Message, signature);
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
public static RSAParameters GetRsaParameters_2048_Bit_PKCS1_v1_5(byte[] publicKey)
|
|
{
|
|
// From RFC 2313, PKCS #1, Version 1.5:http://tools.ietf.org/html/rfc2313
|
|
// 7.1 Public-key syntax
|
|
//
|
|
// An RSA public key shall have ASN.1 type RSAPublicKey:
|
|
//
|
|
// RSAPublicKey ::= SEQUENCE {
|
|
// modulus INTEGER, -- n
|
|
// publicExponent INTEGER -- e }
|
|
//
|
|
// (This type is specified in X.509 and is retained here for
|
|
// compatibility.)
|
|
//
|
|
// The fields of type RSAPublicKey have the following meanings:
|
|
//
|
|
// o modulus is the modulus n.
|
|
//
|
|
// o publicExponent is the public exponent e.
|
|
//
|
|
|
|
// BER Encoding
|
|
// http://en.wikipedia.org/wiki/Distinguished_Encoding_Rules#DER_encoding
|
|
//
|
|
// ASN.1 Format with DER (subset of BER) encoding
|
|
// http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
|
|
|
// It's important to know that the RSAPublicKey is encoded in an ASN.1 (Abstract Syntax Notation One)
|
|
// representation using DER encoding. I had to use a couple articles on Wikipedia to understand
|
|
// ASN.1 and then I manually decoded the public key to determine where the modulus and exponent were
|
|
// located within the 2048 bit public key from Google.
|
|
//
|
|
// Bytes of sample 2048 bit Public Key (hexadecimal) with ASN.1 decoding shown for each byte
|
|
// 30 Identifier: 30 hex = 00110000, P/C = Constructed (1), TAG = SEQUENCE (10000)
|
|
// 82 Length: 82 hex = 130 decimal = 10000010, Long Form Length with 2 octects for length
|
|
// 01 Byte 1/2 of long form length
|
|
// 22 Byte 2/2 of long form length, 0x01 0x22, 00000001 00100010 = 290 bytes
|
|
// 30 Identifier: 30 hex = 00110000, P/C = Constructed (1), TAG = SEQUENCE (10000)
|
|
// 0d Length: 0d hex = 13 decimal
|
|
// 06 Identifier: 06 hex = 00000110, P/C = Primitive (0), TAG = OBJECT IDENTIFIER (00110)
|
|
// 09 Length: 09 hex = 9 decimal
|
|
// 2a Byte 1/9 of OBJECT IDENTIFIER
|
|
// 86 Byte 2/9 of OBJECT IDENTIFIER
|
|
// 48 Byte 3/9 of OBJECT IDENTIFIER
|
|
// 86 Byte 4/9 of OBJECT IDENTIFIER
|
|
// f7 Byte 5/9 of OBJECT IDENTIFIER
|
|
// 0d Byte 6/9 of OBJECT IDENTIFIER
|
|
// 01 Byte 7/9 of OBJECT IDENTIFIER
|
|
// 01 Byte 8/9 of OBJECT IDENTIFIER
|
|
// 01 Byte 9/9 of OBJECT IDENTIFIER
|
|
// 05 Identifier: 05 hex = 00000101, P/C = Primitive (0), TAG = NULL (00101)
|
|
// 00 Length: 00 hex = 0 decimal
|
|
// 03 Identifier: 03 hex = 00000011, P/C = Primitive (0), TAG = BIT STRING (00011)
|
|
// 82 Length: 82 hex = 130 decimal = 10000010, Long Form Length with 2 octects for length
|
|
// 01 Byte 1/2 of long form length
|
|
// 0f Byte 2/2 of long form length, 0x01 0x0f, 00000001 00010000 = 272 bytes
|
|
// 00 ???? Why 0, what does this mean?
|
|
// 30 Identifier: 30 hex = 00110000, P/C = Constructed (1), TAG = SEQUENCE (10000)
|
|
// 82 Length: 82 hex = 130 decimal = 10000010, Long Form Length with 2 octects for length
|
|
// 01 Byte 1/2 of long form length
|
|
// 0a Byte 2/2 of long form length, 0x01 0x0a, 00000001 00001010 = 266 bytes
|
|
// 02 Identifier: 02 hex = 00000010, P/C = Primitive (0), TAG = INTEGER (00010)
|
|
// 82 Length: 82 hex = 130 decimal = 10000010, Long Form Length with 2 octects for length
|
|
// 01 Byte 1/2 of long form length
|
|
// 01 Byte 2/2 of long form length, 0x01 0x01, 00000001 00000001 = 257 bytes
|
|
// 00 Byte 1/257 of modulus (padded left with a 0, leaves 256 actual values)
|
|
// a9 Byte 2/257 of modulus... public key (modulus) starts here??
|
|
// 87 Byte 3/257 of modulus
|
|
// ....
|
|
// 8f Byte 255/257 of modulus
|
|
// 14 Byte 256/257 of modulus93 Byte 257/257 of modulus
|
|
// 02 Identifier: 02 hex = 00000010, P/C = Primitive (0), TAG = INTEGER (00010)
|
|
// 03 Length: 03 hex = 3 decimal
|
|
// 01 Byte 1/3 of exponent
|
|
// 00 Byte 2/3 of exponent
|
|
// 01 Byte 3/3 of exponent
|
|
|
|
// Modulus starts at byte offset 33 and is 2048 bits (256 bytes)
|
|
// Exponent starts at byte offset 291 and is 3 bytes
|
|
|
|
RSAParameters rsaParameters = new RSAParameters();
|
|
|
|
int modulusOffset = 33; // See comments above
|
|
int modulusBytes = 256; // 2048 bit key
|
|
int exponentOffset = 291; // See comments above
|
|
int exponentBytes = 3; // See comments above
|
|
|
|
byte[] modulus = new byte[modulusBytes];
|
|
for (int i = 0; i < modulusBytes; i++)
|
|
modulus[i] = publicKey[modulusOffset + i];
|
|
|
|
byte[] exponent = new byte[exponentBytes];
|
|
for (int i = 0; i < exponentBytes; i++)
|
|
exponent[i] = publicKey[exponentOffset + i];
|
|
|
|
rsaParameters.Modulus = modulus;
|
|
rsaParameters.Exponent = exponent;
|
|
|
|
return rsaParameters;
|
|
}
|
|
|
|
public static bool VerifySignature_2048_Bit_PKCS1_v1_5(byte[] data, byte[] signature, byte[] publicKey)
|
|
{
|
|
// Links for information about PKCS #1 version 1.5:
|
|
// RFC 2313: http://tools.ietf.org/html/rfc2313
|
|
// PKCS #1 on Wikipedia: http://en.wikipedia.org/wiki/PKCS_1
|
|
|
|
|
|
// Compute an SHA1 hash of the raw data
|
|
SHA1 sha1 = SHA1.Create();
|
|
byte[] hash = sha1.ComputeHash(data);
|
|
|
|
// Specify the public key
|
|
RSAParameters rsaParameters = GetRsaParameters_2048_Bit_PKCS1_v1_5(publicKey);
|
|
|
|
// Use RSACryptoProvider to verify the signature with the public key
|
|
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
|
|
rsa.ImportParameters(rsaParameters);
|
|
|
|
RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
|
|
rsaDeformatter.SetHashAlgorithm("SHA1");
|
|
return rsaDeformatter.VerifySignature(hash, signature);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据purchaseData获取google的订单号
|
|
/// </summary>
|
|
/// <param name="purchaseData"></param>
|
|
/// <returns></returns>
|
|
public static string GetOrderId3rdFromPurchaseData(string purchaseData)
|
|
{
|
|
JsonData jsonData = JsonMapper.ToObject(purchaseData);
|
|
if (jsonData == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
JsonData payload = jsonData["developerPayload"];
|
|
if (payload == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
JsonData purchaseState = jsonData["purchaseState"];
|
|
JsonData orderId = jsonData["orderId"];
|
|
JsonData packageName = jsonData["packageName"];
|
|
JsonData productId = jsonData["productId"];
|
|
JsonData purchaseTime = jsonData["purchaseTime"];
|
|
JsonData token = jsonData["token"];
|
|
if (token != null)
|
|
{
|
|
JsonData purchaseToken = token["purchaseToken"];
|
|
}
|
|
|
|
if(orderId == null)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
return orderId.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据purchaseData获取我们自己的订单号,不是google的订单号,是payload
|
|
/// </summary>
|
|
/// <param name="purchaseData"></param>
|
|
/// <returns></returns>
|
|
public static string GetOrderIdFromPurchaseData(string purchaseData)
|
|
{
|
|
JsonData jsonData = JsonMapper.ToObject(purchaseData);
|
|
if (jsonData == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
JsonData payload = jsonData["developerPayload"];
|
|
if (payload == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return payload.ToString();
|
|
}
|
|
}
|
|
}
|
|
|