我有一个Android应用程序,它提供应用程序内的计费,我们有我们的应用程序服务器,android应用程序连接到用户提供服务,在应用程序内购买我们想将收据推送到服务器进行验证过程。
现在问题是我不知道如何在dot net(C#)中转换Security.java文件,因为我们的服务器是用dot net编写的
注意:此文件附带了android应用程序内计费相同的应用程序,它提供了消息签名功能,我只需要它们在dot net中的等价物。
有关此问题的详细信息,请访问: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/66bb5683-fde6-47ca-92d7-de255cc8655a
答案 0 :(得分:5)
这是来自Checking Google Play Signatures on .Net的纯C#实现。
创建一个控制台应用程序项目,将公钥转换为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);
}
答案 1 :(得分:3)
我找到了解决方案,为了实现你首先要将公钥格式转换为dot net,使用不同的Key作为输入。
我不知道其他方法,但我们可以使用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;
}
现在要验证数字签名,您可以在点网程序(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));
}
}
我希望它对每个人都有用,对我有用。感谢
答案 2 :(得分:3)
对于所有需要验证签名的人来说,这是一个完整的c#实现,使用BouncyCastle dll来提供帮助。
如果您使用Google公钥提供课程,则可以验证签名,而无需任何Java代码。玩得开心。 grtz Martien
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));
}
}
}
}
答案 3 :(得分:1)
对于peeps搜索的FYI,这是完整的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项目并运行为带有断点的java app(int i = 0;)来提取你的密钥,代码不是我的,只是由我提供,道具给原作者,链接上面
和VB.NET
答案 4 :(得分:0)
我基于BouncyCastle C#nuget的解决方案。
将消息,签名和密钥替换为您的消息并对其进行测试。无需Java即可获取模量或指数。
[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>));
}
答案 5 :(得分:0)
对于可能仍然感兴趣的任何人,这里有一个纯 C# 解决方案(使用 .NET 5.0 测试):
public static bool IsSignatureValid(string message,
string signature, string publicKey)
{
try {
var publicKeyBytes = Convert.FromBase64String(
publicKey);
var rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(
publicKeyBytes, out var _);
using var rsaProvider = new RSACryptoServiceProvider();
rsaProvider.ImportParameters(
rsa.ExportParameters(false));
return rsaProvider.VerifyData(
Encoding.UTF8.GetBytes(message),
HashAlgorithmName.SHA256.Name,
Convert.FromBase64String(signature));
} catch (Exception ex) {
Console.WriteLine(ex);
return false;
}
}