您好,登錄后才能下訂單哦!
前言
我們在Web應用中往往涉及到敏感的數據,由于HTTP協議以明文的形式與服務器進行交互,因此可以通過截獲請求的數據包進行分析來盜取有用的信息。雖然https可以對傳輸的數據進行加密,但是必須要申請證書(一般都是收費的),成本較高。那么問題來了,如果對web提交的敏感數據進行加密呢?web應用中,前端的數據處理和交互基本上都是靠javascript來完成,后臺的邏輯處理可以C#(java)等進行處理。
微軟的C#中雖然有RSA算法,但是格式和OpenSSL生成的公鑰/私鑰文件格式并不兼容。這個也給貫通前后臺的RSA加密解密帶來了難度。為了兼容OpenSSL生成的公鑰/私鑰文件格式,貫通javascript和C#的RSA加密解密算法,必須對C#內置的方法進行再度封裝。
下面以登錄為例,用戶在密碼框輸入密碼后,javascript發送ajax請求時,對密碼先進行rsa加密后再發送,服務器接收到加密后的密碼后,先對其進行解密, 然后再驗證登錄是否成功。
1、為了進行RSA加密解密,首先需要用openssl生成一對公鑰和私鑰(沒有的先下載openssl):
1) 打開openssl.exe文件,輸入 genrsa -out openssl_rsa_priv.pem 1024
此命令在openssl.exe同目錄下生成openssl_rsa_private_key.pem文件。
2) 生成公鑰 rsa -in openssl_rsa__private.pem -pubout -out openssl_rsa__public.pem
以上命令會創建如下的文件:
這個文件可以用文本編輯器進行打開,查看內容。
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0w036ClSD0LvxPROMun0u022R OJlZE6P3m+gjq3gpi4n7lo8jhTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52F AcriY5brOXUVgBLx5QMHLLd1gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1 CGllB1riNrdksSQP+wIDAQAB -----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQC0w036ClSD0LvxPROMun0u022ROJlZE6P3m+gjq3gpi4n7lo8j hTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52FAcriY5brOXUVgBLx5QMHLLd1 gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1CGllB1riNrdksSQP+wIDAQAB AoGAIOyl6lIxXKULZoBKbEqXfIz0GwxlGg1ywyn5mW2lAGQzKMken0ioBnD9xIVW rOlHyhkIvBCyuC0jgfE2Avn93MlB3j0WRuXMFlJpCBlEklMilO9Zgmwl+vTB3VZb 8VzdrEEEUBio7LWP/KvSo+IFlNjDTKgAczbLTwAmj4w6g0ECQQDm4yxPdxcU2ywZ 7PyjIMM9qnSah9KcrjU8gjEyHsUpgTjhw1cx7Peo+vRiHqxDy1yaSu1BlwRR52pC jKNnl0QhAkEAyGx3NxEIiLk2oXGGbIMZ4P6geC8gYu01BiRNWVf0Yi7+sCH68eUP oI+G5bJ8bvzXpvHjQi0s2OlRfct/qtPQmwJBALa+2DONbxdy4lUi3lO/esk0QVaO aoTY3gomggnJkQRo4zzOABXkGaIF/6gp3u9J5uG4rFFd1m19XP2Pk0ZK1AECQBYi lJAKW4zuF7CA3z3AxOzqckKTwdnrJL4G6FwDsMPfONWvCw4IJE+xSk64BbIkTpTr hhPa9WcHba6c+P6e4h0CQQDWeGMMpkqPG/w4afNCGmvRnM8vNkGUAmDGvCsfkTID ijpKl5SD55hPHsWE5rsv1TLUpkWtrFBcg61bHwMUP3cv -----END RSA PRIVATE KEY-----
2、用jsencrypt對密碼進行加密:
首先需要導入js包文件
<script src="dist/js/jsencrypt.js"></script>
var encrypt = new JSEncrypt(); var pubkey = "-----BEGIN PUBLIC KEY----- \ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAj0dPnBMf3Z4VT1B8Ee6bjKNs \ hlYj7xvGijAa8RCdmGR7mrtrExnk8mdUlwdcS05gc4SSFOyWJcYtKUHpWn8/pkS0 \ vgGOl9Bzn0Xt9hiqTb3pZAfykNrMDGZMgJgfD6KTnfzVUAOupvxjcGkcoj6/vV5I \ eMcx8mT/z3elfsDSjQIDAQAB \ -----END PUBLIC KEY-----"; encrypt.setPublicKey(pubkey); var encrypted = encrypt.encrypt($('#txtpwd').val()); //console.log(encrypted); $.ajax({ type: "POST", url: "http://localhost:24830/services/rsa_pem.ashx", data: { "pwd": encrypted }, dataType: "Json", error: function (xhr, status, error) { // alert(error); $("#txtInfo").text(' 請求服務器失敗!'); $(that).text('登 錄'); $(that).attr('disabled', false); }, success: function (json) { if (uid == "admin" && json.data=="000") { window.location.href = "index.html"; } else { $("#txtInfo").text(' 用戶名或者密碼錯誤!'); $(that).text('登 錄'); $(that).attr('disabled', false); } } });
3、后臺用C#進行解密
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace CMCloud.SaaS { public class RSACryptoService { private RSACryptoServiceProvider _privateKeyRsaProvider; private RSACryptoServiceProvider _publicKeyRsaProvider; /// <summary> /// RSA解密 /// </summary> /// <param name="cipherText"></param> /// <returns></returns> public string Decrypt(string cipherText) { if (_privateKeyRsaProvider == null) { throw new Exception("_privateKeyRsaProvider is null"); } return Decrypt2(cipherText); } /// <summary> /// RSA加密 /// </summary> /// <param name="text"></param> /// <returns></returns> public string Encrypt(string text) { if (_publicKeyRsaProvider == null) { throw new Exception("_publicKeyRsaProvider is null"); } return Encrypt2(text); //return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), false)); } private string Encrypt2(string text) { Byte[] PlaintextData = Encoding.UTF8.GetBytes(text); int MaxBlockSize = _publicKeyRsaProvider.KeySize / 8 - 11;//加密塊最大長度限制 if (PlaintextData.Length <= MaxBlockSize) { return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(PlaintextData, false)); } else { using (MemoryStream PlaiStream = new MemoryStream(PlaintextData)) using (MemoryStream CrypStream = new MemoryStream()) { Byte[] Buffer = new Byte[MaxBlockSize]; int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize); while (BlockSize > 0) { Byte[] ToEncrypt = new Byte[BlockSize]; Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize); Byte[] Cryptograph = _publicKeyRsaProvider.Encrypt(ToEncrypt, false); CrypStream.Write(Cryptograph, 0, Cryptograph.Length); BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize); } return Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None); } } } private string Decrypt2(string ciphertext) { Byte[] CiphertextData = Convert.FromBase64String(ciphertext); int MaxBlockSize = _privateKeyRsaProvider.KeySize / 8; //解密塊最大長度限制 if (CiphertextData.Length <= MaxBlockSize) return System.Text.Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(CiphertextData, false)); using (MemoryStream CrypStream = new MemoryStream(CiphertextData)) using (MemoryStream PlaiStream = new MemoryStream()) { Byte[] Buffer = new Byte[MaxBlockSize]; int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize); while (BlockSize > 0) { Byte[] ToDecrypt = new Byte[BlockSize]; Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize); Byte[] Plaintext = _privateKeyRsaProvider.Decrypt(ToDecrypt, false); PlaiStream.Write(Plaintext, 0, Plaintext.Length); BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize); } return System.Text.Encoding.UTF8.GetString(PlaiStream.ToArray()); } } public RSACryptoService(string privateKey, string publicKey = null) { if (!string.IsNullOrEmpty(privateKey)) { _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); } if (!string.IsNullOrEmpty(publicKey)) { _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); } } private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey) { var privateKeyBits = System.Convert.FromBase64String(privateKey); var RSA = new RSACryptoServiceProvider(); var RSAparams = new RSAParameters(); using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) { byte bt = 0; ushort twobytes = 0; twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) binr.ReadByte(); else if (twobytes == 0x8230) binr.ReadInt16(); else throw new Exception("Unexpected value read binr.ReadUInt16()"); twobytes = binr.ReadUInt16(); if (twobytes != 0x0102) throw new Exception("Unexpected version"); bt = binr.ReadByte(); if (bt != 0x00) throw new Exception("Unexpected value read binr.ReadByte()"); RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.D = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.P = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr)); RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); } RSA.ImportParameters(RSAparams); return RSA; } private int GetIntegerSize(BinaryReader binr) { byte bt = 0; byte lowbyte = 0x00; byte highbyte = 0x00; int count = 0; bt = binr.ReadByte(); if (bt != 0x02) return 0; bt = binr.ReadByte(); if (bt == 0x81) count = binr.ReadByte(); else if (bt == 0x82) { highbyte = binr.ReadByte(); lowbyte = binr.ReadByte(); byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; count = BitConverter.ToInt32(modint, 0); } else { count = bt; } while (binr.ReadByte() == 0x00) { count -= 1; } binr.BaseStream.Seek(-1, SeekOrigin.Current); return count; } private RSACryptoServiceProvider CreateRsaProviderFromPublicKey(string publicKeyString) { // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; byte[] x509key; byte[] seq = new byte[15]; int x509size; x509key = Convert.FromBase64String(publicKeyString); x509size = x509key.Length; // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ using (MemoryStream mem = new MemoryStream(x509key)) { using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading { byte bt = 0; ushort twobytes = 0; twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; seq = binr.ReadBytes(15); //read the Sequence OID if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct return null; twobytes = binr.ReadUInt16(); if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8203) binr.ReadInt16(); //advance 2 bytes else return null; bt = binr.ReadByte(); if (bt != 0x00) //expect null byte next return null; twobytes = binr.ReadUInt16(); if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) binr.ReadByte(); //advance 1 byte else if (twobytes == 0x8230) binr.ReadInt16(); //advance 2 bytes else return null; twobytes = binr.ReadUInt16(); byte lowbyte = 0x00; byte highbyte = 0x00; if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus else if (twobytes == 0x8202) { highbyte = binr.ReadByte(); //advance 2 bytes lowbyte = binr.ReadByte(); } else return null; byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order int modsize = BitConverter.ToInt32(modint, 0); int firstbyte = binr.PeekChar(); if (firstbyte == 0x00) { //if first byte (highest order) of modulus is zero, don't include it binr.ReadByte(); //skip this null byte modsize -= 1; //reduce modulus buffer size by 1 } byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data return null; int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) byte[] exponent = binr.ReadBytes(expbytes); // ------- create RSACryptoServiceProvider instance and initialize with public key ----- RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); RSAParameters RSAKeyInfo = new RSAParameters(); RSAKeyInfo.Modulus = modulus; RSAKeyInfo.Exponent = exponent; RSA.ImportParameters(RSAKeyInfo); return RSA; } } } private bool CompareBytearrays(byte[] a, byte[] b) { if (a.Length != b.Length) return false; int i = 0; foreach (byte c in a) { if (c != b[i]) return false; i++; } return true; } } }
雖然將公鑰暴露在js文件中,但是如果需要解密得到明文,必須需要私鑰(這個存儲在后臺,不容易獲取)。
調試運行,可以看到獲取的密碼是加密后的數據,然后在后臺可以進行解密獲取到明文。
總結
好了,大概就這樣,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對億速云的支持
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。