前言
我们在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-----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对密码进行加密:
首先需要导入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,MaxBlockSize); while (BlockSize > 0) { Byte[] ToEncrypt = new Byte[BlockSize]; Array.copy(Buffer,ToEncrypt,BlockSize); Byte[] Cryptograph = _publicKeyRsaProvIDer.Encrypt(ToEncrypt,false); CrypStream.Write(Cryptograph,Cryptograph.Length); BlockSize = PlaiStream.Read(Buffer,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,MaxBlockSize); while (BlockSize > 0) { Byte[] ToDecrypt = new Byte[BlockSize]; Array.copy(Buffer,ToDecrypt,BlockSize); Byte[] Plaintext = _privateKeyRsaProvIDer.Decrypt(ToDecrypt,false); PlaiStream.Write(Plaintext,Plaintext.Length); BlockSize = CrypStream.Read(Buffer,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,0xF7,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,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文件中,但是如果需要解密得到明文,必须需要私钥(这个存储在后台,不容易获取)。
调试运行,可以看到获取的密码是加密后的数据,然后在后台可以进行解密获取到明文。
总结
好了,大概就这样,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对编程小技巧的支持
总结以上是内存溢出为你收集整理的同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)全部内容,希望文章能够帮你解决同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)