Encrypt Json result on WebApi Backend - c#

I finished my web api with data encriptions, but now, i have to encripty the Json result. When i put the querystring on the browser, now i have the answer (example) :
[{"SUSPID":"111","IVNOME":"teste","IVMAE":"teste","IVPAI":"teste","IVDATANASC":"02/07/1970","IVRG":"0000 (IFP)","ICPF":"Não Cadastrado"}]
I cannot show this...i have to show like (ENCRYPTED): [{"SUSPID":"AUAUAUA","IVNOME":"UAUAU","IVMAE":"UAUAU", ......]
I am seeing some examples, but i am not finding one that is what i need
Part of the code on my service (Cliente-side):
var response = await client.GetAsync(urllink);
var JsonResult = response.Content.ReadAsStringAsync().Result;
if (typeof(T) == typeof(string))
return null;
var rootobject = JsonConvert.DeserializeObject<T>(JsonResult);
return rootobject;
And at my controller (web api BackEnd), i return this dataset:
return lretorno.Tables[0].AsEnumerable().Select(row => new Envolvido
{
SUSPID = Convert.ToString(row["SUSPID"]),
IVNOME = Convert.ToString(row["SUSPNOME"]),
IVMAE = Convert.ToString(row["SUSPMAE"]),
IVPAI = Convert.ToString(row["SUSPPAI"]),
IVDATANASC = Convert.ToString(row["SUSPDATANASC"]).Replace(" 00:00:00", ""),
IVRG = Convert.ToString(row["RG"]),
ICPF = Convert.ToString(row["CPF"]),
MANDADO = Convert.ToInt16(row["TEMMANDADO"]),
OCORRENCIA = Convert.ToInt16(row["TEMOCORRENCIA"]),
});
I cannot understand where i have to encripty and where i have to decrypt on the code.

If you really must do some additional encryption on top of https, for instance if you want to help stop automated man-in-the-middle you can do something like the following... this would require the people in between (Government, ISP, Telco, Endpoint network admin's) to do a second man in the middle attack by figuring out specifically how you are passing your extra public key. In addition to this you could also include the "pk" parameter inside your JSON before its encrypted... and then when you decrypt the json you can compare it against the public-key that you sent, if they don't match then for sure there was a man-in-the-middle. I used the built in RSACryptoServiceProvider.
CLIENT-SIDE
// Generate private and public keys (use any asymmetric crypto/key size you want)
RSACryptoServiceProvider rsaKeys = new RSACryptoServiceProvider();
var privateXmlKeys = rsaKeys.ToXmlString(true);
var publicXmlKeys = rsaKeys.ToXmlString(false);
// Make the request for the json data from the server, and also pass along the public xml keys encoded as base64
var response = await http.GetAsync(new Uri(String.Format("https://example.com/data?id=777&pk=\"{0}\"", Convert.ToBase64String(Encoding.ASCII.GetBytes(publicXmlKeys)))));
var encryptedJsonBytes = await response.Content.ReadAsByteArrayAsync();
// Decrypt the bytes using the private key generated earlier
RSACryptoServiceProvider rsaDecrypt = new RSACryptoServiceProvider();
rsaDecrypt.FromXmlString(privateXmlKeys);
byte[] decryptedBytes = rsaDecrypt.Decrypt(encryptedJsonBytes, false);
// Now change from bytes to string
string jsonString = Encoding.ASCII.GetString(decryptedBytes);
// TODO: For extra validation, parse json, get the public key out that the server
// had used to encrypt, and compare with the "pk" you sent "publicXmlKeys",
// if these values do not match there was an attack.
SERVER-SIDE
// Assuming you have your JSON string already
string json = "{\"key\":\"secret_value\"}";
// Get the "pk" request parameter from the http request however you need to
string base64PublicKey = request.getParameter("pk");
string publicXmlKey = Encoding.ASCII.GetString(Convert.FromBase64String(base64PublicKey));
// TODO: If you want the extra validation, insert "publicXmlKey" into the json value before
// converting it to bytes
// var jo = parse(json); jo.pk = publicXmlKey; json = jo.ToString();
// Convert the string to bytes
byte[] jsonBytes = Encoding.ASCII.GetBytes(json);
// Encrypt the json using the public key provided by the client
RSACryptoServiceProvider rsaEncrypt = new RSACryptoServiceProvider();
rsaEncrypt.FromXmlString(publicXmlKey);
byte[] encryptedJsonBytes = rsaEncrypt.Encrypt(jsonBytes, false);
// Send the encrypted json back to the client
return encryptedJsonBytes;
If you need to protect against man in the middle attacks then I suggest you turn off the computer or pre-share the key via a different method not over the internet, phone, or mail and then do not embed it into your application :P Take a look into Off-the-record, End-to-end-encryption and Diffie–Hellman key exchange

Basically I have some generic ideas about this sort of cases for you. If you knows about the Man in the middle attack, only in E2E connection you can rely on your encryption algorithm, That's because only these End-points have the private and public key and the attacker can not spoof, But in these cases(like your case) the attacker can simply have your public key and even your encrypted block which you're trying to send to your webservice, That's because all you have in client-side is in the javascript resources that everyones can read.
So the only solution I can give you is that take your webservices on the HTTPS protocols which normally handle these kind of issues and you don't need to any encryption.
Regards.

Related

Convert Node JS to ASP.NET C# for HMAC_SHA256 Hash

I'm trying to convert this Node JS code to ASP.NET C#:
const crypto = require('crypto');
/**
* Validates a billing service webhook
*
* #param {string} req Node request object, where 'req.body' is a Node
* Buffer object containing the request body
* #param {string} secret the secret string saved in the service App
*/
const isValidSignature = (req, secret) => {
const fsSignature = req.headers['X-FS-Signature'];
const computedSignature = crypto.createHmac('sha256', secret)
.update(req.body)
.digest()
.toString('base64');
return fsSignature === computedSignature;
}
Here is my attempt in C#
private bool CheckNotificationValidContextual(string varRequestHashValue, string varMessageBody) // #2047
{
// This involves a test of the webhook functionality using ngroks and Postman
// to send values that were previously generated from a test webhook from billing service
// to a local development copy of the seller application (running in Visual Studio)
// where the inputs are:
// varRequestHashValue = Request.Headers["X-Fs-Signature"];
// varMessageBody = new System.IO.StreamReader(Request.InputStream).ReadToEnd();
// get the local copy of the webhook secret key from the local web config file
var AMPTK_FSP_HMAC_SHA256_Key = ConfigVal.AMPTK_FSP_HMAC_SHA256();
// convert the local copy of the secret key to a byte array
byte[] AMPTK_keyBytes = Encoding.UTF8.GetBytes(AMPTK_FSP_HMAC_SHA256_Key);
// create a hash object with the local copy of the secret key
var _hashObjectOfLocalKey = new HMACSHA256(AMPTK_keyBytes);
// convert the input webhook message body to a byte array
byte[] _messageBodyByteArray = Encoding.UTF8.GetBytes(varMessageBody);
// create a hash byte array of the message body byte array
// using the hash object based on the local copy of the webhook secret
byte[] _computedMessageHashBytes = _hashObjectOfLocalKey.ComputeHash(_messageBodyByteArray);
// convert the hash byte array of the message body to a string
string _stringOfComputedMessageHashBytes = Encoding.UTF8.GetString(_computedMessageHashBytes, 0, _computedMessageHashBytes.Length);
// remove dashes and convert to lowercase
_stringOfComputedMessageHashBytes= BitConverter.ToString(_computedMessageHashBytes).Replace("-", "").ToLower();
// compare the string of the computed message body hash
// to the received webhook secret hash value from Request.Headers["X-Fs-Signature"]
if (_stringOfComputedMessageHashBytes == varRequestHashValue)
{
return true;
}
else
{
return false;
}
}
The C# code compiles and runs ok.
The result I'm looking for is:
prNdADI26M0ov5x6ZlMr2J2zzB8z2TJRBDy+8gjPttk=
What I'm getting from the C# code "_stringOfComputedMessageHashBytes"
is this:
f37cdae653e167e36c8ed17e44ffa456832dbb7dcec1d00dc1b44a1234965e73
I've checked the inputs carefully (could still be wrong).
Question: have I translated the Node code properly to C#, and if not, how can I improve it? Or what else might be off?
Thanks!
I checked your C# code for computing hash, in general looks ok, you used correct library and correct class HMACSHA256 to calculate HMAC_SHA256 hash. The problem seems on the part that you were trying to convert the computed hash value to a base 64 string. You can use Convert.ToBase64String method to convert your byte array to base 64 string.
Here below you can find your updated C# code(I did minor improvements and updated the code to find _stringOfComputedMessageHashBytes).
I tested the hash results generated from node.js and C#, now they are both generating the same hash result:
private bool CheckNotificationValidContextual(string varRequestHashValue, string varMessageBody) // #2047
{
// This involves a test of the webhook functionality using ngroks and Postman
// to send values that were previously generated from a test webhook from billing service
// to a local development copy of the seller application (running in Visual Studio)
// where the inputs are:
// varRequestHashValue = Request.Headers["X-Fs-Signature"];
// varMessageBody = new System.IO.StreamReader(Request.InputStream).ReadToEnd();
// get the local copy of the webhook secret key from the local web config file
var AMPTK_FSP_HMAC_SHA256_Key = ConfigVal.AMPTK_FSP_HMAC_SHA256();
// convert the local copy of the secret key to a byte array
var AMPTK_keyBytes = Encoding.UTF8.GetBytes(AMPTK_FSP_HMAC_SHA256_Key);
// create a hash object with the local copy of the secret key
using (var _hashObjectOfLocalKey = new HMACSHA256(AMPTK_keyBytes))
{
// convert the input webhook message body to a byte array
var _messageBodyByteArray = Encoding.UTF8.GetBytes(varMessageBody);
// create a hash byte array of the message body byte array
// using the hash object based on the local copy of the webhook secret
var _computedMessageHashBytes = _hashObjectOfLocalKey.ComputeHash(_messageBodyByteArray);
var _stringOfComputedMessageHashBytes = Convert.ToBase64String(_computedMessageHashBytes);
// compare the string of the computed message body hash
// to the received webhook secret hash value from Request.Headers["X-Fs-Signature"]
return _stringOfComputedMessageHashBytes == varRequestHashValue;
}
}

How do I transmit a SecureString (or similar) across the network without exposing the plaintext?

I have an application with a "secret" (eg password)
I don't want to store this secret locally in a user-accessible context file, so I'm planning to retrieve it on-demand from the server over HTTPS
I also don't want it visible in memory (eg from a crash log), for obvious reasons, so I'm storing the secret in a SecureString
However, when I serialise the SecureString, the result just shows the length of the plaintext string, eg {"Length":4}
If I transmit the password in plaintext, though, then it will be visible in the retrieved JSON in memory, even if subsequently store it in a SecureString
Is there any way to serialize a SecureString, or to receive the JSON and convert a plaintext string to a SecureString without needing an intermediate regular String that would be stored in memory?
In this scenario, I have to store/send the actual password, rather than, for example, a one-time-use key as I'd prefer: that's beyond my control here. I need the actual plaintext password to access another service, so the usual "Hash it then compare the hash" doesn't apply either
Here is a worked example of using RSA encryption to send a string to someone whose public key you have. In your question you want the server to send a message (password) to the client and for the client to securely use that password without having to worry about logging etc in the middle. For this to work you need to have the client create the private key file and send the public key file to the server which can then communicate back securely.
There are probably libraries that make this way easier.
[Test]
public void TestEncryption()
{
/////////////// Create Key Files ////////////////
RSACryptoServiceProvider provider = new RSACryptoServiceProvider(4096);
//Create the key files on disk and distribute them to sender / reciever
var publicKey = provider.ToXmlString(false);
var privateKey = provider.ToXmlString(true);
/////////////// Actual Test ////////////////
//send with the public key
byte[] sent = Send("hey",publicKey);
//cannot receive with public key
var ex = Assert.Throws<CryptographicException>(()=>Receive(sent, publicKey));
StringAssert.Contains("Key does not exist",ex.Message);
//but can with private key
Assert.AreEqual("hey", Receive(sent,privateKey));
}
private Byte[] Send(string send, string publicKey)
{
using (RSACryptoServiceProvider rsaSender = new RSACryptoServiceProvider())
{
rsaSender.FromXmlString(publicKey);
return rsaSender.Encrypt(Encoding.ASCII.GetBytes(send), false);
}
}
private object Receive(byte[] sent, string privateKey)
{
using (RSACryptoServiceProvider rsaReceiver = new RSACryptoServiceProvider())
{
rsaReceiver.FromXmlString(privateKey);
return Encoding.ASCII.GetString(rsaReceiver.Decrypt(sent, false));
}
}

Create token with unique ID and data fields

I am using ASP.NET Core 1.1 and I need to create an unique token with data.
So each token will be composed by: UniqueID + Data1 + Data2 + ... + DataN.
The UniqueId is a Guid and the Data objects can be types like Int32, String, etc:
DateTime expires = DateTime.Now.AddHours(24);
Int32 userId = user.Id;
Boolean enable = true;
And the method might be something like this:
public String GenerateToken(Guid id, params[] Object data) {
Byte[] idBin = id.ToByteArray();
// 1. Convert each object to Byte array
// 2. Concat all byte arrays into tokenData string
String token = Convert.ToBase64String(tokenData.ToArray());
// 3. Encrypt token
return encryptedToken;
}
So the main problems I have are:
Convert each object to Byte array
I know how to convert a specific type but not an Object.
Concat all byte arrays into tokenData string
Encrypt token
Is this the best way to create a token? The token will be sent in a URL.
And how can I solve problems 1 to 3?
Keeping your original method I made this:
public String GenerateToken(Guid id, params object[] allData)
{
byte[] idBin = id.ToByteArray();
byte[] total = new byte[] { };
total.Concat(idBin);
foreach (var data in allData)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, data);
total.Concat(ms.ToArray());
}
}
String token = Convert.ToBase64String(total);
return token;
}
This turns all your extra parameters into 1 concatenated byte array.
The only thing I have excluded is the encryption as there are already a million examples out there: How to Encrypt and Decrypt (hint read past the 1st answer)
I guess that you are using encryption so the user is not able to see or modify data inside this token.
You want to serialize an object to a byte array. The best way to do it is to keep your object typed instead of using an object array. Then use a serialization library like BinaryFormatter or protobuf (better). C# and .NET: How to serialize a structure into a byte[] array, using BinaryWriter?
It will be done automatically using a typed token content and serialization library.
To protect your token, you must use MachineKey.Protect.
Sample :
public String GenerateToken(TokenContent data)
{
byte[] data;
using(var ms = new MemoryStream())
{
Serializer.Serialize(ms, cust);
data = ms.ToArray();
}
var encryptedData = MachineKey.Protect(data, "TokenDataUrl");
var token = Convert.ToBase64String(encryptedData);
return token;
}
public TokenContent ReadToken(string token)
{
byte[] encryptedData = Convert.FromBase64String(token);
var data = MachineKey.Unprotect(encryptedData , "TokenDataUrl");
TokenContent content;
using(var ms = new MemoryStream(data))
{
content = Serializer.Deserialize<TokenContent>(ms);
}
return content;
}
I'd definitely consider using something like Json.Net. This will keep all your serialised data nice and cross-platform. Also, since you mentioned you're using Asp.Net Core, BinaryFormatter isn't available to you if you want to use the cross-platform .Net Standard libraries.
To take your example, you might do something similar to this:
public static string GenerateToken(Guid id, params object[] data)
{
var claims = new List<object>(data);
claims.Add(new
{
id = id
});
string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);
return serialised;
}
So you can call the method with something like:
GenerateToken(Guid.NewGuid(), "hello world!", 25, new { Test = "value" });
Giving you the following:
["hello world!",25,{"Test":"value"},{"id":"bf9e5d38-5ac4-4c6b-b68f-88136fc233cf"}]
You could just encrypt this string and pass it to your API, decrypt it and then deserialise it to an object:
public static object DeserialiseToken(string token)
{
object deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject(token);
return deserialised;
}
That will return you an object with all your original data in.
Notice that because we're using params object[] for our arguments, we can't create key-value pairs easily. We lose a variable's original name when we pass it into the method, and we haven't really got a good way of knowing what each entry in the data array should be called. For example, accessing the 'hello world!' string could be tedious.
We might run into difficulties interpreting the data properly when we want to read it later on.
Improving It!
Having said all that, I think we can improve the approach a little bit.
The first thing I'd do is introduce a proper model for your claims. If you can guarantee your tokens will all have the same 'model' for the data they contain, you can have a class such as:
public class Token
{
public Guid Id { get; set; }
public int UserId { get; set; }
public bool Enable { get; set; }
}
And pass that directly into Json.Net (or use some other serialiser):
string output = GenerateToken(new Token
{
Id = Guid.NewGuid(),
Enable = false,
UserId = 2062
});
and
public static string GenerateToken(Token claims)
{
string serialised = Newtonsoft.Json.JsonConvert.SerializeObject(claims);
return serialised;
}
When you get back around to deserialising the json, you can map it straight to an object:
public static Token DeserialiseToken(string token)
{
Token deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(token);
return deserialised;
}
You'll have a strongly-typed object with all your claims mapped against them.
You should also think about whether actually need to encrypt your token. One popular approach is the JSON Web Token (JWT) standard, where the set of claims are plaintext, but sent along with a verification hash, where the claims are hashed together with a secret.
In a situation where a user modifies the claims, when the token reaches your API it will rehash the claims with the secret, and the signatures won't match, so you'll know it's been tampered with!
If you're not storing anything particularly sensitive in your token then this is a perfectly good approach.
Just a thought, but if you want to serialise (and deserialise) objects (any C# .net object) without having to write a serialisation routing for them, and assuming that Json is OK as an output format, you can use NewtonSoft Json.Net:
http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_SerializeObject.htm
var output = JsonConvert.SerializeObject(object);
if you wanted then to make a unique hash for this object, you could use SHA256 like here: Hashing a string with Sha256

Authenticating Mandrill Inbound Webhook Requests in .NET

I'm using Mandrill Inbound Webhooks to call a method in my WCF API. The request is coming through, I can successfully parse it, etc.
My problem lies in getting the value of the X-Mandrill-Signature header to match the signature that I'm generating (based on the process detailed here: https://mandrill.zendesk.com/hc/en-us/articles/205583257-Authenticating-webhook-requests).
This is what I'm currently doing:
List<string> keys = HttpContext.Current.Request.Params.AllKeys.ToList();
keys.Sort();
string url = "MyMandrillWebhookURL";
string MandrillKey = "MyMandrillWebhookKey"
foreach (var key in keys)
{
url += key;
url += HttpContext.Current.Request.Params[key];
}
byte[] byteKey = System.Text.Encoding.ASCII.GetBytes(MandrillKey);
byte[] byteValue = System.Text.Encoding.ASCII.GetBytes(url);
HMACSHA1 myhmacsha1 = new HMACSHA1(byteKey);
byte[] hashValue = myhmacsha1.ComputeHash(byteValue);
string generatedSignature = Convert.ToBase64String(hashValue);
And generatedSignature does not match the value for X-Mandrill-Signature
I know that the Mandrill docs indicate that the encoding needs to be done in binary and not hexadecimal (and I think my code does that, but correct me if I'm wrong), but, beyond that I can't make heads or tails of what my issue is. Any help is greatly appreciated.
The problem is with how you're retrieving the keys in your validation. You only need to use the request's POST variables alphabetically by key, not all the Request parameters. There is only one POST variable, mandrill_events that needs to be used in the signature generation.
string url = "MyMandrillWebhookURL";
string MandrillKey = "MyMandrillWebhookKey"
url += "mandrill_events";
url += mandrillEvents;
byte[] byteKey = System.Text.Encoding.ASCII.GetBytes(MandrillKey);
byte[] byteValue = System.Text.Encoding.ASCII.GetBytes(url);
...

Generate authenticated CMSEnvelopedData Messages with bouncycastle

I am trying to encrypt data with a password and store it inside a ASN.1 encoded CMS message (using C# and BouncyCastle 1.4)
The code I have seems to have two problems:
the data does not seem to be signed with a HMAC, so when I tamper with the encodedData (by enabling the commented out line), the decryption still succeeds.
when I decrypt the data I have tampered with, I get beck corrupted plain text. However only a two blocks of plaintext data are corrupted. This seems to suggest that the encryption does not actually use CBC mode.
(edit: disregard the second point, this is exactly how CBC is supposed to work)
This is what I am testing with:
public void TestMethod1()
{
byte[] data = new byte[1024]; // plaintext: a list of zeroes
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
CmsPbeKey encryptionKey = new Pkcs5Scheme2PbeKey("foo", new byte[] { 1, 2, 3 }, 2048);
generator.AddPasswordRecipient(encryptionKey, CmsEnvelopedDataGenerator.Aes256Cbc);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(data);
CmsEnvelopedData envelopeData = generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
byte[] encodedData = envelopeData.GetEncoded();
// encodedData[500] = 10; // tamper with the data
RecipientID recipientID = new RecipientID();
CmsEnvelopedData decodedEnvelopeData = new CmsEnvelopedData(encodedData);
RecipientInformation recipient = decodedEnvelopeData.GetRecipientInfos().GetFirstRecipient(recipientID);
byte[] data2 = recipient.GetContent(encryptionKey);
CollectionAssert.AreEqual(data, data2);
}
What am I doing wrong? What would be the correct way to write this?
To add an HMAC to a CMS message, you would have to use a AuthenticatedData-structure.
I am not especially familiar with Bouncy Castle, but from a cursory look at the API, I would say that it does not support AuthenticatedData. In fact, it looks like it only supports SignedData for authentication.
So your options seems to be:
Use another library (or write your own code) to handle the AuthenticatedData-structure.
Calculate the HMAC and provide it in a non-standard way (in a proprietary Attribute or out-of-band).
Use SignedData with an RSA key pair instead.

Categories