兼容javascript和C#的RSA加密解密算法,对web提交的数据进行加密传输

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 微软的C#中虽然有RSA算法,但是格式和OpenSSL生成的公钥/私钥文件格式并不兼容。这个也给贯通前后台的RSA加密解密带来了难度。为了兼容OpenSSL生成的公钥/私钥文件格式,贯通javascript和C#的RSA加密解密算法,必须对C#内置的方法进行再度封装。

  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

76497-20161107163648295-1668761517.png

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

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

76497-20161107164135842-920775863.png

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

76497-20161107164255092-2015288419.png

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

-----BEGINPUBLICKEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0w036ClSD0LvxPROMun0u022ROJlZE6P3m+gjq3gpi4n7lo8jhTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52FAcriY5brOXUVgBLx5QMHLLd1gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1CGllB1riNrdksSQP+wIDAQAB-----ENDPUBLICKEY-----
-----BEGINRSAPRIVATEKEY-----MIICXQIBAAKBgQC0w036ClSD0LvxPROMun0u022ROJlZE6P3m+gjq3gpi4n7lo8jhTqMqgccDbVJqnIfMzWS9O3lnlQXWTxJ3B4XJ52FAcriY5brOXUVgBLx5QMHLLd1gtJnmG4i7r4ytgX7XVKRnojR6zca1YnS0lbGGDF1CGllB1riNrdksSQP+wIDAQABAoGAIOyl6lIxXKULZoBKbEqXfIz0GwxlGg1ywyn5mW2lAGQzKMken0ioBnD9xIVWrOlHyhkIvBCyuC0jgfE2Avn93MlB3j0WRuXMFlJpCBlEklMilO9Zgmwl+vTB3VZb8VzdrEEEUBio7LWP/KvSo+IFlNjDTKgAczbLTwAmj4w6g0ECQQDm4yxPdxcU2ywZ7PyjIMM9qnSah9KcrjU8gjEyHsUpgTjhw1cx7Peo+vRiHqxDy1yaSu1BlwRR52pCjKNnl0QhAkEAyGx3NxEIiLk2oXGGbIMZ4P6geC8gYu01BiRNWVf0Yi7+sCH68eUPoI+G5bJ8bvzXpvHjQi0s2OlRfct/qtPQmwJBALa+2DONbxdy4lUi3lO/esk0QVaOaoTY3gomggnJkQRo4zzOABXkGaIF/6gp3u9J5uG4rFFd1m19XP2Pk0ZK1AECQBYilJAKW4zuF7CA3z3AxOzqckKTwdnrJL4G6FwDsMPfONWvCw4IJE+xSk64BbIkTpTrhhPa9WcHba6c+P6e4h0CQQDWeGMMpkqPG/w4afNCGmvRnM8vNkGUAmDGvCsfkTIDijpKl5SD55hPHsWE5rsv1TLUpkWtrFBcg61bHwMUP3cv-----ENDRSAPRIVATEKEY-----

2 用jsencrypt对密码进行加密:

首先需要导入js包文件:

<scriptsrc="dist/js/jsencrypt.js"></script>

然后编写JS加密算法,示例如下:

varencrypt=newJSEncrypt();
varpubkey="-----BEGIN PUBLIC KEY----- \MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAj0dPnBMf3Z4VT1B8Ee6bjKNs \hlYj7xvGijAa8RCdmGR7mrtrExnk8mdUlwdcS05gc4SSFOyWJcYtKUHpWn8/pkS0 \vgGOl9Bzn0Xt9hiqTb3pZAfykNrMDGZMgJgfD6KTnfzVUAOupvxjcGkcoj6/vV5I \eMcx8mT/z3elfsDSjQIDAQAB \-----END PUBLIC KEY-----";
encrypt.setPublicKey(pubkey);
varencrypted=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#进行解密

usingSystem;
usingSystem.Collections.Generic;
usingSystem.IO;
usingSystem.Linq;
usingSystem.Security.Cryptography;
usingSystem.Text;
usingSystem.Threading.Tasks;
namespaceCMCloud.SaaS{
publicclassRSACryptoService    {
privateRSACryptoServiceProvider_privateKeyRsaProvider;
privateRSACryptoServiceProvider_publicKeyRsaProvider;
/// <summary>/// RSA解密/// </summary>/// <param name="cipherText"></param>/// <returns></returns>publicstringDecrypt(stringcipherText)
        {
if (_privateKeyRsaProvider==null)
            {
thrownewException("_privateKeyRsaProvider is null");
            }
returnDecrypt2(cipherText);
        }
/// <summary>/// RSA加密/// </summary>/// <param name="text"></param>/// <returns></returns>publicstringEncrypt(stringtext)
        {
if (_publicKeyRsaProvider==null)
            {
thrownewException("_publicKeyRsaProvider is null");
            }
returnEncrypt2(text);
//return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), false));        }
privatestringEncrypt2(stringtext)
        {
Byte[] PlaintextData=Encoding.UTF8.GetBytes(text);
intMaxBlockSize=_publicKeyRsaProvider.KeySize/8-11;//加密块最大长度限制if (PlaintextData.Length<=MaxBlockSize)
            {
returnConvert.ToBase64String(_publicKeyRsaProvider.Encrypt(PlaintextData, false));
            }
else            {
using (MemoryStreamPlaiStream=newMemoryStream(PlaintextData))
using (MemoryStreamCrypStream=newMemoryStream())
                {
Byte[] Buffer=newByte[MaxBlockSize];
intBlockSize=PlaiStream.Read(Buffer, 0, MaxBlockSize);
while (BlockSize>0)
                    {
Byte[] ToEncrypt=newByte[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);
                    }
returnConvert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                }
            }
        }
privatestringDecrypt2(stringciphertext)
        {
Byte[] CiphertextData=Convert.FromBase64String(ciphertext);
intMaxBlockSize=_privateKeyRsaProvider.KeySize/8;    //解密块最大长度限制if (CiphertextData.Length<=MaxBlockSize)
returnSystem.Text.Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(CiphertextData, false));
using (MemoryStreamCrypStream=newMemoryStream(CiphertextData))
using (MemoryStreamPlaiStream=newMemoryStream())
            {
Byte[] Buffer=newByte[MaxBlockSize];
intBlockSize=CrypStream.Read(Buffer, 0, MaxBlockSize);
while (BlockSize>0)
                {
Byte[] ToDecrypt=newByte[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);
                }
returnSystem.Text.Encoding.UTF8.GetString(PlaiStream.ToArray());
            }
        }
publicRSACryptoService(stringprivateKey, stringpublicKey=null)
        {
if (!string.IsNullOrEmpty(privateKey))
            {
_privateKeyRsaProvider=CreateRsaProviderFromPrivateKey(privateKey);
            }
if (!string.IsNullOrEmpty(publicKey))
            {
_publicKeyRsaProvider=CreateRsaProviderFromPublicKey(publicKey);
            }
        }
privateRSACryptoServiceProviderCreateRsaProviderFromPrivateKey(stringprivateKey)
        {
varprivateKeyBits=System.Convert.FromBase64String(privateKey);
varRSA=newRSACryptoServiceProvider();
varRSAparams=newRSAParameters();
using (BinaryReaderbinr=newBinaryReader(newMemoryStream(privateKeyBits)))
            {
bytebt=0;
ushorttwobytes=0;
twobytes=binr.ReadUInt16();
if (twobytes==0x8130)
binr.ReadByte();
elseif (twobytes==0x8230)
binr.ReadInt16();
elsethrownewException("Unexpected value read binr.ReadUInt16()");
twobytes=binr.ReadUInt16();
if (twobytes!=0x0102)
thrownewException("Unexpected version");
bt=binr.ReadByte();
if (bt!=0x00)
thrownewException("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);
returnRSA;
        }
privateintGetIntegerSize(BinaryReaderbinr)
        {
bytebt=0;
bytelowbyte=0x00;
bytehighbyte=0x00;
intcount=0;
bt=binr.ReadByte();
if (bt!=0x02)
return0;
bt=binr.ReadByte();
if (bt==0x81)
count=binr.ReadByte();
elseif (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);
returncount;
        }
privateRSACryptoServiceProviderCreateRsaProviderFromPublicKey(stringpublicKeyString)
        {
// 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=newbyte[15];
intx509size;
x509key=Convert.FromBase64String(publicKeyString);
x509size=x509key.Length;
// ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------using (MemoryStreammem=newMemoryStream(x509key))
            {
using (BinaryReaderbinr=newBinaryReader(mem))  //wrap Memory Stream with BinaryReader for easy reading                {
bytebt=0;
ushorttwobytes=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 byteelseif (twobytes==0x8230)
binr.ReadInt16();   //advance 2 byteselsereturnnull;
seq=binr.ReadBytes(15);       //read the Sequence OIDif (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correctreturnnull;
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 byteelseif (twobytes==0x8203)
binr.ReadInt16();   //advance 2 byteselsereturnnull;
bt=binr.ReadByte();
if (bt!=0x00)     //expect null byte nextreturnnull;
twobytes=binr.ReadUInt16();
if (twobytes==0x8130) //data read as little endian order (actual data order for Sequence is 30 81)binr.ReadByte();    //advance 1 byteelseif (twobytes==0x8230)
binr.ReadInt16();   //advance 2 byteselsereturnnull;
twobytes=binr.ReadUInt16();
bytelowbyte=0x00;
bytehighbyte=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 moduluselseif (twobytes==0x8202)
                    {
highbyte=binr.ReadByte(); //advance 2 byteslowbyte=binr.ReadByte();
                    }
elsereturnnull;
byte[] modint= { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian orderintmodsize=BitConverter.ToInt32(modint, 0);
intfirstbyte=binr.PeekChar();
if (firstbyte==0x00)
                    {   //if first byte (highest order) of modulus is zero, don't include itbinr.ReadByte();    //skip this null bytemodsize-=1;   //reduce modulus buffer size by 1                    }
byte[] modulus=binr.ReadBytes(modsize);   //read the modulus bytesif (binr.ReadByte() !=0x02)            //expect an Integer for the exponent datareturnnull;
intexpbytes= (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 -----RSACryptoServiceProviderRSA=newRSACryptoServiceProvider();
RSAParametersRSAKeyInfo=newRSAParameters();
RSAKeyInfo.Modulus=modulus;
RSAKeyInfo.Exponent=exponent;
RSA.ImportParameters(RSAKeyInfo);
returnRSA;
                }
            }
        }
privateboolCompareBytearrays(byte[] a, byte[] b)
        {
if (a.Length!=b.Length)
returnfalse;
inti=0;
foreach (bytecina)
            {
if (c!=b[i])
returnfalse;
i++;
            }
returntrue;
        }
    }
}

     虽然将公钥暴露在js文件中,但是如果需要解密得到明文,必须需要私钥(这个存储在后台,不容易获取)。调试运行,可以看到获取的密码是加密后的数据,然后在后台可以进行解密获取到明文。

相关文章
|
1月前
|
SQL 缓存 分布式计算
C#如何处理上亿级数据的查询效率
C#如何处理上亿级数据的查询效率
22 1
|
3月前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法
|
3月前
|
JavaScript 算法 前端开发
JS算法必备之Array常用操作方法
这篇文章详细介绍了JavaScript中数组的创建、检测、转换、排序、操作方法以及迭代方法等,提供了数组操作的全面指南。
JS算法必备之Array常用操作方法
|
1月前
|
中间件 数据库连接 API
C#数据分表核心代码
C#数据分表核心代码
35 0
|
2月前
|
存储 C# 开发者
枚举与结构体的应用:C#中的数据组织艺术
在C#编程中,枚举(`enum`)和结构体(`struct`)是非常重要的数据类型。枚举用于定义命名常量集合,提高代码可读性;结构体则封装相关数据字段,适合小型数据集。本文从基本概念入手,探讨它们的使用技巧、常见问题及解决方案,帮助开发者更好地利用这些特性构建健壮的应用程序。
41 8
|
1月前
|
XML JSON 前端开发
C#使用HttpClient四种请求数据格式:json、表单数据、文件上传、xml格式
C#使用HttpClient四种请求数据格式:json、表单数据、文件上传、xml格式
356 0
|
1月前
|
API C#
异步轮询 Web API 的实现与 C# 示例
异步轮询 Web API 的实现与 C# 示例
78 0
|
3月前
|
前端开发 JavaScript C#
C#开发者的新天地:Blazor如何颠覆传统Web开发,打造下一代交互式UI?
【8月更文挑战第28天】Blazor 是 .NET 生态中的革命性框架,允许使用 C# 和 .NET 构建交互式 Web UI,替代传统 JavaScript。本文通过问答形式深入探讨 Blazor 的基本概念、优势及应用场景,并指导如何开始使用 Blazor。Blazor 支持代码共享、强类型检查和丰富的生态系统,简化 Web 开发流程。通过简单的命令即可创建 Blazor 应用,并利用其组件化和数据绑定特性快速搭建界面。无论对于 .NET 还是 Web 开发者,Blazor 都是一个值得尝试的新选择。
118 1
|
3月前
|
前端开发 JavaScript 开发者
【Web 前端】彻底告别“this”指向困扰:从零开始掌握 JavaScript 中“this”的奥秘
【8月更文挑战第23天】在 JavaScript 中,`this` 关键字的指向是根据其调用上下文动态确定的,这对于 Web 前端开发者而言是一项核心技能。本文通过多个示例解释了 `this` 的指向规则:在对象方法中指向该对象,在独立函数中指向全局对象或 `undefined`,在事件处理器中指向触发事件的 DOM 元素,在构造函数中指向新创建的对象。此外,还介绍了一些技巧,如使用箭头函数、`bind` 方法以及在事件处理器中显式保存 `this` 的引用,以帮助开发者更好地理解和控制 `this` 的指向。
59 1
|
3月前
|
前端开发 JavaScript 安全

热门文章

最新文章