رمزنگاری در JavaScript و رمزگشایی در #C با الگوریتم AES در ASP.NET MVC

یکشنبه 10 آبان 1394

در این مقاله نشان می دهیم که چگونه می توانیم مقادیر را سمت کلاینت با جاوا اسکریپت رمزنگاری کرده و با #C سمت سرور توسط الگوریتم AES رمزگشایی کنیم. این روش امنیت داده های ارسال شده از کلاینت به سرور را تامین می کند.

رمزنگاری در JavaScript و رمزگشایی در #C با الگوریتم AES در ASP.NET MVC

الگوریتم AES چیست؟

الگوریتم AES یا Advanced Encryption Standard یک الگوریتم رمزنگاری متقارن است. این الگوریتم توسط دو رمزنگار بلژیکی به نام های جان دامن و وینسنت ریجمن توسعه داده شد.

AES برای کارآمدی هم سخت افزار و هم نرم افزار طراحی شده بود و از طول بلوک های 128 بیتی و طول کلیدهای 128، 192 و 256 بیتی پشتیبانی می کند.

بهتر از همه، نرم افزار AES Crypt می باشد که به طور کامل رایگان بوده و متن باز است.

از آنجایی که این نرم افزار متن باز است، چندین نفر در این نرم افزار مشارکت داشته و کد منبع نرم افزار را بررسی کرده اند تا از درستی کارکرد آن اطمینان حاصل شود و امنیت اطلاعات تامین به خوبی ایجاد شود.

کجا از AES استفاده می کنیم؟

در جهان امروزی معمولا از برنامه هایی در وب استفاده می کنیم که مستعد ابتلا به حملات مختلف هستند. برای جلوگیری از این حملات می توانیم که داده های آن را در سمت کلاینت رمزنگاری کرده و زمانی که کاربر اطلاعاتی را به سمت سرور پست می کند، سمت سرور رمزگشایی کنیم.

در MVC متد ( )Html.AntiForgeryToken برای جلوگیری از درخواست های غیر مجاز از سایت های دیگر استفاده می شود. و از حملات جعل اطلاعات مهم در سایت مانند CSRF (XSRF) جلوگیری می کند. اما اگر داده ها را در سمت کلاینت نیز رمزنگاری کنیم آنگاه چیزی برای دسترسی در سمت کلاینت وجود ندارد و امنیت برنامه بالاتر می رود. برای انجام این کار به ادامه مقاله توجه کنید:

روند کار:

ایجاد یک solution

ایجاد یک  Model

اضافه کردن یک Controller

اضافه کردن یک View

اضافه کردن فایل AES JavaScript

اضافه کردن کنترل های hidden field روی فرم ها.

اضافه کردن کد AESEncrytDecry  برای رمزگشایی

در نهایت رمزگشایی روی رخداد کلیک button و گرفتن متن خام از آن.

حالا اجازه دهید کار را شروع کنیم:

مرحله 1:

یک پروژه جدید در ASP.NET MVC ایجاد کنید و نام آن را MvcEncrypandDecryp قرار دهید. solution آن مانند تصویر زیر می شود:

مرحله 2:

بعد از ایجاد solution یک مدل با 4 فیلد زیر را برای نشان دادن صفحه نمایشی، اضافه می کنیم.

Username

Password

HDUser

HDpass

برای اضافه کردن مدل بر روی پوشه Model راست کلیک کرده و از لیست ظاهر شده Add Class را انتخاب کرده و نام آن  را UserLogin.cs قرار دهید. مانند تصویر زیر:

تصویر زیر را ببینید:

 

بعد از اضافه کردن Model می توانید solution زیر را به طور مشابه در پروژه خود  ببینید:

حالا اجازه دهید صفت ها (attribute) را مانند کد زیر به Model اضافه کنیم:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace MvcEncrypandDecryp.Models
{
    public class Userlogin
    {
        [Required(ErrorMessage = "Enter Username")]
        public string Username { get; set; }
        [Required(ErrorMessage = "Enter Password")]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        public string HDUser { get; set; }
        // hidden fields for username
        public string HDpass { get; set; }
        // hidden fields for password
    }
}

حالا وقت آن است که Controller را نیز اضافه کنیم، اما قبل آن یک بار پروژه خود را build کنید.

مرحله 3:

برای اضافه کردن کنترلر بر روی پوشه Controller راست کلیک کرده و Add->Controller را انتخاب کنید:

بعد از آن یک کنترلر خالی ایجاد کرده و نام آن را Userlogin قرار دهید.

بعد از اضافه کردن کنترلر می بینید که UserloginController به پوشه Controller اضافه شده است:

حالا کد زیر را به کنترلر UserLoginController اضافه کنید:

همانطور که دیدی بعد از تغییر ActionResult مربوط به UserLoginController دو متد ایجاد کردیم، یکی برای GET و دیگری برای POST.

مرحله 4:

حالا View را اضافه می کنیم.

برای اضافه کردن View بر روی ActionResult راست کلیک کرده و Add View را بزنید، مانند شکل زیر:

صفحه ای ظاهر می شود که در آن نام View را تعیین می کنیم اما همان نام پیش فرض مناسب است و آن را تغییر نمی دهیم:

برای ایجاد Strongly-typed view تیک مورد نظر آن را می زنیم:

دکمه Add را کلیک کرده و View به صورت زیر ایجاد می شود:

مرحله 5:

بعد از اضافه کردن View حالا فایل جاوا اسکریپت مربوط به AES را به پوشه script اضافه می کنیم. برای اضافه کردن آن فقط  فایل aes.js در پوشه scripts قرار دهید. برای درک بهتر تصویر زیر را ببینید:

بعد از اضافه کردن aes.js آن را به صفحه Login در جایی رفرنس دهید که میخواهیم داده را رمزنگاری کنیم:

مرحله 6:

حالا دو hidden fields برای ذخیره کردن داده ها رمزنگاری شده اضافه کنید:

مرحله 7:

در اینجا فیلدها را به فرم ها اضافه می کنیم و کدهای جاوا اسکریپت را برای رمزنگاری داده روی دکمه ارسال (Submit) می نویسیم:

حالا جزئیات تابع جاوا اسکریپت را بررسی می کنیم. در این کد ما یک مقدار را از TexBox(که کاربر وارد می کند) در فیلدهای username و password می گیریم:

var txtUserName = $('#Username').val();  
var txtpassword = $('#Password').val(); 

سپس کلید را رمزنگاری کرده و Initialization vector (IV) اختصاص می یابد و باید 16 کاراکتر باشد:

var key = CryptoJS.enc.Utf8.parse('8080808080808080');  
var iv = CryptoJS.enc.Utf8.parse('8080808080808080'); 

حالا مقدار را رمزنگاری کرده و مقدار Password را در فیلدهای پنهان HDUser ذخیره می کنیم:

var encryptedlogin = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(txtUserName), key,  
{
   keySize: 128 / 8,   
   iv: iv,  
   mode: CryptoJS.mode.CBC,  
   padding: CryptoJS.pad.Pkcs7 
});  
  
$('#HDUser').val(encryptedlogin); 

به طور مشابه همین کار را برای رمزنگاری Password و ذخیره سازی مقدار در فیلدهای مخفی HDPass انجام می دهیم:

var encryptedpassword = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(txtpassword), key,  
{ 
   keySize: 128 / 8,   
   iv: iv,  
   mode: CryptoJS.mode.CBC,  
   padding: CryptoJS.pad.Pkcs7
});  
  
$('#HDpass').val(encryptedpassword); 

بعد از رمزنگاری مقادیر از یک هشدار برای نشان دادن نسخه رمزنگاری شده متن استفاده کرده ایم:

alert('encrypted login :' + encryptedlogin);  
alert('encrypted password :' + encryptedpassword);

حالا قسمت جاوا اسکریپت(سمت سرور) را انجام می دهیم.

مرحله 8:

برای این نیاز داریم که یک کلاس برای رمزگشایی فیلدهایی که رمزنگاری کرده بودیم، اضافه کنیم.

برای همین یک کلاس با نام AESEncrytDecry.cs ایجاد می کنیم.

این کلاس دو متد به صورت زیر دارد:

DecryptStringFromBytes

    EncryptStringToBytes

و DecryptStringAES به صورت سفارشی برای مقادیر رمزگشایی ایجاد شده است:

متد DecryptStringFromBytes :


    private static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv)  
    {  
        // Check arguments.  
        if (cipherText == null || cipherText.Length <= 0)  
        {  
            throw new ArgumentNullException("cipherText");  
        }  
        if (key == null || key.Length <= 0)  
        {  
            throw new ArgumentNullException("key");  
        }  
        if (iv == null || iv.Length <= 0)  
        {  
            throw new ArgumentNullException("key");  
        }  
      
        // Declare the string used to hold  
        // the decrypted text.  
        string plaintext = null;  
      
        // Create an RijndaelManaged object  
        // with the specified key and IV.  
        using (var rijAlg = new RijndaelManaged())  
        {  
            //Settings  
            rijAlg.Mode = CipherMode.CBC;  
            rijAlg.Padding = PaddingMode.PKCS7;  
            rijAlg.FeedbackSize = 128;  
      
            rijAlg.Key = key;  
            rijAlg.IV = iv;  
      
            // Create a decrytor to perform the stream transform.  
            var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);  
      
         try  
            {  
                // Create the streams used for decryption.  
                using (var msDecrypt = new MemoryStream(cipherText))  
                {  
    ing (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))  
                    {  
      
                        using (var srDecrypt = new StreamReader(csDecrypt))  
                        {  
                            // Read the decrypted bytes from the decrypting stream  
                            // and place them in a string.  
                            plaintext = srDecrypt.ReadToEnd();  
      
                        }  
      
                    }  
                }  
            }  
            catch  
            {  
                plaintext = "keyError";  
            }  
        }  
      
        return plaintext;  
    }  

 

متد EncryptStringToBytes :


    private static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)  
    {  
        // Check arguments.  
        if (plainText == null || plainText.Length <= 0)  
        {  
            throw new ArgumentNullException("plainText");  
        }  
        if (key == null || key.Length <= 0)  
        {  
            throw new ArgumentNullException("key");  
        }  
        if (iv == null || iv.Length <= 0)  
        {  
            throw new ArgumentNullException("key");  
        }  
        byte[] encrypted;  
        // Create a RijndaelManaged object  
        // with the specified key and IV.  
        using (var rijAlg = new RijndaelManaged())  
        {  
            rijAlg.Mode = CipherMode.CBC;  
            rijAlg.Padding = PaddingMode.PKCS7;  
            rijAlg.FeedbackSize = 128;  
      
            rijAlg.Key = key;  
            rijAlg.IV = iv;  
      
            // Create a decrytor to perform the stream transform.  
            var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);  
      
            // Create the streams used for encryption.  
            using (var msEncrypt = new MemoryStream())  
            {  
              using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))  
                {  
                    using (var swEncrypt = new StreamWriter(csEncrypt))  
                    {  
                        //Write all data to the stream.  
                        swEncrypt.Write(plainText);  
                    }  
                    encrypted = msEncrypt.ToArray();  
                }  
            }  
        }  
      
        // Return the encrypted bytes from the memory stream.  
        return encrypted;  
    }  

 

متد DecryptStringAES :

public static string DecryptStringAES(string cipherText)  
{  
    var keybytes = Encoding.UTF8.GetBytes("8080808080808080");  
    var iv = Encoding.UTF8.GetBytes("8080808080808080");  
  
    var encrypted = Convert.FromBase64String(cipherText);  
    var decriptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv);  
    return string.Format(decriptedFromJavascript);  
} 

تصویر زیر را ببینید:

حالا بر روی  دکمه ارسال مربوط به "( )onclick="return SubmitsEncry کلیک کنید و اولین JavaScript برای رمزنگاری داده فراخوانی می شود.

و سپس متد post مربوط به این دکمه داده های فرستاده شده از سمت سرور را رمزگشایی می کند:

<input type="submit" onclick="SubmitsEncry();" value="Create" /> 

در اینجا  [HttpPost] مقادیری را از فیلدهای مخفی می گیرد و آنها را به کلاس AESEncrytDecry متد DecryptStringAES می فرستد جایی که مقادیر رمزگشایی خواهند شد.فرستادن مقادیر به این متد به صورت زیر است:


    public static string DecryptStringAES(string cipherText)  
    {  
        var keybytes = Encoding.UTF8.GetBytes("8080808080808080");  
        var iv = Encoding.UTF8.GetBytes("8080808080808080");  
      
        var encrypted = Convert.FromBase64String(cipherText);  
        var decriptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv);  
        return string.Format(decriptedFromJavascript);  
    }

و شما خواهید دید که کلید و بردار اولیه که ارسال می کنیم باید شبیه آنچه که  از جاوا اسکریچت ارسال کرده بودیم باشد.

مرحله 9:

کد سمت سرور برای رمزگشایی:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using System.Web.Mvc;  
using MvcEncrypandDecryp.Models;  
using ClientsideEncryption;  
  
namespace MvcEncrypandDecryp.Controllers  
{  
    public class UserloginController : Controller  
    {  
        [HttpGet]  
        public ActionResult Login()  
        {  
            return View();  
        }  
  
        [HttpPost]  
        [ValidateAntiForgeryToken]  
        public ActionResult Login(Userlogin objUL )  
        {  
            if (ModelState.IsValid)  
            {  
                var username = AESEncrytDecry.DecryptStringAES(objUL.HDUser);  
                var password = AESEncrytDecry.DecryptStringAES(objUL.HDpass);  
  
                if (username == "keyError" && password == "keyError")  
                {  
                    TempData["notice"] = "Invalid Login";  
                }  
                else  
                {  
                    TempData["notice"] = "login successfully";  
                }  
                return View(objUL);  
            }  
            else  
            {  
                ModelState.AddModelError(string.Empty, "Invalid Login");  
                return View(objUL);  
            }  
        }  
  
    }  
} 

حالا برنامه را اجرا کنید و خروجی به صورت زیر می شود:

 

همانطور که در شکل میبینید مقدار سمت کلاینت به سمت سرور Post شده است:

تصویر زیر پس از رمزگشایی مقدار را نشان می دهد:

برای محافظت از داده های حساس، بهترین کار استفاده از HTTPS به جای HTTP می باشد. پروتکل HTTPS به طور خودکار اطلاعات حساس را که از کلاینت به سمت سرور POST شده است،رمزنگاری می کند بنابراین هیچ کس نمی تواند از شنود شبکه (network sniffers) برای رهگیری و خواندن اطلاعات استفاده کند.

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید