Android应用内计费Dot Net中收据验证(C#)

问题描述 投票:0回答:6

我有一个提供应用内计费的Android应用程序,我们有一个应用程序服务器,Android应用程序连接到该服务器为用户提供服务,在应用程序内购买时,我们希望将收据推送到服务器以进行验证过程。

现在的问题是我不知道如何在 dot net(C#) 中转换 Security.java 文件,因为我们的服务器是用 dot net 编写的

注意:此文件附带 Android 应用内计费相同的应用程序,该应用程序提供消息签名功能,我只需要在 .net 中使用它们的等效功能。

有关此问题的更多详细信息,请访问 http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/66bb5683-fde6-47ca-92d7-de255cc8655a

android .net cryptography in-app-purchase
6个回答
8
投票

这是一个纯 C# 实现,来自 Checking Google Play Signatures on .Net

创建一个控制台应用程序项目,将公钥转换为

RSACryptoServiceProvider
期望的 XML 格式。将 PEMKeyLoader.cs 添加到控制台应用程序项目。

using PublicKeyConvert;
using System.Security.Cryptography;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            RSACryptoServiceProvider provider = PEMKeyLoader.CryptoServiceProviderFromPublicKeyInfo(MY_BASE64_PUBLIC_KEY);
            System.Console.WriteLine(provider.ToXmlString(false));
        }

        const string MY_BASE64_PUBLIC_KEY = "Paste your base64 Google public key here.";
    }
}

运行该控制台应用程序将输出(到控制台)

RSACryptoServiceProvider
期望的 XML 格式。

现在您已经有了 XML 格式的公钥,您可以使用它来验证签名:

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);
}

3
投票

我找到了解决方案,要实现这一点,您首先必须转换公钥格式,因为点网使用不同的密钥作为输入。

我不知道其他方法,但我们可以使用 java 代码获取点网格式密钥,您只需运行一次即可生成点网友好的 RSA 公钥。 (仅当给定的公众不会快速变化时才建议这样做,例如在 Android 市场应用内计费的情况下)

以下 Java 代码对我有用

public static DotNetRSA GenerateDotNetKey(String base64PubKey)
            throws IOException, NoSuchAlgorithmException,
            InvalidKeySpecException {
        /*
         * String base64PubKey - 
         * Is a Key retrieved from Google Checkout Merchant Account
         */
        BASE64Decoder decoder = new BASE64Decoder();

        byte[] publicKeyBytes = decoder.decodeBuffer(base64PubKey);

        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
        RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);

        byte[] modulusBytes = publicKey.getModulus().toByteArray();
        byte[] exponentBytes = publicKey.getPublicExponent().toByteArray();

        modulusBytes = stripLeadingZeros(modulusBytes);

        BASE64Encoder encoder = new BASE64Encoder();
        String modulusB64 = encoder.encode(modulusBytes);
        String exponentB64 = encoder.encode(exponentBytes);

        return new DotNetRSA(modulusB64, exponentB64);
    }

      private static byte[] stripLeadingZeros(byte[] a) {
        int lastZero = -1;
        for (int i = 0; i < a.length; i++) {
          if (a[i] == 0) {
            lastZero = i;
          }
          else {
            break;
          }
        }
        lastZero++;
        byte[] result = new byte[a.length - lastZero];
        System.arraycopy(a, lastZero, result, 0, result.length);
        return result;
      }

现在要验证数字签名,您可以在 dot net 程序(c#)中使用以下代码,前提是 GCHO_PUB_KEY_EXP 是您的指数,GCHO_PUB_KEY_MOD 是上述 Java 代码提取的模数

public static bool VerifyDataSingature(string data, string sign)
{
     using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
     {
         RSAParameters rsaKeyInfo = new RSAParameters() 
         { 
             Exponent = Convert.FromBase64String(GCHO_PUB_KEY_EXP), 
             Modulus = Convert.FromBase64String(GCHO_PUB_KEY_MOD) 
         };
         rsa.ImportParameters(rsaKeyInfo);

         return rsa.VerifyData(Encoding.ASCII.GetBytes(data), 
                               "SHA1", 
                               Convert.FromBase64String(sign));
     }
}

我希望它对每个人都有效,就像对我一样。谢谢

归功于代码项目文章


3
投票

对于所有需要验证签名的人来说,这里有一个完整的 C# 实现,使用 BouncyCastle dll 来提供帮助。

如果您向班级提供 Google 公钥,您将能够验证签名,而无需任何 Java 代码。玩得开心。 grtz 马丁

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using System.Security.Cryptography;

namespace GoogleEncryptTest
{
    class GoogleSignatureVerify
{
    RSAParameters _rsaKeyInfo;

    public GoogleSignatureVerify(String GooglePublicKey)
    {
        RsaKeyParameters rsaParameters= (RsaKeyParameters) PublicKeyFactory.CreateKey(Convert.FromBase64String(GooglePublicKey)); 

        byte[] rsaExp   = rsaParameters.Exponent.ToByteArray();
        byte[] Modulus  = rsaParameters.Modulus.ToByteArray();

        // Microsoft RSAParameters modulo wants leading zero's removed so create new array with leading zero's removed
        int Pos = 0;
        for (int i=0; i<Modulus.Length; i++)
        {
            if (Modulus[i] == 0) 
            {
                Pos++;
            }
            else
            {
                break;
            }
        }
        byte[] rsaMod = new byte[Modulus.Length-Pos];
        Array.Copy(Modulus,Pos,rsaMod,0,Modulus.Length-Pos);

        // Fill the Microsoft parameters
        _rsaKeyInfo = new RSAParameters()
        {
            Exponent    = rsaExp,
            Modulus     = rsaMod
        };
    }

    public bool Verify(String Message,String Signature)
    {
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {      
            rsa.ImportParameters(_rsaKeyInfo);  
            return rsa.VerifyData(Encoding.ASCII.GetBytes(Message), "SHA1", Convert.FromBase64String(Signature));  
        }           
    }
}
}

1
投票

供大家搜索,这里是完整的Java文件,以及VB.NET中的函数。



/**
 * <p>Title: RSA Security</p>
 * Description: This class generates a RSA private and public key, reinstantiates
 * the keys from the corresponding key files.It also generates compatible .Net Public Key,
 * which we will read later in C# program using .Net Securtiy Framework
 * The reinstantiated keys are used to sign and verify the given data.</p>
 *
 * @author Shaheryar
 * @version 1.0
 */

import java.security.*;
import java.security.spec.*;
import java.io.*;
import java.security.interfaces.*;
import java.security.cert.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class SecurityManager {

  private KeyPairGenerator keyGen; //Key pair generator for RSA
  private PrivateKey privateKey; // Private Key Class
  private PublicKey publicKey; // Public Key Class
  private KeyPair keypair; // KeyPair Class
  private Signature sign; // Signature, used to sign the data
  private String PRIVATE_KEY_FILE; // Private key file.
  private String PUBLIC_KEY_FILE; // Public key file.
  private String DOT_NET_PUBLIC_KEY_FILE; // File to store .Net Compatible Key Data

  /**
   * Default Constructor. Instantiates the key paths and signature algorithm.
 * @throws IOException 
 * @throws InvalidKeySpecException 
 * @throws NoSuchAlgorithmException 
   */
  public SecurityManager() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {

  }


  public static void main(String args[]) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException{
      GenerateDotNetKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp6340BNzismmb/n98sTcYfNEmmzNGumdWnK1e7NNWntM6mjZMnQaVZ9HiJKmMgtn69dAU4gaMVUWACDsuup1GBxN8dLgDbtR26M0u1jf1G8AQehcKfqxqSYzxKquXXotffdYsJPpjseZbi96Y7j47kz9CjNP3y1BzjJNTWQUx9fc9e2Bpsi0GtqJ8porPBuIGTjcCnlKM14tIv6YlHtECW1L1wcOBkoj/5liI1nhlYDth/DNXg1OY11JqIIP1fO2vQPtKEpdtcTBTjmB9M45O1N8K/shTcMntFjwVTpL0hRd+eaN1bUjpMvrhFik0VcF/ZNN6Hn0Coqe+ey18dLosQIDAQAB");
  }
  public static void GenerateDotNetKey(String base64PubKey)
          throws IOException, NoSuchAlgorithmException,
          InvalidKeySpecException {
      /*
       * String base64PubKey - 
       * Is a Key retrieved from Google Checkout Merchant Account
       */
      BASE64Decoder decoder = new BASE64Decoder();

      byte[] publicKeyBytes = decoder.decodeBuffer(base64PubKey);

      EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
      RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);

      byte[] modulusBytes = publicKey.getModulus().toByteArray();
      byte[] exponentBytes = publicKey.getPublicExponent().toByteArray();

      modulusBytes = stripLeadingZeros1(modulusBytes);

      BASE64Encoder encoder = new BASE64Encoder();
      String modulusB64 = encoder.encode(modulusBytes);
      String exponentB64 = encoder.encode(exponentBytes);
int i=0;
     // return new DotNetRSA(modulusB64, exponentB64);
  }

    private static byte[] stripLeadingZeros1(byte[] a) {
      int lastZero = -1;
      for (int i = 0; i < a.length; i++) {
        if (a[i] == 0) {
          lastZero = i;
        }
        else {
          break;
        }
      }
      lastZero++;
      byte[] result = new byte[a.length - lastZero];
      System.arraycopy(a, lastZero, result, 0, result.length);
      return result;
    }


  }

/**
 * <p>Title: RSA Security</p>
 * Description: This class generates a RSA private and public key, reinstantiates
 * the keys from the corresponding key files.It also generates compatible .Net Public Key,
 * which we will read later in C# program using .Net Securtiy Framework
 * The reinstantiated keys are used to sign and verify the given data.</p>
 *
 * @author Shaheryar
 * @version 1.0
 */

import java.security.*;
import java.security.spec.*;
import java.io.*;
import java.security.interfaces.*;
import java.security.cert.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class SecurityManager {

  private KeyPairGenerator keyGen; //Key pair generator for RSA
  private PrivateKey privateKey; // Private Key Class
  private PublicKey publicKey; // Public Key Class
  private KeyPair keypair; // KeyPair Class
  private Signature sign; // Signature, used to sign the data
  private String PRIVATE_KEY_FILE; // Private key file.
  private String PUBLIC_KEY_FILE; // Public key file.
  private String DOT_NET_PUBLIC_KEY_FILE; // File to store .Net Compatible Key Data

  /**
   * Default Constructor. Instantiates the key paths and signature algorithm.
 * @throws IOException 
 * @throws InvalidKeySpecException 
 * @throws NoSuchAlgorithmException 
   */
  public SecurityManager() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {

  }


  public static void main(String args[]) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException{
      GenerateDotNetKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp6340BNzismmb/n98sTcYfNEmmzNGumdWnK1e7NNWntM6mjZMnQaVZ9HiJKmMgtn69dAU4gaMVUWACDsuup1GBxN8dLgDbtR26M0u1jf1G8AQehcKfqxqSYzxKquXXotffdYsJPpjseZbi96Y7j47kz9CjNP3y1BzjJNTWQUx9fc9e2Bpsi0GtqJ8porPBuIGTjcCnlKM14tIv6YlHtECW1L1wcOBkoj/5liI1nhlYDth/DNXg1OY11JqIIP1fO2vQPtKEpdtcTBTjmB9M45O1N8K/shTcMntFjwVTpL0hRd+eaN1bUjpMvrhFik0VcF/ZNN6Hn0Coqe+ey18dLosQIDAQAB");
  }
  public static void GenerateDotNetKey(String base64PubKey)
          throws IOException, NoSuchAlgorithmException,
          InvalidKeySpecException {
      /*
       * String base64PubKey - 
       * Is a Key retrieved from Google Checkout Merchant Account
       */
      BASE64Decoder decoder = new BASE64Decoder();

      byte[] publicKeyBytes = decoder.decodeBuffer(base64PubKey);

      EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
      RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);

      byte[] modulusBytes = publicKey.getModulus().toByteArray();
      byte[] exponentBytes = publicKey.getPublicExponent().toByteArray();

      modulusBytes = stripLeadingZeros1(modulusBytes);

      BASE64Encoder encoder = new BASE64Encoder();
      String modulusB64 = encoder.encode(modulusBytes);
      String exponentB64 = encoder.encode(exponentBytes);
int i=0;
     // return new DotNetRSA(modulusB64, exponentB64);
  }

    private static byte[] stripLeadingZeros1(byte[] a) {
      int lastZero = -1;
      for (int i = 0; i < a.length; i++) {
        if (a[i] == 0) {
          lastZero = i;
        }
        else {
          break;
        }
      }
      lastZero++;
      byte[] result = new byte[a.length - lastZero];
      System.arraycopy(a, lastZero, result, 0, result.length);
      return result;
    }


  }

只需添加到一个新的Java项目并作为带有断点(int i = 0;)的java应用程序运行来提取你的密钥,代码不是我的,只是我的,支持原作者,上面的链接

和 VB.NET


0
投票

我的解决方案基于 BouncyCastle C# nuget。

将消息、签名和密钥替换为您的消息、签名和密钥并进行测试。不需要 java 来获取模数或指数。

Private Function VerifyDataSignature(ByVal data As String, ByVal sign As String) As Boolean
    Using rsa As New RSACryptoServiceProvider()
        Dim rsaKeyInfo As RSAParameters = New RSAParameters()
        rsaKeyInfo.Exponent = Convert.FromBase64String("ExponentFromJava")
        rsaKeyInfo.Modulus = Convert.FromBase64String("ModulusFromJava")
        rsa.ImportParameters(rsaKeyInfo)
        Return rsa.VerifyData(Encoding.ASCII.GetBytes(data), "SHA1", Convert.FromBase64String(sign))
    End Using
End Function

0
投票

对于任何可能仍然感兴趣的人,这里有一个纯 C# 解决方案(使用 .NET 5.0 测试):

[TestMethod]
public void ValidadeMessageTest()
{
    //Base64-encoded RSA public key obtained from Google PlayStore, for the app. Go to DevelomentTools->Service & APIs
    var GooglePlayPK = "<put your key here>";

    bool validateReceipt(String message,String  messageSignature)
    {
        const String SIGNATURE_ALGORITHM = "SHA1";

        var rsaParameters = new RSAParameters();
        byte[] publicKeyBytes = Convert.FromBase64String(GooglePlayPK);
        AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes);
        RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
        rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
        rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();

        using (var rsa = new RSACryptoServiceProvider())
        {
            var encoder = new ASCIIEncoding();
            byte[] bytesToVerify = encoder.GetBytes(message);
            byte[] signedBytes = Convert.FromBase64String(messageSignature);

                rsa.ImportParameters(rsaParameters);
                return  rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID(SIGNATURE_ALGORITHM), signedBytes);                   
        }        
    }
    //test your receipt
    Assert.IsTrue(validateReceipt(<original>, <signature>));
}
© www.soinside.com 2019 - 2024. All rights reserved.