在 .Net 中验证 Google Play 应用内计费签名 - 2048 位密钥,PKCS #1 v1.5

8
我花了一些时间才弄清楚如何在ASP.NET中验证Google Play In-App计费签名,因此我想在StackOverflow上分享我的做法。
正如实施应用内计费 (IAB 版本 3)所述:
为了确保向您的应用发送的交易信息的完整性,Google Play 对包含购买订单响应数据的 JSON 字符串进行签名。Google Play 使用与您的应用在开发者控制台中关联的私钥创建此签名。开发者控制台为每个应用程序生成 RSA 密钥对。
注意:要查找此密钥对的公共密钥部分,请在开发者控制台中打开您应用的详细信息,然后单击“服务和 API”,并查看标题为“此应用的许可证密钥”的字段。
由Google Play生成的Base64编码的RSA公钥是二进制编码、X.509 subjectPublicKeyInfo DER SEQUENCE格式的。这是与Google Play许可一起使用的相同的公钥。
当您的应用收到此签名响应时,您可以使用您RSA密钥对的公钥部分来验证签名。通过执行签名验证,您可以检测到已篡改或伪造的响应。您可以在应用程序中执行此签名验证步骤;但是,如果您的应用程序连接到安全远程服务器,则建议您在该服务器上执行签名验证。
我将自己回答这个问题,提供我编写的代码来解码DER序列格式中的二进制编码、X.509公钥,并使用.Net RSA API验证签名。
1个回答

21

Google Play使用PKCS #1版本1.5签名购买信息(包括开发人员有效负载)。要验证签名,您需要签名(已签购买信息),原始购买信息文本和公钥。在Google Play开发者控制台的“服务和API设置”中可以找到公钥。

公钥采用DER编码的ASN.1格式(是BER的子集)。

假设购买信息和签名都作为字符串发布到您的Web表单或MVC控制器,并且公钥可在从数据库或web.config加载的字符串中使用,其中购买信息为原始数据,签名和密钥均为Base64编码,此代码将它们转换为字节数组并调用方法验证签名:

            byte[] purchaseInfoBytes = Encoding.UTF8.GetBytes(purchaseInfo);
            byte[] signatureBytes = Convert.FromBase64String(signature);
            byte[] publicKeyBytes = Convert.FromBase64String(publicKey);

            result = CryptUtil.VerifySignature_2048_Bit_PKCS1_v1_5(
                purchaseInfoBytes,
                signatureBytes,
                publicKeyBytes);

这里是一个简单类的代码,用于解码公钥和验证签名。类内的注释解释了如何解码签名,并包含有关 ASN.1、PKCS #1 version 1.5 和 DER (BER) 编码的有用文章链接。ASN.1PKCS #1 version 1.5DER (BER) 编码

using System;
using System.Security.Cryptography;
using System.Text;

namespace YourNamespace.Cryptography
{
    public static class CryptUtil
    {
        public static RSAParameters GetRsaParameters_2048_Bit_PKCS1_v1_5(byte[] publicKey)
        {
            // From RFC 2313, PKCS #1, Version 1.5:https://www.rfc-editor.org/rfc/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: https://www.rfc-editor.org/rfc/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);
        }

    }
}

或者如果您更喜欢LINQ: new RSAParameters { Modulus = publicKey.Skip(33).Take(256).ToArray(), Exponent = publicKey.Skip(291).Take(3).ToArray() } - Johan Levin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接