2015年9月17日 星期四

如何在C# 和JavaScript 用AES 加解密

    作者使用WebAPI(C#)來傳遞資料,但擔心個資法等議題,特別針對個人隱私的資料加密,以免資料在網站上暴露,其中加密的工具,網站上也有不少,其中c# 我就使用.net framework 4.5 提供System.Security.Cryptography 來加密WebAPI的資料,再由JavaScript ( aes.js) 負責將資料解密回來,這樣就能保護個人穩私的資料,以下是示範教學。

一、 C# 的加密/解密函式

public class AESEncrytDecry
    {
//解密資料
        public static string DecryptStringAES(string cipherText)
        {
            var keybytes = Encoding.UTF8.GetBytes("8080808080808080");   //自行設定,但要與JavaScript 一致
            var iv = Encoding.UTF8.GetBytes("8080808080808080"); // 自行設定,但要與JavaScript 一致
            var encrypted = Convert.FromBase64String(cipherText);
            var decriptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv);
            return string.Format(decriptedFromJavascript);
        }

        private static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv)
        {
           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");
            }          
            string plaintext = null;
            using (var rijAlg = new RijndaelManaged())
            {
                rijAlg.Mode = CipherMode.CBC;
                rijAlg.Padding = PaddingMode.PKCS7;
                rijAlg.FeedbackSize = 128;
                rijAlg.Key = key;
                rijAlg.IV = iv;
                var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
                try
                {
                    using (var msDecrypt = new MemoryStream(cipherText))
                    {
                        using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (var srDecrypt = new StreamReader(csDecrypt))
                            {                               
                                plaintext = srDecrypt.ReadToEnd();
                            }
                        }
                    }
                }
                catch
                {
                    plaintext = "keyError";
                }
            }
            return plaintext;
        }
       //加密資料
        public static string EncryptStringAES(string cipherText)
        {
            var keybytes = Encoding.UTF8.GetBytes("8080808080808080");   //自行設定
            var iv = Encoding.UTF8.GetBytes("8080808080808080");         //自行設定
            var EncryptString = EncryptStringToBytes(cipherText, keybytes, iv);
            return Convert.ToBase64String(EncryptString);
        }
        private static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)
        {         
            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;
            using (var rijAlg = new RijndaelManaged())
            {
                rijAlg.Mode = CipherMode.CBC;
                rijAlg.Padding = PaddingMode.PKCS7;
                rijAlg.FeedbackSize = 128;
                rijAlg.Key = key;
                rijAlg.IV = iv;
                var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
                using (var msEncrypt = new MemoryStream())
                {
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }
            return encrypted;
        }
    }

   
範例程式片段:透過實體層的函式,已將部份回傳值加密

foreach (DataRow dr in dt.Rows)
{
    viewPatientObj.sickNo =  AESEncrytDecry.EncryptStringAES(dr["病歷編號"].ToString()); 
    viewPatientObj.name = dr["病患姓名"].ToString(); 
    viewPatientObj.sex = dr["性別"].ToString();
    viewPatientObj.birthDay = AESEncrytDecry.EncryptStringAES(dr["出生日期"].ToString());
    viewPatientObj.mobile = AESEncrytDecry.EncryptStringAES(dr["行動電話"].ToString());
    viewPatientObj.tel = AESEncrytDecry.EncryptStringAES(dr["住宅電話"].ToString());
    viewPatientObj.address = AESEncrytDecry.EncryptStringAES(dr["地址"].ToString());
}


Web API:
        [EnableCors(
          origins: "*",
          headers: "accept,content-type,origin",
          methods: "get,post"), HttpGet]
        [Route("api/values/readPatient/{sickNo}/{pregistDate}")]
        public viewPatient readPatient(string sickNo,string pregistDate)
        {
            return controlClinicObj.readPatient(sickNo, pregistDate);
        }
l   加密:   AESEncrytDecry.EncryptStringAES(文字);
l   解密:   AESEncrytDecry. DecryptStringAES (文字);

二、 JavaScript 的加密/解密函式


<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="scripts/aesEncrytDecry.js"></script>


檔名aesEncrytDecry.js

<script>
var key = CryptoJS.enc.Utf8.parse('8080808080808080');  //需要和伺服器端一致,否則… 無法解密
var iv = CryptoJS.enc.Utf8.parse('8080808080808080');   //需要和伺服器端一致,否則… 無法解密
var aesEncryDecry = {
    decryptStringAES: function (strEncryptText) {
        var decrypted = CryptoJS.AES.decrypt(strEncryptText, key, {
            keySize: 128 / 8,
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return decrypted.toString(CryptoJS.enc.Utf8);
    },
    encryptStringAES: function (strOrignText) {
        var encrypted = CryptoJS.AES.encrypt(strOrignText, key,{
            keySize: 128 / 8,
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return encrypted.toString();
    }
};
</script>

Javascript 的範例程式: 解密
  
$.ajax({
        url: 'http://' + clinicIP + '/webClinic/api/values/readPatient/' + sickNo + "/" + date,
        type: 'GET',
        error: function (error)
        {
            alert(error)
        },
        success: function (data) {
            $('#patientPage_name').html(data.name + "(" + data.sex + ")");
            var birthday = "";
            if (data.birthDay != null) {
                var decodeBirthDay =aesEncryDecry.decryptStringAES(data.birthDay);
                if (decodeBirthDay.trim() != "") {
                    birthday = decodeBirthDay.substring(0, 3) + "-" + decodeBirthDay.substring(3, 5) + "-" + decodeBirthDay.substring(5, 7);
                }
            }           
            $('#patientPage_birthday').html(birthday);
            $('#patientPage_addr').html(aesEncryDecry.decryptStringAES(data.address));
            $('#patientPage_tel').val(aesEncryDecry.decryptStringAES(data.tel));
            $('#patientPage_mobile').val(aesEncryDecry.decryptStringAES(data.mobile));

            //解密約診資料,如果空值就填空白
            var pregistContext = "";
            if (data.pregistContext != null) {
                pregistContext = aesEncryDecry.decryptStringAES(data.pregistContext);
            }

            $('#patientPage_pregistContext').val(pregistContext);
        }
    });



l   加密:aesEncryDecry. encryptStringAES (文字)
l   解密:aesEncryDecry.decryptStringAES(文字)

三、 示範結果

傳送資料是經過加密
  
Codova APP JS解譯後,回復原始的資料





















    對於AES這方面有困擾的朋友們,不知道是否有幫助到各位,感謝各位耐心的看完本篇文章,未來也能繼續期許能寫出好文章,讓網路的資源更豐富。

參考文獻: