بررسی محبوب ترین الگوریتم های متقارن و نامتقارن

در مقاله ی روش های مختلف رمزنگاری با الگوریتم های متقارن و نامتقارن آشنا شدیم و به بررسی تفاوت های آن ها پرداختیم، در این مقاله با الگوریتم های AES و RSA آشنا خواهیم شد و همچنین به قطعه کدهایی که با استفاده از آن ها میتوان اطلاعات خود را رمزگزاری (Encryption) و رمزگشایی (Decryption) کرد، خواهیم پرداخت.

الگوریتم AES

یکی از الگوریتم های رمزنگاری متقارن و پرکاربرد میباشد. زمانی که از رمزنگاری صحبت میکنیم به معنای ایجاد یک رشته معادل(ciphertext) قابل بازگشت است. در این الگوریتم یک کلید کاملا محرمانه در نظر گرفته میشود که در الگوریتم رمزنگاری از این کلید برای Encrypt و Decrypt داده ها استفاده میشود، یا به عبارت دیگر عبارت رمز شده فقط با این کلید قابل بازگشت خواهد بود؛ طبیعتا با لو رفتن این کلید، تمام ciphertext های تولید شده با الگوریتم AES را میتوان به رشته اصلی بازگرداند. 
 متدهای مربوط به Encrypt و Decrypt متن ورودی در زبان #C به صورت ذیل میباشد.

Encrypt

static string EncryptStringToBytes(string plainText, string KeyString, string IVString)
            {
                var Key = Convert.FromBase64String(KeyString);
                var IV = Convert.FromBase64String(IVString);
                // 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("IV");
                byte[] encrypted;
                // Create an RijndaelManaged object
                // with the specified key and IV.
                using (RijndaelManaged rijAlg = new RijndaelManaged())
                {
                    rijAlg.Key = Key;
                    rijAlg.IV = IV;

                    // Create an encryptor to perform the stream transform.
                    ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                    // Create the streams used for encryption.
                    using (MemoryStream msEncrypt = new MemoryStream())
                    {
                        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (StreamWriter 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 Convert.ToBase64String(encrypted);
            }

Decrypt

static string DecryptStringFromBytes(string cipherTextString, string KeyString, string IVString)
            {
                var Key = Convert.FromBase64String(KeyString);
                var IV = Convert.FromBase64String(IVString);
                var cipherText = Convert.FromBase64String(cipherTextString);

                // 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("IV");

                // Declare the string used to hold
                // the decrypted text.
                string plaintext = null;

                // Create an RijndaelManaged object
                // with the specified key and IV.
                using (RijndaelManaged rijAlg = new RijndaelManaged())
                {
                    rijAlg.Key = Key;
                    rijAlg.IV = IV;

                    // Create a decryptor to perform the stream transform.
                    ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

                    // Create the streams used for decryption.
                    using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                    {
                        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                            {
                                // Read the decrypted bytes from the decrypting stream
                                // and place them in a string.
                                plaintext = srDecrypt.ReadToEnd();
                            }
                        }
                    }
                }

                return plaintext;
            }
        }

در قطعه کدهای بالا دو متغیر با عنوان keyString و IVString وجود دارد. در واقع KeyString همان کلید محرمانه ای است که داده های رمزنگاری شده فقط با این کلید میتوانند به رشته اصلی بازگردانده شوند، لذا باید برای نگهداری محرمانه این کلید اهمیت بسیار زیادی قائل شد. در زبان سیشارپ میتوان این دو متغیر را از طریق قطعه کد زیر ایجاد نمود. قاعدتا یک بار نیاز به ایجاد کلید خواهید داشت و این قطعه کد تنها یک بار استفاده میشود، مگر زمانی که بخواهید کلید را تغییر دهید.

using (RijndaelManaged myRijndael = new RijndaelManaged())
                    {

                        myRijndael.GenerateKey();
                        myRijndael.GenerateIV();

                        var keyString = Convert.ToBase64String(myRijndael.Key);
                        var IVString = Convert.ToBase64String(myRijndael.IV);

                        string encrypted = EncryptStringToBytes(original, keyString, IVString);
                    
                        string roundtrip = DecryptStringFromBytes(encrypted, keyString, IVString);
                    }

الگوریتم RSA

یکی از پرکاربردترین و ایمن ترین الگوریتم های رمزنگاری نامتقارن که در طیف گسترده ای از سامانه های نرم افزاری و در لایه های مختلف مورد استفاده قرار میگیرد. همانطور که در ابتدای مقاله گفته شد، در الگوریتم های نامتقارن از یک کلید برای Encrypt و از کلید دیگری برای Decrypt استفاده میشود که به این زوج کلید عمومی (public key) و کلید خصوصی (private key) گفته میشود. از الگوریتم RSA علاوه بر رمزگذاری، براس Sign کردن اطلاعات نیز استفاده میشود که به توضیح آن خواهیم پرداخت.
کلید عمومی در عملیات رمزنگاری برای Encrypt داده ها و کلید خصوصی برای Decrypt استفاده میشود. لازم به ذکر است که این دو کلید به هم وابسته هستند و باید با هر دو با ابزارهای موجودی به صورت همزمان و مرتبط با هم تولید شوند.  برای درک بهتر به سناریویی که مطرح میکنیم توجه نمایید.
فرض کنید شما و یک سازمان دیگر قصد دارید متونی را با یکدیگر تبادل کنید و نیاز است تا داده هایی که آن سازمان به شما ارسال میکنید کاملا رمز شده باشد. در این سناریو از الگوریتم رمزنگاری متقارن نباید استفاده نمود چرا که کلید میان شما و سازمان مقابل شما مشترک و مشابه است و اگر در هر یک از سازمان ها کلید در اختیار افراد غیرمجاز قرار گیرد، امنیت تبادل اطلاعات با خطرات جدی مواجه میشود. در مقابل زمانی که شما از الگوریتم نامتقارن استفاده میکنید، کلید خصوصی را در نهایت امنیت و محرمانگی سمت خودتان نگهداری میکنید و تنها کلید عمومی را در اختیار سازمان مقابل قرار میدهید. آن سازمان میتواند با استفاده از کلید عمومی که دریافت کرده است اطلاعات را کاملا رمزنگاری کند و پس از ارسال ، شما میتوانید با کلید خصوصی رمزگشایی را انجام دهید. طبیعتا کلید عمومی حتی اگر در اختیار افراد غیر مجاز قرار گیرد، هیچ گونه خطری تبادل اطلاعات را تهدید نخواهد کرد، چرا که تنها برای Encrypt داده ها قابل استفاده است؛ این در حالیست که کلید خصوصی را در نهایت امنیت، نزد خودتان نگهداری میکنید.
متدهای  مربوط به Encrypt و Decrypt داده ها در الگوریتم Rsa در ذیل آورده شده است.


Encrypt

  public static string Encrypt(string plainText, string publicKey)
        {
            UnicodeEncoding _encoder = new UnicodeEncoding();

            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(publicKey);
            var dataToEncrypt = _encoder.GetBytes(plainText);
            var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();
           return Convert.ToBase64String(encryptedByteArray);
        }

Decrypt

public static string Decrypt(string cipherText, string privateKey)
        {
            UnicodeEncoding _encoder = new UnicodeEncoding();

            var rsa = new RSACryptoServiceProvider();
            var dataByte= Convert.FromBase64String(cipherText);
            rsa.FromXmlString(privateKey);
            var decryptedByte = rsa.Decrypt(dataByte, false);
            return _encoder.GetString(decryptedByte);
        }

برای اجرای متدهای فوق به کلید عمومی و خصوصی نیاز است که میتوانید از طریق کد ذیل، یک بار به ایجاد این دو کلید بپردازید و سپس از این زوج کلید ایجاد شده استفاده کنید.

using (var rsa = new RSACryptoServiceProvider(2048))
            {
                var publicKey = rsa.ToXmlString(false);
                var privateKey = rsa.ToXmlString(true);
            }

در الگوریتم فوق طول کلید ها 2048 بایت است. طول کلید را میتوان مقادیر مختلفی در نظر گرفت. عملیات Encrypt و Decrypt در RSA میتواند باعث ایجاد سربار و کندی محسوسی شود، بنابراین باید در جای صحیح و درست مورد استفاده قرار گیرد. هر چه طول کلید بیشتر باشد امنیت بالاتر و در عین حال سربار بیشتر است.

همانطور که گفتیم از RSA برای sign کردن داده ها نیز استفاده میشود. وقتی داده ای را sign میکنیم، عملیاتی مشابه با Hashing صورت میگیرد و غیر قابل بازگشت است؛ با این تفاوت که حتی برای تطبیق نیاز به کلید است و از همین طریق امنیت افزایش میابد. بنابراین حتی اگر رشته sign شده در اختیار افراد غیرمجاز قرار گیرد، در صورت نداشتن کلید sign نمیتوانند تطبیق را انجام دهند.
از این روش برای تبادل داده هش میان دو سامانه  و برای افزایش امنیت استفاده میشود. به عبارت دیگر ابتدا رشته اصلی هش و سپس داده هش شده sign و سپس داده sign شده به سامانه دیگر ارسال میشود. 
در عملیات Sign کردن داده ها از الگوریتم RSA با همان کلیدهای خصوصی و عمومی استفاده میشود. از کلید خصوصی برای Sign کردن داده و از کلید عمومی برای تطبیق استفاده میشود.
 

Sign

public static string SignData(string plainText, string privateKey)
        {
            try
            {
                ASCIIEncoding ByteConverter = new ASCIIEncoding();
                byte[] originalData = ByteConverter.GetBytes(plainText);
                byte[] signedData;

                // Create a new instance of RSACryptoServiceProvider using the
                // key from RSAParameters.
                RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
                RSAalg.FromXmlString(privateKey);

                // Hash and sign the data. Pass a new instance of SHA1CryptoServiceProvider
                // to specify the use of SHA1 for hashing.
                signedData = RSAalg.SignData(originalData, CryptoConfig.MapNameToOID("SHA256"));

                var result = Convert.ToBase64String(signedData, 0, signedData.Length);
                return result;
            }
            catch (Exception ex)
            {
                return "";
            }
        }

Verify

public static bool VerifySign(string inputText, string signedMessage, string publicKey)
        {
            bool success = false;
            using (var rsa = new RSACryptoServiceProvider())
            {
                byte[] bytesToVerify = Encoding.UTF8.GetBytes(inputText);

                byte[] signedBytes = Convert.FromBase64String(signedMessage);
                try
                {
                    rsa.FromXmlString(publicKey);

                    SHA256Managed Hash = new SHA256Managed();

                    byte[] hashedData = Hash.ComputeHash(signedBytes);

                    success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA256"), signedBytes);
                }
                catch (CryptographicException e)
                {
                    Console.WriteLine(e.Message);
                }
                finally
                {
                    rsa.PersistKeyInCsp = false;
                }
            }
            return success;
        }

همانطور که از قطعه کد فوق قابل تشخیص است برای Sign  از RSA و برای هشینگ از SHA256 استفاده شده است.

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

نظر خود را بنویسید.