Encrypt and Decrypt URL in MVC 4

Hi,

In the below post we are going to learn about how we can implement a custom URL encryption and decryption logic in MVC 4 .

For encrypt and Decrypt URL in MVC with Area Name click here.

This post will teach you below

  1. How to create custom helper
  2. How to create custom Attribute and apply them to MVC

So, Lets start from creating custom ActionLinkHelper 

Create a static class “MyExtensions” and  update the code as below.In the below code I have created a custom helper “EncodedActionLink” which will generate the encoded URL

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using System.Web.Routing;
using System.Security.Cryptography;
using System.IO;

namespace IgnouBuddy.Helpers
{
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("");
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());
}
}
}

After updating the code rebuild the project you are ready to render encrypted URL , just you need to call it as regular helper in the views also please add the using directive for your helper in the view page as below

image

now you can use your helper as below.(using @Html and pressing “.” should show you your custom helper)

@Html.EncodedActionLink(item.QuestionText, "Index", "Answer", new { questionId = item.QuestionID }, null)

Till now first part is done, now get ready for second part .

Create a new class “EncryptedActionParameterAttribute”, inherit it from “ActionFilterAttribute” and update its code as below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Threading;
using System.Web.Mvc;
using WebMatrix.WebData;
using IgnouBuddy.Models;
using System.Security.Cryptography;
using System.IO;

namespace IgnouBuddy.Filters
{

[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());
}
}
}

Rebuild the project and now apply the “EncryptedActionParameter” to the actions for which you have generate the encrypted URL.

@Html.EncodedActionLink(item.QuestionText, "Index", "Answer", new { questionId = item.QuestionID }, null)

image

now run the project and encryption/decryption for URL should work correctly for you.

image 

Hope you find this post useful. Thanks for reading .

Advertisements

36 thoughts on “Encrypt and Decrypt URL in MVC 4

  1. Works great except for one bug. When the encrypted value contains a '+' character in the encrypted string, the Get on the QueryString decodes the + as a space. This makes the encrypted text corrupt. To fix this, simply encode the Encrypted text.
    ancor.Append(“?q=” + HttpUtility.UrlEncode(Encryptor.Encrypt(queryString)));
    Thanks for the post.
    Ed

    Like

  2. done in MyExtension class this part

    StringBuilder ancor = new StringBuilder(); ….

    if (queryString != string.Empty)
    {
    //ancor.Append(“?q=” + Encrypt(queryString));
    ancor.Append(“?q=” + HttpUtility.UrlEncode(Encrypt(queryString)));
    }

    Like

  3. hi.. how can i handle
    {“Invalid length for a Base-64 char array or string.”} Error….
    in Decrypt method …
    inputByte = Convert.FromBase64String(encryptedText); < ----- this line...
    thanks.

    Like

  4. I thought this was really slick but it didn't get routing quite right, in my case when you had your site living in an application off the default IIS site.

    I dug through MS' MVC source and melded ActionLink with EncodedActionLink to handle more of those scenarios:

    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);
    }
    }

    object newRouteValues = new { q = Encrypt(queryString) };

    string url = UrlHelper.GenerateUrl(null, actionName, controllerName, null, null, null, new RouteValueDictionary(newRouteValues), htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true);
    TagBuilder tagBuilder = new TagBuilder(“a”)
    {
    InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty
    };
    tagBuilder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    tagBuilder.MergeAttribute(“href”, url);

    return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal));
    }

    Like

  5. HI Mahesh and Kamalu,

    The “”Invalid length for a Base-64 char array or string.” error generally comes when the encryptedText is not in correct format. Are you updating this text in between?

    Like

  6. I would like to encrypt an id parameter in my controller. Is there a way to do this using your extension? I would like to encrypt the id below….

    return RedirectToAction(“Details”, “Info”, new { id =id });

    Thank you for your helpful post!

    Like

  7. HI. First of all, I would like to thank you for you guidance into this.
    But, I am facing some problems. Let me describe. I've tried to debug, but an error occurred somehow in a point that break points aren't stopping.
    1) when I use an action to an Int Id. Works great, But if user mess with encrypted parameter, removing or changing some characters, and refreshes page, it gets: System.IndexOutOfRangeException, at line decryptedParameters.Add(paramArr[0], Convert.ToInt32(paramArr[1]));
    2) when I use an action with a ViewModelClass as parameter, it does not work at all, it presents same exception at same line.

    VS does not allow me to debug those lines, looking at stack:
    Checkin.CheckinExpress.Web.Filters.EncryptedActionParameterAttribute.OnActionExecuting(ActionExecutingContext filterContext) in d:\Projetos\Enter\CheckinExpress\fnt\Checkin.CheckinExpress.Web\Filter\EncriptedActionParameterAttribute.cs:34
    System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +296
    System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +1137
    System.Web.Mvc.Async.<>c__DisplayClass33.b__31(AsyncCallback asyncCallback, Object asyncState) +112
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +132
    System.Web.Mvc.Async.<>c__DisplayClass21.b__19(AsyncCallback asyncCallback, Object asyncState) +1449
    System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +150
    System.Web.Mvc.Async.AsyncResultWrapper.Begin(AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate`1 endDelegate, Object tag, Int32 timeout) +96
    System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +487
    System.Web.Mvc.Controller.b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +45
    It seems that error occurs in some point between decryption code and Controller.BeginExecuteCore

    Like

  8. Your code is working fine. Thank you
    OnActionExecuting method query string pass any value eg int,string datetime so that use object.
    for (int i = 0; i < paramsArrs.Length; i++)
    {
    string[] paramArr = paramsArrs[i].Split('=');
    //decryptedParameters.Add(paramArr[0], Convert.ToInt32(paramArr[1]));
    object value = paramArr[1];
    decryptedParameters.Add(paramArr[0], value);
    }
    //Implement the particular method use this
    public ActionResult Edit(string SkillLevelId,string test, string testdate)
    {
    try
    {
    int skillLevelId = Convert.ToInt32(SkillLevelId);
    string sts = test;
    DateTime dd = Convert.ToDateTime(testdate);
    }

    Like

  9. you are only considering integer parameters
    for (int i = 0; i < paramsArrs.Length; i++)
    {
    string[] paramArr = paramsArrs[i].Split('=');
    decryptedParameters.Add(paramArr[0], Convert.ToInt32(paramArr[1]));
    }
    what about other types.

    Like

      1. Hello Navin ,

        I have applied you concept in mvc application and it worked fine for url encryption .
        Now my question is when i try to save record no Id sent to controller action . i’m getting following error:

        The parameters dictionary contains a null entry for parameter ‘id’ of non-nullable type ‘System.Int32’ for method ‘System.Web.Mvc.ActionResult Edit(WyData.Admin.Models.PostedCcGs, Int32, System.String)’ in ‘WyData.Admin.Controllers.PctListController’. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
        Parameter name: parameters

        can you tell what i am doing wrong.

        Thanks

        Like

  10. Definitely imagine that which you stated. Your
    favorite justification appeared to be on the
    web the easiest thing to be aware of. I say to you, I certainly get
    irked even as other folks consider worries that they plainly don’t know about.
    You controlled to hit the nail upon the top and also
    outlined out the whole thing without having side-effects
    , other people could take a signal. Will likely be again to get more.
    Thanks

    Like

  11. Hi Guys ,

    I have applied above method to encrypt url and it worked fine .

    Now my question is how to decrypt , encrypted data . i am getting following error :

    The parameters dictionary contains a null entry for parameter ‘id’ of non-nullable type ‘System.Int32’ for method ‘System.Web.Mvc.ActionResult Edit(WyData.Admin.Models.PostedCcGs, Int32, System.String)’ in ‘WyData.Admin.Controllers.PctListController’. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
    Parameter name: parameters

    Has anyone know how to resolve the issue .

    Thanks in advance

    Like

  12. Hi,
    yes its only working for integers parameters
    whats the best way to write that it can work with
    any type of parameters or model objects

    Thanks

    Like

  13. This solution is working in in Development environment but in IIS it shows “404 Error”.

    For example following url is showing 404 in IIS. While in development environment works fine. Please guide.

    “http://touseef-ahmad/Attachment/ViewAtachment?q=xFrvoYvDB9g=”

    Thanks

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s