前言
我们在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文件中,但是如果需要解密得到明文,必须需要私钥(这个存储在后台,不容易获取)。
调试运行,可以看到获取的密码是加密后的数据,然后在后台可以进行解密获取到明文。
总结
好了,大概就这样,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持
原文链接:http://HdhCmsTestcnblogs测试数据/isaboy/p/csharp_openssl_rsa_jsencrypt.html
dy("nrwz");
查看更多关于同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)的详细内容...