欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)

程序员文章站 2023-12-04 14:40:28
前言 我们在web应用中往往涉及到敏感的数据,由于http协议以明文的形式与服务器进行交互,因此可以通过截获请求的数据包进行分析来盗取有用的信息。虽然https可以对传输...

前言

我们在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

同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)

此命令在openssl.exe同目录下生成openssl_rsa_private_key.pem文件。

  2) 生成公钥 rsa  -in openssl_rsa__private.pem -pubout -out openssl_rsa__public.pem

同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)

  以上命令会创建如下的文件:

同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)

这个文件可以用文本编辑器进行打开,查看内容。

-----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和C#的RSA加密解密算法详解(对web提交的数据加密传输)

虽然将公钥暴露在js文件中,但是如果需要解密得到明文,必须需要私钥(这个存储在后台,不容易获取)。

调试运行,可以看到获取的密码是加密后的数据,然后在后台可以进行解密获取到明文。

同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输) 

总结

好了,大概就这样,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持