前言
我們在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