How to encrypt the query string ID in MVC 4 ActionLink - asp.net-mvc-4

How can I pass the encrypted id in ActionLink. This is what I have written in my view:
#model IEnumerable<forumAPP.tblTechnology>
#foreach (var item in Model)
{
string techName=item.TechName;
#Html.ActionLink(techName, "Details","Home", new { TopicID = item.TechID },null) // Here I would like to encrypt the TopicID
<br />
<br />
#Html.DisplayFor(modelItem => item.TechDesc)
}

Here are a couple of simple methods you can use to encode/decode.
The encoded value is not secure, and as you can see, decoding it is trivial. If your goal is to obfuscate the id, this will work. If you need to secure it, you should take a different approach.
public string Encode( string encodeMe )
{
byte[] encoded = System.Text.Encoding.UTF8.GetBytes( encodeMe );
return Convert.ToBase64String( encoded );
}
public static string Decode( string decodeMe )
{
byte[] encoded = Convert.FromBase64String( decodeMe );
return System.Text.Encoding.UTF8.GetString( encoded );
}
So you could place these methods in your controller, and pass the encoded TechId to the view with viewBag
int techId = 1;
var encoded = Encode(id.ToString());
ViewBag.Encoded = encoded;
And then to use it in your link
#Html.ActionLink(techName, "Details","Home", new { TopicID = ViewBag.Encoded },null)
(Though, you should really consider using a view model. ViewBag, while a convienent and easy way to pass data to the view, is not considered to be best practice. Becoming comfortable with view models and strongly typed views will make your mvc life much easier in the future. Not to mention, produce cleaner and more maintainable code for those that follow you.)

Add A Folder with two classes
Class 1 : EncryptedActionParameterAttribute
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Web;
using System.Web.Mvc;
namespace MVCInvoicClient.Extensions
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class EncryptedActionParameterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Dictionary<string, object> decryptedParameters = new Dictionary<string, object>();
if (HttpContext.Current.Request.QueryString.Get("q") != null)
{
string encryptedQueryString = HttpContext.Current.Request.QueryString.Get("q");
string decrptedString = Decrypt(encryptedQueryString.ToString());
string[] paramsArrs = decrptedString.Split('?');
for (int i = 0; i < paramsArrs.Length; i++)
{
string[] paramArr = paramsArrs[i].Split('=');
decryptedParameters.Add(paramArr[0], Convert.ToInt32(paramArr[1]));
}
}
for (int i = 0; i < decryptedParameters.Count; i++)
{
filterContext.ActionParameters[decryptedParameters.Keys.ElementAt(i)] = decryptedParameters.Values.ElementAt(i);
}
base.OnActionExecuting(filterContext);
}
private string Decrypt(string encryptedText)
{
string key = "jdsg432387#";
byte[] DecryptKey = { };
byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 };
byte[] inputByte = new byte[encryptedText.Length];
DecryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
inputByte = Convert.FromBase64String(encryptedText);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(DecryptKey, IV), CryptoStreamMode.Write);
cs.Write(inputByte, 0, inputByte.Length);
cs.FlushFinalBlock();
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
return encoding.GetString(ms.ToArray());
}
}
}
Class 2 : MyExtensions
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MVCInvoicClient.Extensions
{
public static class MyExtensions
{
public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
string queryString = string.Empty;
string htmlAttributesString = string.Empty;
if (routeValues != null)
{
RouteValueDictionary d = new RouteValueDictionary(routeValues);
for (int i = 0; i < d.Keys.Count; i++)
{
if (i > 0)
{
queryString += "?";
}
queryString += d.Keys.ElementAt(i) + "=" + d.Values.ElementAt(i);
}
}
if (htmlAttributes != null)
{
RouteValueDictionary d = new RouteValueDictionary(htmlAttributes);
for (int i = 0; i < d.Keys.Count; i++)
{
htmlAttributesString += " " + d.Keys.ElementAt(i) + "=" + d.Values.ElementAt(i);
}
}
//What is Entity Framework??
StringBuilder ancor = new StringBuilder();
ancor.Append("<a ");
if (htmlAttributesString != string.Empty)
{
ancor.Append(htmlAttributesString);
}
ancor.Append(" href='");
if (controllerName != string.Empty)
{
ancor.Append("/" + controllerName);
}
if (actionName != "Index")
{
ancor.Append("/" + actionName);
}
if (queryString != string.Empty)
{
ancor.Append("?q=" + Encrypt(queryString));
}
ancor.Append("'");
ancor.Append(">");
ancor.Append(linkText);
ancor.Append("</a>");
return new MvcHtmlString(ancor.ToString());
}
private static string Encrypt(string plainText)
{
string key = "jdsg432387#";
byte[] EncryptKey = { };
byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 };
EncryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
byte[] inputByte = Encoding.UTF8.GetBytes(plainText);
MemoryStream mStream = new MemoryStream();
CryptoStream cStream = new CryptoStream(mStream, des.CreateEncryptor(EncryptKey, IV), CryptoStreamMode.Write);
cStream.Write(inputByte, 0, inputByte.Length);
cStream.FlushFinalBlock();
return Convert.ToBase64String(mStream.ToArray());
}
}
}
Controller
Add this line above the controller class Example for your Index
[EncryptedActionParameter]
In your View
#Html.EncodedActionLink("Download Invoice", "FileDownload","DataFiles", new { id = item.DataFilesID }, null)
add a using statement
#using MVCInvoicClient.Extensions

I came across this while looking for a secure method of doing this. In case someone else wishes to do this securely, you can use the MvcSerializer (I found it in the MVC futures 3 project, I am unsure whether it is included in MVC 4). For example:
(new MvcSerializer()).Serialize(<Your data here>, SerializationMode.EncryptedAndSigned)
And then to reverse the process...
(new MvcSerializer()).Deserialize(<Serialized data here>, SerializationMode.EncryptedAndSigned)
This is great because without any extra effort it encrypts and signs the data. The futures project also includes some attributes to make this happen automatically during model binding.

Related

How does C# know the length of string using Binary Writer?

Please look at the code below. This program simply saves a 33-character-length string "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" with an additional byte value of "33".
using System.Text;
namespace Test
{
internal class Program
{
static void Main(string[] args)
{
string filepath = args[0];
using (var stream = File.Open(filepath, FileMode.Create))
{
using (var writer = new BinaryWriter(stream, Encoding.UTF8, false))
{
writer.Write(new string('!', 33));
writer.Write((byte)33);
}
}
using (var stream = File.Open(filepath, FileMode.Open))
{
using (var reader = new BinaryReader(stream, Encoding.UTF8, false))
{
Console.WriteLine(reader.ReadString());
Console.WriteLine(reader.ReadByte());
}
}
Console.ReadKey();
}
}
}
And here is the binary representation of it:
Apparently, the first starting "ox21" is the length of the string - but how on earth does C# know?

"Validation failed for one or more entities. ERROR

I am creating web API to save the uploaded file in my local storage. When I testing my code it gives an error as ExceptionMessage": "Validation failed for one or more entities. See EntityValidationErrors' property for more details."
Can anyone help to fix this issue. Thanks in advance.
Controller(FileUploadController)
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using System.Web.Http;
using VantageCore.BL;
namespace VantageCoreApi.Controllers.Api
{
public class FileUploadController : ApiController
{
[HttpPost]
[Route("api/FileUpload")]
public async Task<IHttpActionResult> UploadFile(string FileName, int Id)
{
try
{
List<string> ids = new List<string>();
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var referenceId = FileName.Split('_')[0];
foreach (var file in provider.Contents)
{
Guid guid;
ids.Add(Guid.TryParse(await new FileUploadMgt().ReceiveFile(file, FileName, Id), out guid) ? FileName : "Error");
}
return Ok(ids);
}
catch (Exception e)
{
return InternalServerError(e);
}
}
public string SaveFile(byte[] File, string path)
{
string Result = "";
try
{
//LOCAL SERVER PATH
var fs = new BinaryWriter(new FileStream(#"F:\Testfolder" + path, FileMode.Append, FileAccess.Write));
fs.Write(File);
fs.Close();
Result = path;
}
catch (Exception ee)
{
Result = ee.ToString();
}
return Result;
}
}
}
BL (FileUplodMgt.cs)
using System;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Net.Http;
using VantageCore.Entity.Model;
using File = VantageCore.Entity.Model.File;
namespace VantageCore.BL
{
public class FileUploadMgt
{
public async Task<string> ReceiveFile(HttpContent receivedFile, string receivedFileName, int Id)
{
if (receivedFile != null)
{
var fileId = Guid.NewGuid();
using (var c = new DBEntities())
{
NameValueCollection appSettings = ConfigurationManager.AppSettings;
string folder = appSettings["TestPath"];
var fileName = fileId.ToString() + Path.GetExtension(receivedFileName).ToLower();
var file = Path.Combine(folder, fileName);
bool exists = Directory.Exists(folder);
if (!exists) Directory.CreateDirectory(folder);
using (var fs = new BinaryWriter(new FileStream(file, FileMode.Create, FileAccess.Write)))
{
fs.Write(await receivedFile.ReadAsByteArrayAsync());
}
string extention = Path.GetExtension(file);
receivedFileName = Path.GetFileNameWithoutExtension(receivedFileName).Length <= 32
? Path.GetFileNameWithoutExtension(receivedFileName)
: Path.GetFileNameWithoutExtension(receivedFileName).Substring(0, 31) + "~";
var newFile = new File
{
Uid = fileId,
FileExtention = extention,
FileName = receivedFileName,
FileSize = (int)(receivedFile.Headers.ContentLength / 1024),
CreatedDate = DateTime.UtcNow
};
c.Files.Add(newFile);
c.SaveChanges();
}
return fileId.ToString();
}
else
{
return "Error,Invalid file Or file size exceeded";
}
}
}
}
You could try as below to observe the error message when you debug and share it;
try
{
c.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
}
}

.Net Core Equivalent to CryptoJS.AES.encrypt

I have below JavaScript code that uses CryptoJS.AES.encrypt function from CryptoJS library and I am looking for equivalent .Net Core code, can anyone please guide?
var myData = '';
var key1 = '';
var key2 = '';
var encryptedData = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(myData.substr(0, myData.length - 1)), CryptoJS.enc.Utf8.parse(key1),
{
keySize: 128 / 8,
iv: CryptoJS.enc.Utf8.parse(key2),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
Edit
I have found below code for .Net Core:
public string EncryptString(string text, string keyString, string ivString)
{
var key = Encoding.UTF8.GetBytes(keyString);
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var iv = aesAlg.IV;
var decryptedContent = msEncrypt.ToArray();
var result = new byte[iv.Length + decryptedContent.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);
return Convert.ToBase64String(result);
}
}
}
}
But I am not sure how to specify these options as specified in JavaScript code:
{
keySize: 128 / 8,
iv: CryptoJS.enc.Utf8.parse(key2),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
Edit 2
I updated my C# code following suggestions from Topaco and below is my code.
But when I test it the output of my C# code and JavaScript code is different can anyone please guide what's missing?
public string RequestHash(string text, string keyString, string ivString)
{
var key = Encoding.UTF8.GetBytes(keyString);
var iv = Encoding.UTF8.GetBytes(ivString);
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, iv))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var decryptedContent = msEncrypt.ToArray();
return Convert.ToBase64String(decryptedContent);
}
}
}
}
Thank you so much Topaco for your help, really appreciate!
Here's my final code:
public string RequestHash(string text, string keyString, string ivString)
{
var key = Encoding.UTF8.GetBytes(keyString);
var iv = Encoding.UTF8.GetBytes(ivString);
using (var aesAlg = Aes.Create())
{
using (var encryptor = aesAlg.CreateEncryptor(key, iv))
{
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(text);
}
var decryptedContent = msEncrypt.ToArray();
return Convert.ToBase64String(decryptedContent);
}
}
}
}

Flag / Unflag email sending in CRM 2011

In CRM 2011, I want to attach Contacts to Quote, no problems for that.
When I save the quote, for each Contact I want to send a email for reminder purpose. (With a plugin)
How It's possible to flag this and give the ability to CRM user to unflag this from the quote form with a checkbox.
The final purpose, It's to give the ability to CRM user to send a new email reminder to one or multiple contacts attached in the quote.
Can you help me ?
You will need to have a ribbon button that will call a JavaScript method in one of the web-resources.
In the CommandDefinition of you RibbonDiff XML you will need to send a parameter to the JS method which will contain all the IDs of selected records in the subgrid.
<CommandDefinitions>
<CommandDefinition Id="xyz.Button.SendEmail.command">
<EnableRules>
</EnableRules>
<DisplayRules>
</DisplayRules>
<Actions>
<JavaScriptFunction Library="$webresource:Test.Js" FunctionName="SendEmail">
<CrmParameter Value="SelectedControlAllItemIds" />
</JavaScriptFunction>
</Actions>
</CommandDefinition>
and then the JS method would be something like below wherein you will need to parse all the IDs and then process your logic
function SendEmail(selectedIds) {
if (selectedIds != null && selectedIds != “”) {
var strIds = selectedIds.toString();
var arrIds = strIds.split(“, ”);
for (var indxIds = 0; indxIds < arrIds.length; indxIds++) {
//The logic that you want to process on each record will come here.
}
} else {
alert(“No records selected !! !”);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Crm;
using Microsoft.Xrm.Sdk;
using System.ServiceModel;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace SendEmail
{
public class Email : IPlugin
{
public void Execute(IServiceProvider serviceprovider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceprovider.GetService(typeof(IPluginExecutionContext));
if (!(context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity))
return;
//entity
Entity ent = (Entity)context.InputParameters["Target"];
if (ent.LogicalName != "entityName")//EntityName
throw new InvalidPluginExecutionException("Not a Service Request record! ");
//service
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceprovider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService _service = serviceFactory.CreateOrganizationService(context.UserId);
string Email="";
if (ent.Contains("emailidfiled"))
Email = (string)ent["emailidfiled"];
#region email template
QueryExpression query = new QueryExpression()
{
EntityName = "template",
Criteria = new FilterExpression(LogicalOperator.And),
ColumnSet = new ColumnSet(true)
};
query.Criteria.AddCondition("title", ConditionOperator.Equal, "templateName");
EntityCollection _coll = _service.RetrieveMultiple(query);
if (_coll.Entities.Count == 0)
throw new InvalidPluginExecutionException("Unable to find the template!");
if (_coll.Entities.Count > 1)
throw new InvalidPluginExecutionException("More than one template found!");
var subjectTemplate = "";
if (_coll[0].Contains("subject"))
{
subjectTemplate = GetDataFromXml(_coll[0]["subject"].ToString(), "match");
}
var bodyTemplate = "";
if (_coll[0].Contains("body"))
{
bodyTemplate = GetDataFromXml(_coll[0]["body"].ToString(), "match");
}
#endregion
#region email prep
Entity email = new Entity("email");
Entity entTo = new Entity("activityparty");
entTo["addressused"] =Email;
Entity entFrom = new Entity("activityparty");
entFrom["partyid"] = "admin#admin.com";
email["to"] = new Entity[] { entTo };
email["from"] = new Entity[] { entFrom };
email["regardingobjectid"] = new EntityReference(ent.LogicalName, ent.Id);
email["subject"] = subjectTemplate;
email["description"] = bodyTemplate;
#endregion
#region email creation & sending
try
{
var emailid = _service.Create(email);
SendEmailRequest req = new SendEmailRequest();
req.EmailId = emailid;
req.IssueSend = true;
GetTrackingTokenEmailRequest wod_GetTrackingTokenEmailRequest = new GetTrackingTokenEmailRequest();
GetTrackingTokenEmailResponse wod_GetTrackingTokenEmailResponse = (GetTrackingTokenEmailResponse)
_service.Execute(wod_GetTrackingTokenEmailRequest);
req.TrackingToken = wod_GetTrackingTokenEmailResponse.TrackingToken;
_service.Execute(req);
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException("Email can't be saved / sent." + Environment.NewLine + "Details: " + ex.Message);
}
#endregion
}
private static string GetDataFromXml(string value, string attributeName)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
XDocument document = XDocument.Parse(value);
// get the Element with the attribute name specified
XElement element = document.Descendants().Where(ele => ele.Attributes().Any(attr => attr.Name == attributeName)).FirstOrDefault();
return element == null ? string.Empty : element.Value;
}
}
}

Can I implement file download using MVC4's ApiController?

Using regular Controller I could do it by returning FileResult. The same doesn't seem to work with ApiController. Can it be done? Is it even a right thing to do?
Try this.
[HttpGet]
public HttpResponseMessage Get()
{
var file = HttpContext.Current.Server.MapPath("~/Images/accent.png");
var stream = new FileStream(file, FileMode.Open);
var content = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(stream)
};
content.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return content;
}
I have this working thanks to this question.
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http;
namespace Web.Controllers
{
//usage: /download/report
[RoutePrefix("download")]
public class DownloadController : ApiController
{
[HttpGet("report")]
public HttpResponseMessage Report()
{
using (var service = new Client())
{
var report = service.BuildReport();
return DownloadResponse(report, "Report.csv");
}
}
private static HttpResponseMessage DownloadResponse(string content, string fileName)
{
var downloadContent = new StringContent(content);
var mediaType = new MediaTypeHeaderValue("application/octet-stream");
var disposition= new ContentDispositionHeaderValue("attachment") { FileName = fileName };
downloadContent.Headers.ContentType = mediaType;
downloadContent.Headers.ContentDisposition = disposition;
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = downloadContent
};
return result;
}
}
}