Android In App Purchase Signature Validation - c#

I am using Xamarin.Android and use the BillingClient for In App Purchases. I use the following code to successfully verify the originalJson signature of a purchase:
private bool ClientSignatureValid(string originalJson, string signature, string key)
{
var success = false;
try
{
var decodedSignature = Android.Util.Base64.Decode(signature, 0);
var decodedData = System.Text.Encoding.UTF8.GetBytes(originalJson);
var decodedPublicKey = Android.Util.Base64.Decode(key, 0);
var keyFactory = Java.Security.KeyFactory.GetInstance("RSA");
var sign = Signature.GetInstance("SHA1withRSA");
var keySpec = new Java.Security.Spec.X509EncodedKeySpec(decodedPublicKey);
var key = keyFactory.GeneratePublic(keySpec);
sign.InitVerify(key);
sign.Update(System.Text.Encoding.UTF8.GetBytes(originalJson));
success = sign.Verify(decodedSignature);
}
catch(Exception e)
{
logger?.LogInformation($"Failed to verify android purchase data: {e.Message}");
}
return success;
}
I want to implement the same signature validation on my server and don't have access to the same java packages to reuse the same code. I have tried converting to use System.Security.Cryptography but the code always fails to validate the signature.
The code looks like this:
public bool ServerSignatureValid(string originalJson, string signature, string key)
{
var success = false;
try
{
var decodedSignature = System.Convert.FromBase64String(signature);
var decodedData = System.Text.Encoding.UTF8.GetBytes(originalJson);
var decodedPublicKey = System.Convert.FromBase64String(key);
var rsa = System.Security.Cryptography.RSA.Create();
rsa.ImportSubjectPublicKeyInfo(decodedPublicKey, out _);
success = rsa.VerifyData(
decodedData,
decodedSignature,
System.Security.Cryptography.HashAlgorithmName.SHA1,
System.Security.Cryptography.RSASignaturePadding.Pkcs1);
}
catch (Exception e)
{
logger?.LogInformation($"Failed to verify android signature data: {e.Message}");
}
return success;
}
The only definition from Google that I can find is here and only goes as far as specifying the RSASSA-PKCS1-v1_5 scheme.
The parameters looks something like this:
string key = #"MIIBIjANBgkq...";
string originalJson = "{\"productId\":\"uk.co...\"";
string signature = "LQC7ps7Db577I8Iq...";
I have tried various combinations and can't seem to get the validation to pass on the Server. What have I done wrong?
Any help appreciated.

I was using the wrong key, it was for a different app. The code is fine.

Related

How to translate name using Google Translate in C#?

I'm working on a project (Names Translator ) in C#.
This is my code:
public String Translate(String word)
{
var toLanguage = "en";//English
var fromLanguage = "ar";//Deutsch
var url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={HttpUtility.UrlEncode(word)}";
var webClient = new WebClient
{
Encoding = System.Text.Encoding.UTF8
};
var result = webClient.DownloadString(url);
try
{
result = result.Substring(4, result.IndexOf("\"", 4, StringComparison.Ordinal) - 4);
return result;
}
catch
{
return "Error";
}
and it works for most names,
but sometimes Google Translate translates the names literally,
for example
string result=Translate("خوله محمد احمد");
The result will be
He was authorized by Mohamed Ahmed
"خوله" =he was authorized
On the Google Translate website it gives me the same wrong translation:
But as you notice from the picture "khwlh muhamad ahmad" next to red arrow is what I want!
How can I achieve this?

Signature Validation for a SAML Authn Request via GET method fails

I am sending a SIGNED authnRequest to the idp using c# and asp.net. My code signs the authnRequest but the signature validation fails at idp.
Details
I tried a lot of solutions but in vain.
This is what i am doing following guidlines set by https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf:
STEPS
Deflate the auth request, then base64 encode it and finally Url Encode it. Lets call it AR
Url encode the RelayState. Lets call it RS
Url encode the signing Algorithm string. Lets call it SA
So the string to be signed now becomes
SAMLRequest=AR&RelayState=RS&SigAlg=SA
Now i sign the string we get in step #4 using our private key (service provider private key).
6.The resultant signature that i get, i base 64 encode it, and then URL encode it. Thus i get a base64 and url encoded signature. Lets call it SG
Now i append the signature we got in step #6 to the querystring in step #4. So the final querystring becomes
SAMLRequest=AR&RelayState=RS&SigAlg=SA&Signature=SG
All this works fine but the signature validation is failing !
Here's my code which is similar to the code found here https://github.com/Sustainsys/Saml2/blob/v0.21.2/Kentor.AuthServices/WebSSO/Saml2RedirectBinding.cs#L53-L68
protected void btnSendAuthRequest_Click(object sender, EventArgs e)
{
string authRequest = txtInput.Text;
//authRequest = authRequest.TrimEnd('\r', '\n');
authRequest = DeflateBase64UrlEncode(authRequest);
string spPrivateKey= txtKey.Text;
string relayState = HttpUtility.UrlEncode("https://example.com/pages/home.aspx");
string qs = "SAMLRequest=" + authRequest + "&RelayState=" + relayState;
qs = AddSignature(qs, spPrivateKey);
txtOutput.Text = qs;
}
public string AddSignature(string queryString, string PrivateKeyNoHeaders)
{
RSACryptoServiceProvider tmpRsa = RSAKeyTests.RSAKeyUtils.DecodePrivateKeyInfo(Convert.FromBase64String(PrivateKeyNoHeaders));
string signingAlgorithmUrl = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
queryString += "&SigAlg=" + HttpUtility.UrlEncode(signingAlgorithmUrl);
var signatureDescription = (SignatureDescription)CryptoConfig.CreateFromName(signingAlgorithmUrl);
HashAlgorithm hashAlg = signatureDescription.CreateDigest();
hashAlg.ComputeHash(Encoding.UTF8.GetBytes(queryString));
AsymmetricSignatureFormatter asymmetricSignatureFormatter =
signatureDescription.CreateFormatter(
((RSACryptoServiceProvider)tmpRsa));
//.GetSha256EnabledRSACryptoServiceProvider());
// Is the signature failing because of above ?
byte[] signatureValue = asymmetricSignatureFormatter.CreateSignature(hashAlg);
queryString += "&Signature=" + HttpUtility.UrlEncode(Convert.ToBase64String(signatureValue));
return queryString;
}
private string DeflateBase64UrlEncode(string input)
{
var inputs = string.Format(input, Guid.NewGuid());
var bytes = Encoding.UTF8.GetBytes(inputs);
using (var output = new MemoryStream())
{
using (var zip = new DeflateStream(output, CompressionMode.Compress))
{
zip.Write(bytes, 0, bytes.Length);
}
var base64 = Convert.ToBase64String(output.ToArray());
return HttpUtility.UrlEncode(base64);
}
}
CryptoConfig.createFromName(...) doesn't know about http://www.w3.org/2000/09/xmldsig#rsa-sha1 as the digest+signing algorithm. If CryptoConfig.createFromName() is not returning null, whatever algorithm is registered for http://www.w3.org/2000/09/xmldsig#rsa-sha1 might not be RSA-SHA1. Here's an explicit implementation of SignatureDescription with RSA and SHA1:
public class RSASHA1SignatureDescription : SignatureDescription {
public RSASHA1SignatureDescription() {
KeyAlgorithm = "System.Security.Cryptography.RSA";
DigestAlgorithm = "System.Security.Cryptography.SHA1Cng";
FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter";
DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter";
_hashAlgorithm = "SHA1";
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key) {
AsymmetricSignatureDeformatter item = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
item.setKey(key);
item.SetHashAlgorithm(_hashAlgorithm);
return item;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key) {
AsymmetricSignatureFormatter item = (AsymmetricSignatureFormatter) CryptoConfig.CreateFromName(FormatterAlgorithm);
item.setKey(key);
item.SetHashAlgorithm(_hashAlgorithm);
return item;
}
private string _hashAlgorithm;
}
The other possibility is that however you're validating the signature doesn't want rsa-sha1 (many identity providers prohibit rsa-sha1 via configuration) or the validation is incorrect. Try registering with a real IdP such as Okta or Salesforce and validate there.

Instasharper does not return Likers and Tags list

I have this piece of code in C# using InstaSharper to return information of a media id from Instagram.
public async Task<UserSessionData> SignIn(string userName, string password)
{
userSessionData = new UserSessionData();
userSessionData.UserName = "username";
userSessionData.Password = "password";
api = InstaApiBuilder.CreateBuilder()
.SetUser(userSessionData)
.Build();
var loginRequest = await api.LoginAsync();
if (loginRequest.Succeeded && api.IsUserAuthenticated)
{
IResult<InstaUser> userSearch = await api.GetUserAsync(userName);
IResult<InstaMediaList> media = await api.GetUserMediaAsync(userName, PaginationParameters.MaxPagesToLoad(1));
IResult<InstaMedia> mediaInfo = await api.GetMediaByIdAsync("1924613027431050955");
return userSessionData;
}
else
{
return null;
}
}
GetMediaByIdAsync method correctly returns data about the requested media but the Likers and Tags collection is empty. Is there any way to include those data?
Had the same issue, I found the solution.
Try using this code:
var posts = await api.GetUserMediaAsync("username", PaginationParameters.MaxPagesToLoad(Int32.MaxValue)); // If you want to load all of the posts. If not, replace the "Int32.MaxValue" with the amount of posts you want to load
foreach (var post in posts.Value)
{
var likers = await api.GetMediaLikersAsync(post.InstaIdentifier);
foreach (var liker in likers.Value)
{
Console.WriteLine(liker.UserName);
}
}
This works fine for me, try it out, Im sure it will work for you as well.

Why am I getting (400) Bad Request error when connecting to Text Analytics API?

I am new C# developer and I am trying to use Text Analytics API with Azure Machine Learning in my test ASP.NET application to define the sentiment and key phrases for the tweets I have in the database.
I followed this useful article over here to be able to connect to Text Analytics API. While debugging my code, I was able to get the key phrases and sentiment for the first tweet in the list and then I got the following exception and I don't know why:
The remote server returned an error: (400) Bad Request.
Here's my code:
private const string apiKey = "XXXXXXXXX"; //Key 2 value of Text Analytics API from Azure Portal
private const string sentimentUri = "https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment";
private const string keyPhrasesUri = "https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/keyPhrases";
private const string languageUri = "https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/languages";
protected void Page_Load(object sender, EventArgs e)
{
}
private IEnumerable<T_Tweet> GetData()
{
TweetBL tweetBl = new TweetBL();
IEnumerable<T_Tweet> tweetsList = tweetBl.GetTweets();
return tweetsList;
}
protected void lbtnAnalyze_Click(object sender, EventArgs e)
{
var tweetsList = GetData();
if (tweetsList != null)
{
List<TweetSentimentModel> tweetSenti = new List<TweetSentimentModel>();
foreach (var tweet in tweetsList)
{
try
{
// Prepare headers
var client = new WebClient();
client.Headers.Add("Ocp-Apim-Subscription-Key", apiKey);
client.Headers.Add("Content-Type", "application/json");
client.Headers.Add("Accept", "application/json");
// Detect language
var postData1 = #"{""documents"":[{""id"":""1"", ""text"":""#sampleText""}]}".Replace("#sampleText", tweet.TweetText);
var response1 = client.UploadString(languageUri, postData1);
var language = new Regex(#"""iso6391Name"":""(\w+)""").Match(response1).Groups[1].Value;
// Determine sentiment
var postData2 = #"{""documents"":[{""id"":""1"", ""language"":""#language"", ""text"":""#sampleText""}]}".Replace("#sampleText", tweet.TweetText).Replace("#language", language);
var response2 = client.UploadString(sentimentUri, postData2);
var sentimentStr = new Regex(#"""score"":([\d.]+)").Match(response2).Groups[1].Value;
var sentiment = Convert.ToDouble(sentimentStr, System.Globalization.CultureInfo.InvariantCulture);
// Detemine key phrases
var postData3 = postData2;
var response3 = client.UploadString(keyPhrasesUri, postData2);
var keyPhrases = new Regex(#"""keyPhrases"":(\[[^\]]*\])").Match(response3).Groups[1].Value;
TweetSentimentModel tweetSentiObj = new TweetSentimentModel();
tweetSentiObj.TweetId = tweet.Id;
tweetSentiObj.TweetText = tweet.TweetText;
tweetSentiObj.SentimentLabel = sentiment.ToString();
tweetSentiObj.KeyPhrases = keyPhrases;
tweetSentiObj.CreatedOn = DateTime.Now;
tweetSenti.Add(tweetSentiObj);
}
catch (Exception ex)
{
throw ex;
}
}
if (tweetSenti != null)
{
PrintAnalysisResults(tweetSenti);
}
}
}
Could you please tell me why I am getting this error and how I can fix it?
I was able to get the key phrases and sentiment for the first tweet in the list and then I got the following exception and I don't know why:
According to your description, the difference between second time and first time is the tweet object. And (400) Bad Request is also meaning that http request client paramters are not correct.
I can repro it while tweet.TweetText equals null or tweet.TweetText.trim() equals string.empty.
Please have try to debug, if it is that case ,please have a try add logic to control it. The following is the demo code.
if (!string.IsNullOrEmpty(tweet.TweetText?.Trim()))
{
// Determine sentiment
var postData2 = #"{""documents"":[{""id"":""1"", ""language"":""#language"", ""text"":""#sampleText""}]}".Replace(
"#sampleText", tweet.TweetText).Replace("#language", language);
var response2 = client.UploadString(SentimentUri, postData2);
var sentimentStr = new Regex(#"""score"":([\d.]+)").Match(response2).Groups[1].Value;
var sentiment = Convert.ToDouble(sentimentStr, System.Globalization.CultureInfo.InvariantCulture);
// Detemine key phrases
var postData3 = postData2;
var response3 = client.UploadString(KeyPhrasesUri, postData2);
var keyPhrases = new Regex(#"""keyPhrases"":(\[[^\]]*\])").Match(response3).Groups[1].Value;
}

Trying to call public async Task<Boolean> from a public string submit method

I'm pretty new to this Async thing. I'm trying to write in to an API using a createasync method. However, for some reason I either get an aggregate exception, or the thread just hangs depending on what I've tried. Here is a code example because I have yet to be able to successfully write in to the Salesforce API. I have been able however, to pull back data using similar operations.
Submit Method where I want this all to take place in my controller:
[System.Web.Http.HttpPost]
public string Submit([FromBody]SurveyFormModel survey)
{
// todo - Validate data and stop if bad data comes in.
var report = BuildReport(survey.Questions);
try
{
var task = Task.Run(async () => { await SendReportToSalesforce(survey); });
task.Wait();
}
catch (Exception ex)
{
ex.GetBaseException();
}
EmailExcel(survey);
return JsonConvert.SerializeObject(report);
}
This is the method I'm using to call my salesforceservice.cs
public async Task SendReportToSalesforce([FromBody] SurveyFormModel survey)
{
SalesforceService service = new SalesforceService();
var auth = service.Authenticate();
var sfSurvey = service.SalesForceMapper(survey, survey.AccountDetails);
var result = await service.SendSurveytoSF(auth.Result, sfSurvey); //.Wait();
Console.WriteLine(result);
}
Also included is the salesforceservice class I use to authenticate as well as the method (SendSurveytoSF) that doesnt seem to be working.
public async Task<ForceClient> Authenticate()
{
//get credential values
var consumerkey = ConfigurationManager.AppSettings["consumerkey"];
var consumersecret = ConfigurationManager.AppSettings["consumersecret"];
var username = ConfigurationManager.AppSettings["username"];
var password = ConfigurationManager.AppSettings["password"];
//create auth client to retrieve token
var auth = new AuthenticationClient();
//get back URL and token
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
try
{
await
auth.UsernamePasswordAsync(consumerkey, consumersecret, username, password,
"https://test.salesforce.com/services/oauth2/token");
}
catch (Exception ex)
{
return null;
}
var instanceUrl = auth.InstanceUrl;
var accessToken = auth.AccessToken;
var apiVersion = auth.ApiVersion;
return new ForceClient(instanceUrl, accessToken, apiVersion);
}
public async Task<Boolean> SendSurveytoSF(ForceClient sfclient, Models.Salesforce.Survey survey)
{
survey.Account__c = "0012200000Ah3zG";
var response = await sfclient.CreateAsync("VBR_Assessment__c", survey);
return response.Id != null;
}
I believe I'm maybe just not calling these methods properly, but at this point I really have no clue as I've tried implementing it a ton of different ways. Thank you for any help in advanced!
UPDATE
This is the exception I'm getting:
A first chance exception of type 'Salesforce.Common.ForceException' occurred in mscorlib.dll

Categories