前言
我们在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
以上命令会创建如下的文件:
这个文件可以用文本编辑器进行打开,查看内容。
1
2
3
4
5
6
|
-----begin public key----- migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqc0w036clsd0lvxpromun0u022r ojlze6p3m+gjq3gpi4n7lo8jhtqmqgccdbvjqnifmzws9o3lnlqxwtxj3b4xj52f acriy5broxuvgblx5qmhlld1gtjnmg4i7r4ytgx7xvkrnojr6zca1yns0lbggdf1 cgllb1rinrdkssqp+widaqab -----end public key----- |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
-----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包文件
1
|
< script src = "dist/js/jsencrypt.js" ></ script > |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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#进行解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
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://www.cnblogs.com/isaboy/p/csharp_openssl_rsa_jsencrypt.html