前言
我們?cè)赪eb應(yīng)用中往往涉及到敏感的數(shù)據(jù),由于HTTP協(xié)議以明文的形式與服務(wù)器進(jìn)行交互,因此可以通過(guò)截獲請(qǐng)求的數(shù)據(jù)包進(jìn)行分析來(lái)盜取有用的信息。雖然https可以對(duì)傳輸?shù)臄?shù)據(jù)進(jìn)行加密,但是必須要申請(qǐng)證書(shū)(一般都是收費(fèi)的),成本較高。那么問(wèn)題來(lái)了,如果對(duì)web提交的敏感數(shù)據(jù)進(jìn)行加密呢?web應(yīng)用中,前端的數(shù)據(jù)處理和交互基本上都是靠javascript來(lái)完成,后臺(tái)的邏輯處理可以C#(java)等進(jìn)行處理。
微軟的C#中雖然有RSA算法,但是格式和OpenSSL生成的公鑰/私鑰文件格式并不兼容。這個(gè)也給貫通前后臺(tái)的RSA加密解密帶來(lái)了難度。為了兼容OpenSSL生成的公鑰/私鑰文件格式,貫通javascript和C#的RSA加密解密算法,必須對(duì)C#內(nèi)置的方法進(jìn)行再度封裝。
下面以登錄為例,用戶在密碼框輸入密碼后,javascript發(fā)送ajax請(qǐng)求時(shí),對(duì)密碼先進(jìn)行rsa加密后再發(fā)送,服務(wù)器接收到加密后的密碼后,先對(duì)其進(jìn)行解密, 然后再驗(yàn)證登錄是否成功。
1、為了進(jìn)行RSA加密解密,首先需要用openssl生成一對(duì)公鑰和私鑰(沒(méi)有的先下載openssl):
1) 打開(kāi)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

以上命令會(huì)創(chuàng)建如下的文件:

這個(gè)文件可以用文本編輯器進(jìn)行打開(kāi),查看內(nèi)容。
-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0w036ClSD0LvxPROMun0u022ROJlZE6P3m+gjq3gpi4n7lo8jhTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52FAcriY5brOXUVgBLx5QMHLLd1gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1CGllB1riNrdksSQP+wIDAQAB-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----MIICXQIBAAKBgQC0w036ClSD0LvxPROMun0u022ROJlZE6P3m+gjq3gpi4n7lo8jhTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52FAcriY5brOXUVgBLx5QMHLLd1gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1CGllB1riNrdksSQP+wIDAQABAoGAIOyl6lIxXKULZoBKbEqXfIz0GwxlGg1ywyn5mW2lAGQzKMken0ioBnD9xIVWrOlHyhkIvBCyuC0jgfE2Avn93MlB3j0WRuXMFlJpCBlEklMilO9Zgmwl+vTB3VZb8VzdrEEEUBio7LWP/KvSo+IFlNjDTKgAczbLTwAmj4w6g0ECQQDm4yxPdxcU2ywZ7PyjIMM9qnSah9KcrjU8gjEyHsUpgTjhw1cx7Peo+vRiHqxDy1yaSu1BlwRR52pCjKNnl0QhAkEAyGx3NxEIiLk2oXGGbIMZ4P6geC8gYu01BiRNWVf0Yi7+sCH68eUPoI+G5bJ8bvzXpvHjQi0s2OlRfct/qtPQmwJBALa+2DONbxdy4lUi3lO/esk0QVaOaoTY3gomggnJkQRo4zzOABXkGaIF/6gp3u9J5uG4rFFd1m19XP2Pk0ZK1AECQBYilJAKW4zuF7CA3z3AxOzqckKTwdnrJL4G6FwDsMPfONWvCw4IJE+xSk64BbIkTpTrhhPa9WcHba6c+P6e4h0CQQDWeGMMpkqPG/w4afNCGmvRnM8vNkGUAmDGvCsfkTIDijpKl5SD55hPHsWE5rsv1TLUpkWtrFBcg61bHwMUP3cv-----END RSA PRIVATE KEY-----
2、用jsencrypt對(duì)密碼進(jìn)行加密:
首先需要導(dǎo)入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(' 請(qǐng)求服務(wù)器失敗!');  $(that).text('登 錄');  $(that).attr('disabled', false); }, success: function (json) {  if (uid == "admin" && json.data=="000") {  window.location.href = "index.html"; } else {  $("#txtInfo").text(' 用戶名或者密碼錯(cuò)誤!');  $(that).text('登 錄');  $(that).attr('disabled', false); } }});3、后臺(tái)用C#進(jìn)行解密
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;//加密塊最大長(zhǎng)度限制  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; //解密塊最大長(zhǎng)度限制  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文件中,但是如果需要解密得到明文,必須需要私鑰(這個(gè)存儲(chǔ)在后臺(tái),不容易獲取)。
調(diào)試運(yùn)行,可以看到獲取的密碼是加密后的數(shù)據(jù),然后在后臺(tái)可以進(jìn)行解密獲取到明文。
 
總結(jié)
好了,大概就這樣,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)武林網(wǎng)的支持
新聞熱點(diǎn)
疑難解答
圖片精選