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
Related
I have this method in my controller.
public IActionResult Download()
{
return Json(_context.Users);
}
I noticed that it produces the correct JSON structure but it's being rendered in the browser as common text. I want it to be downloaded to the client's computer. How do I do that?
I'm not sure if is should make my object to stream somehow like this or maybe create a file on my hard drive and serve it like this.
I can't find anything that strikes me as straight-forward and simple like we're used to in C#. So I fear that I'm missing a concept here.
You can just write json object to a stream or array and use one of File method overloads. Add convenient Serialize method
private byte[] Serialize(object value, JsonSerializerSettings jsonSerializerSettings)
{
var result = JsonConvert.SerializeObject(value, jsonSerializerSettings);
return Encoding.UTF8.GetBytes(result);
}
And use it as following
public IActionResult Download()
{
var download = Serialize(_context.Users, new JsonSerializerSettings());
return File(download , "application/json", "file.json");
}
If you set special json serializer settings in Startup using .AddJsonOptions() you would like to use them as ASP.NET framework uses them in Json method. Inject MvcJsonOptions in controller
IOptions<MvcJsonOptions> _options;
public YourController(IOptions<MvcJsonOptions> options)
{
_options = options;
}
And pass settings to method
public IActionResult Download()
{
var download = Serialize(_context.Users, _options.Value.SerializerSettings);
return File(download , "application/json", "file.json");
}
Convert the data into bytes then those bytes into a FileResult. You return the FileResult and the browser will do whatever it does normally when presented with a 'file', usually either prompt the user or download.
Example below:
public ActionResult TESTSAVE()
{
var data = "YourDataHere";
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
var output = new FileContentResult(bytes, "application/octet-stream");
output.FileDownloadName = "download.txt";
return output;
}
In your case you would simply take your JSON data as a string.
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.
I'm trying to encrypt a class before serializing it. The only way I found is to encrypt it and return a String or a stream, is it possible to return the original class ?
public static Options Encrypt(Options Settings)
{
Options sk = null;
try
{
using (var stream = new MemoryStream())
{
RuntimeTypeModel.Default.Serialize(stream, Settings);
byte[] data = encryptWithPadding(stream.ToArray(), 0);
String base64EncryptedString = Convert.ToBase64String(data);
// needs to return an option instance
}
}
catch (Exception e)
{
Global.LogError("Serialization failed", e);
}
return sk;
}
You can't "encrypt" a protobuf without serializing it. Encryption generally works on bytes, not data structures. If you want to encrypt it, and then embed the encrypted version as a field in some other protobuf, make that other field have type bytes instead of Options.
(In theory you could encrypt each of the fields of Options individually, but this would not help you much and would very likely be less secure than encrypting the whole serialized blob.)
I have a Silverlight app. This app calls back to a web service (that I've written) to get a list of customers. I'm trying to generate a hash code that represents the list of customers. In an attempt to do this, I have the following code written in my Silverlight app, and in my server-side code:
public static string GetHashCode<T>(T data)
{
SHA256Managed crypto = new SHA256Managed();
try
{
// Serialize the object
byte[] serializedBytes;
using (var memoryStream = new MemoryStream())
{
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(memoryStream, data);
memoryStream.Seek(0, SeekOrigin.Begin);
serializedBytes = memoryStream.ToArray();
}
byte[] hashed = crypto.ComputeHash(serializedBytes);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashed.Length; i++)
sb.Append(hashed[i].ToString("X2"));
return sb.ToString();
}
catch (Exception)
{
return string.Empty;
}
}
My method on the server-side looks like this:
[OperationContract]
public List<Customer> GetCustomers()
{
List<Customer> customers = Customer.GetAll();
string hash = GetHashCode<List<Customer>>(customers);
return customers;
}
On the client-side, in my Silverlight app, I have the following in my GetCustomersCompleted event handler:
string hash = GetHashCode<List<Customer>>(e.Result.ToList());
Please notice that I am converting the event handler Result to a list first. The reason why is because the Result is actually a ObservableCollection. I bring this up because I believe this is where my problem lies.
My problem is, even though the data is the same, my hash values are different on the server and client-side. This is based on what I'm seeing in the watch window. I have a feeling it has something to do with the serialization that occurs when the results get passed from my server to my app. However, I'm not sure how to get around this. I need a hash of the data, but I can't figure out how to get them to line up. Thank you for any help you can provide.
I need two methods one to encrypt and one to decrypt an xml file with a key= "hello world",the key hello world should be used to encrypt and decrypt the xml file.These methods should work on all machines!!! Any encryption methods will do. XML File contents below:
<root>
<lic>
<number>19834209</number>
<expiry>02/02/2002</expiry>
</lic>
</root>
Can some give me a sample?The issue is the msdn sample encyptions make a xml file encypted but when I decrypt on another machine it doesn't work.For example
I tried this sample:
How to: Encrypt XML Elements with Asymmetric Keys,
but here there is some kinda session and on another machine it says bad data phewf!
If you want the same key for encrypting and decrypting you should use a symmetric method (that's the definition, really). Here's the closest one to your sample (same source).
http://msdn.microsoft.com/en-us/library/sb7w85t6.aspx
The posted sample isn't working because they aren't using the same keys. Not only on different machines: running the program on the same machine twice should not work either (didn't work for me), because they use different random keys every time.
try adding this code after creating your key:
key = new RijndaelManaged();
string password = "Password1234"; //password here
byte[] saltBytes = Encoding.UTF8.GetBytes("Salt"); // salt here (another string)
var p = new Rfc2898DeriveBytes(password, saltBytes); //TODO: think about number of iterations (third parameter)
// sizes are devided by 8 because [ 1 byte = 8 bits ]
key.IV = p.GetBytes(key.BlockSize / 8);
key.Key = p.GetBytes(key.KeySize / 8);
Now the program is using the same key and initial vector, and Encrypt and Decrypt should work on all machines.
Also, consider renaming key to algorithm, otherwise this is very misleading. I'd say it's a bad, not-working-well example from MSDN.
NOTE: PasswordDeriveBytes.GetBytes() has been deprecated because of serious (security) issues within the PasswordDeriveBytes class. The code above has been rewritten to use the safer Rfc2898DeriveBytes class instead (PBKDF2 instead of PBKDF1). Code generated with the above using PasswordDeriveBytes may be compromised.
See also: Recommended # of iterations when using PKBDF2-SHA256?
First of all, if you want to use the same key for encrypting and decrypting, you should look at symmetric cryptography. Asymmetric cryptography is when the keys for encrypting and decrypting are different. Just so that you know - RSA is asymmetric, TripleDES and Rijndael are symmetric. There are others too, but .NET does not have default implementations for them.
I'd advise studying the System.Security.Cryptography namespace. And learning a bit about all that stuff. It has all you need to encrypt and decrypt files, as well as generate a password. In particular, you might be interested in these classes:
CryptoStream
PasswordDeriveBytes
RijndaelManaged
There are also examples for usage in MSDN for each of them. You can use these classes to encrypt any file, not just XML. If however you want to encrypt just a select few elements, you can take a look at System.Security.Cryptography.Xml namespace. I see you've already found one article about it. Keep following the links on that page and you will learn more about those classes.
Would be cooler if you used a private key to sign the <lic> element and added the result to the file (in a <hash> element perhaps). This would make it possibly for everyone to read the xml file in case your support needs to know the license number, or the date of expiry, but they can not change any values without the private key.
The public key needed to verify the signature would be common knowledge.
Clarification
Signing your code will only protect it against changes, it will not keep any information in it hidden. Your original question mentions encryption, but I am not sure that it is a requirement to hide the data, or just protect it from modification.
Example code: (Never publish PrivateKey.key. ServerMethods are only needed when signing the xml file, ClientMethods are only needed when verifying the xml file.)
using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
public static class Program {
public static void Main() {
if (!File.Exists("PublicKey.key")) {
// Assume first run, generate keys and sign document.
ServerMethods.GenerateKeyPair();
var input = new XmlDocument();
input.Load("input.xml");
Debug.Assert(input.DocumentElement != null);
var licNode = input.DocumentElement["lic"];
Debug.Assert(licNode != null);
var licNodeXml = licNode.OuterXml;
var signedNode = input.CreateElement("signature");
signedNode.InnerText = ServerMethods.CalculateSignature(licNodeXml);
input.DocumentElement.AppendChild(signedNode);
input.Save("output.xml");
}
if (ClientMethods.IsValidLicense("output.xml")) {
Console.WriteLine("VALID");
} else {
Console.WriteLine("INVALID");
}
}
public static class ServerMethods {
public static void GenerateKeyPair() {
var rsa = SharedInformation.CryptoProvider;
using (var keyWriter = File.CreateText("PublicKey.key"))
keyWriter.Write(rsa.ToXmlString(false));
using (var keyWriter = File.CreateText("PrivateKey.key"))
keyWriter.Write(rsa.ToXmlString(true));
}
public static string CalculateSignature(string data) {
var rsa = SharedInformation.CryptoProvider;
rsa.FromXmlString(File.ReadAllText("PrivateKey.key"));
var dataBytes = Encoding.UTF8.GetBytes(data);
var signatureBytes = rsa.SignData(dataBytes, SharedInformation.HashAlgorithm);
return Convert.ToBase64String(signatureBytes);
}
}
public static class ClientMethods {
public static bool IsValid(string data, string signature) {
var rsa = SharedInformation.CryptoProvider;
rsa.FromXmlString(File.ReadAllText("PublicKey.key"));
var dataBytes = Encoding.UTF8.GetBytes(data);
var signatureBytes = Convert.FromBase64String(signature);
return rsa.VerifyData(dataBytes, SharedInformation.HashAlgorithm, signatureBytes);
}
public static bool IsValidLicense(string filename) {
var doc = new XmlDocument();
doc.Load(filename);
var licNode = doc.SelectSingleNode("/root/lic") as XmlElement;
var signatureNode = doc.SelectSingleNode("/root/signature") as XmlElement;
if (licNode == null || signatureNode == null) return false;
return IsValid(licNode.OuterXml, signatureNode.InnerText);
}
}
public static class SharedInformation {
public static int KeySize {
get { return 1024; }
}
public static string HashAlgorithm {
get { return "SHA512"; }
}
public static RSACryptoServiceProvider CryptoProvider {
get { return new RSACryptoServiceProvider(KeySize, new CspParameters()); }
}
}
}
this is how you digitally sign and verify XML documents Sign XML Documents