我是靠谱客的博主 发嗲火,这篇文章主要介绍详解c#与js的rsa加密互通,现在分享给大家,希望可以做个参考。

ASN.1

 抽象语法表示(标记)ASN.1(Abstract Syntax Notation One )一种数据定义语言,描述了对数据进行表示、编码、传输和解码的数据格式。网络管理系统中的管理信息库(MIB)、应用程序的数据结构、协议数据单元(PDU)都是用ASN.1定义的。

可以理解为ASN.1是对密钥结构定义的一种规范

密钥结构类型

PKCS#1

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
RSAPublicKey ::= SEQUENCE { modulus INTEGER, -- n publicExponent INTEGER -- e } RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL }

PKCS#8

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, PublicKey BIT STRING ; 其中的BIT STRING是某个算法自己指定的二进制格式 ; RSA算法的话,就是上面的RSAPublicKey } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } PrivateKeyInfo ::= SEQUENCE { version Version, algorithm AlgorithmIdentifier, PrivateKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }

密钥编码类型

der格式

二进制格式

pem格式

把der格式的数据用base64编码后,然后再在头尾加上一段“-----”开始的标记

证书类型

X.509证书

X.509只包含公钥,没有私钥,这种证书一般公开发布,可用于放在客服端使用,用于加密、验签

PKCS#12证书

因为X.509证书只包含公钥,但有些时候我们需要把私钥和公钥合并成一个证书,放在服务端使用,用于解密、签名。

PKCS#12就定义了这样一种证书,它既包含了公钥有包含了私钥。典型的入pfx、p12证书就是PKCS#12证书。

PKCS#7证书

当你收到一个网站的证书后,你需要验证其真实性。因为一个X.509证书包含了公钥、持有人信息、签名。为了验证其真实性,你需要签证其签名,而验证签名则需要签发的CA机构的公钥证书。同样原理,当你拿到CA机构的公钥证书后,你也需要验证该CA机构的真实性,而验证该CA机构的证书,你需要该CA上级机构的CA公钥证书...以此类推,你需要一直验证到根证书为止。所以为了验证一个网站证书的真实性,你需要的不仅一张证书,而是一个证书链。而PKCS#7就定义了这样一个证书链的类型结构。典型如p7b后缀名的证书就是这样的格式。

证书后缀

.cer/.crt:存放公钥,没有私钥,就是一个X.509证书,二进制形式存放

.pfx/.p12:存放公钥和私钥,通常包含保护密码,二进制方式

证书与密钥关系

数字证书和私钥是匹配的关系。就好比钥匙牌和钥匙的关系。在数字证书签发的时候,数字证书签发系统(CA系统),在生成数字证书的同时,还会随机生成一对密钥,一个私钥,一个公钥。数字证书标示用户身份, 相匹配的私钥和公钥,则是用来保障用户身份的可认证性。就好比咱们拿着一串钥匙,每个钥匙上都标明有时某某房间的钥匙,但是否是真的,还需要看能不能打开相应的房门。

密钥生成

复制代码
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
/// <summary> /// 取得私钥和公钥 XML 格式,返回数组第一个是私钥,第二个是公钥. /// </summary> /// <param name="size">密钥长度,默认1024,可以为2048</param> /// <returns></returns> public static string[] CreateXmlKey(int size = 1024) { //密钥格式要生成pkcs#1格式的 而不是pkcs#8格式的 RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size); string privateKey = sp.ToXmlString(true);//private key string publicKey = sp.ToXmlString(false);//public key return new string[] { privateKey, publicKey }; } /// <summary> /// 取得私钥和公钥 CspBlob 格式,返回数组第一个是私钥,第二个是公钥. /// </summary> /// <param name="size"></param> /// <returns></returns> public static string[] CreateCspBlobKey(int size = 1024) { //密钥格式要生成pkcs#1格式的 而不是pkcs#8格式的 RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size); string privateKey = System.Convert.ToBase64String(sp.ExportCspBlob(true));//private key string publicKey = System.Convert.ToBase64String(sp.ExportCspBlob(false));//public key return new string[] { privateKey, publicKey }; } /// <summary> /// 导出PEM PKCS#1格式密钥对,返回数组第一个是私钥,第二个是公钥. /// </summary> public static string[] CreateKey_PEM_PKCS1(int size = 1024) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size); string privateKey = RSA_PEM.ToPEM(rsa, false, false); string publicKey = RSA_PEM.ToPEM(rsa, true, false); return new string[] { privateKey, publicKey }; } /// <summary> /// 导出PEM PKCS#8格式密钥对,返回数组第一个是私钥,第二个是公钥. /// </summary> public static string[] CreateKey_PEM_PKCS8(int size = 1024, bool convertToPublic = false) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size); string privateKey = RSA_PEM.ToPEM(rsa, false, true); string publicKey = RSA_PEM.ToPEM(rsa, true, true); return new string[] { privateKey, publicKey }; }

后端加/解密方法使用

复制代码
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
/// <summary> /// RSA加密 /// </summary> /// <param name="Data">原文</param> /// <param name="PublicKeyString">公钥</param> /// <param name="KeyType">密钥类型XML/PEM</param> /// <returns></returns> public static string RSAEncrypt(string Data,string PublicKeyString,string KeyType) { byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(Data); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); switch (KeyType) { case "XML": rsa.FromXmlString(PublicKeyString); break; case "PEM": rsa = RSA_PEM.FromPEM(PublicKeyString); break; default: throw new Exception("不支持的密钥类型"); } //加密块最大长度限制,如果加密数据的长度超过 秘钥长度/8-11,会引发长度不正确的异常,所以进行数据的分块加密 int MaxBlockSize = rsa.KeySize / 8 - 11; //正常长度 if (data.Length <= MaxBlockSize) { byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密 return System.Convert.ToBase64String(hashvalueEcy); } //长度超过正常值 else { using (MemoryStream PlaiStream = new MemoryStream(data)) 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 = rsa.Encrypt(ToEncrypt, false); CrypStream.Write(Cryptograph, 0, Cryptograph.Length); BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize); } return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None); } } } /// <summary> /// RSA解密 /// </summary> /// <param name="Data">密文</param> /// <param name="PrivateKeyString">私钥</param> /// <param name="KeyType">密钥类型XML/PEM</param> /// <returns></returns> public static string RSADecrypt(string Data,string PrivateKeyString, string KeyType) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); switch (KeyType) { case "XML": rsa.FromXmlString(PrivateKeyString); break; case "PEM": rsa = RSA_PEM.FromPEM(PrivateKeyString); break; default: throw new Exception("不支持的密钥类型"); } int MaxBlockSize = rsa.KeySize / 8; //解密块最大长度限制 //正常解密 if (Data.Length <= MaxBlockSize) { byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Data), false);//解密 return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy); } //分段解密 else { using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Data))) 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 = rsa.Decrypt(ToDecrypt, false); PlaiStream.Write(Plaintext, 0, Plaintext.Length); BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize); } string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray()); return output; } } }

前端加密方法

注:jsencrypt默认PKCS#1结构,生成密钥时需要注意

复制代码
1
2
3
4
5
6
7
<script src="http://passport.cnblogs.com/scripts/jsencrypt.min.js"></script> var encryptor = new JSEncrypt() // 创建加密对象实例 //之前ssl生成的公钥,复制的时候要小心不要有空格 var pubKey = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1QQRl0HlrVv6kGqhgonD6A9SU6ZJpnEN+Q0blT/ue6Ndt97WRfxtS'+ 'As0QoquTreaDtfC4RRX4o+CU6BTuHLUm+eSvxZS9TzbwoYZq7ObbQAZAY+SYDgAA5PHf1wNN20dGMFFgVS/y0ZWvv1UNa2laEz0I8Vmr5ZlzIn88GkmSiQIDAQAB-----END PUBLIC KEY-----' encryptor.setPublicKey(pubKey)//设置公钥 var rsaPassWord = encryptor.encrypt('要加密的内容') // 对内容进行加密

c#pem格式转换

注:c#的RSACryptoServiceProvider默认只支持xml格式的密钥解析

复制代码
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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
public class RSA_Unit { static public string Base64EncodeBytes(byte[] byts) { return System.Convert.ToBase64String(byts); } static public byte[] Base64DecodeBytes(string str) { try { return System.Convert.FromBase64String(str); } catch { return null; } } /// <summary> /// 把字符串按每行多少个字断行 /// </summary> static public string TextBreak(string text, int line) { var idx = 0; var len = text.Length; var str = new StringBuilder(); while (idx < len) { if (idx > 0) { str.Append('n'); } if (idx + line >= len) { str.Append(text.Substring(idx)); } else { str.Append(text.Substring(idx, line)); } idx += line; } return str.ToString(); } } static public class Extensions { /// <summary> /// 从数组start开始到指定长度复制一份 /// </summary> static public T[] sub<T>(this T[] arr, int start, int count) { T[] val = new T[count]; for (var i = 0; i < count; i++) { val[i] = arr[start + i]; } return val; } static public void writeAll(this Stream stream, byte[] byts) { stream.Write(byts, 0, byts.Length); } } 点击并拖拽以移动 public class RSA_PEM { public static RSACryptoServiceProvider FromPEM(string pem) { var rsaParams = new CspParameters(); rsaParams.Flags = CspProviderFlags.UseMachineKeyStore; var rsa = new RSACryptoServiceProvider(rsaParams); var param = new RSAParameters(); var base64 = _PEMCode.Replace(pem, ""); var data = RSA_Unit.Base64DecodeBytes(base64); if (data == null) { throw new Exception("PEM内容无效"); } var idx = 0; //读取长度 Func<byte, int> readLen = (first) => { if (data[idx] == first) { idx++; if (data[idx] == 0x81) { idx++; return data[idx++]; } else if (data[idx] == 0x82) { idx++; return (((int)data[idx++]) << 8) + data[idx++]; } else if (data[idx] < 0x80) { return data[idx++]; } } throw new Exception("PEM未能提取到数据"); }; //读取块数据 Func<byte[]> readBlock = () => { var len = readLen(0x02); if (data[idx] == 0x00) { idx++; len--; } var val = data.sub(idx, len); idx += len; return val; }; //比较data从idx位置开始是否是byts内容 Func<byte[], bool> eq = (byts) => { for (var i = 0; i < byts.Length; i++, idx++) { if (idx >= data.Length) { return false; } if (byts[i] != data[idx]) { return false; } } return true; }; if (pem.Contains("PUBLIC KEY")) { /****使用公钥****/ //读取数据总长度 readLen(0x30); if (!eq(_SeqOID)) { throw new Exception("PEM未知格式"); } //读取1长度 readLen(0x03); idx++;//跳过0x00 //读取2长度 readLen(0x30); //Modulus param.Modulus = readBlock(); //Exponent param.Exponent = readBlock(); } else if (pem.Contains("PRIVATE KEY")) { /****使用私钥****/ //读取数据总长度 readLen(0x30); //读取版本号 if (!eq(_Ver)) { throw new Exception("PEM未知版本"); } //检测PKCS8 var idx2 = idx; if (eq(_SeqOID)) { //读取1长度 readLen(0x04); //读取2长度 readLen(0x30); //读取版本号 if (!eq(_Ver)) { throw new Exception("PEM版本无效"); } } else { idx = idx2; } //读取数据 param.Modulus = readBlock(); param.Exponent = readBlock(); param.D = readBlock(); param.P = readBlock(); param.Q = readBlock(); param.DP = readBlock(); param.DQ = readBlock(); param.InverseQ = readBlock(); } else { throw new Exception("pem需要BEGIN END标头"); } rsa.ImportParameters(param); return rsa; } static private Regex _PEMCode = new Regex(@"--+.+?--+|s+"); static private byte[] _SeqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; static private byte[] _Ver = new byte[] { 0x02, 0x01, 0x00 }; /// <summary> /// 将RSA中的密钥对转换成PEM格式,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响 /// </summary> public static string ToPEM(RSACryptoServiceProvider rsa, bool convertToPublic, bool usePKCS8) { //https://www.jianshu.com/p/25803dd9527d //https://www.cnblogs.com/ylz8401/p/8443819.html //https://blog.csdn.net/jiayanhui2877/article/details/47187077 //https://blog.csdn.net/xuanshao_/article/details/51679824 //https://blog.csdn.net/xuanshao_/article/details/51672547 var ms = new MemoryStream(); //写入一个长度字节码 Action<int> writeLenByte = (len) => { if (len < 0x80) { ms.WriteByte((byte)len); } else if (len <= 0xff) { ms.WriteByte(0x81); ms.WriteByte((byte)len); } else { ms.WriteByte(0x82); ms.WriteByte((byte)(len >> 8 & 0xff)); ms.WriteByte((byte)(len & 0xff)); } }; //写入一块数据 Action<byte[]> writeBlock = (byts) => { var addZero = (byts[0] >> 4) >= 0x8; ms.WriteByte(0x02); var len = byts.Length + (addZero ? 1 : 0); writeLenByte(len); if (addZero) { ms.WriteByte(0x00); } ms.Write(byts, 0, byts.Length); }; //根据后续内容长度写入长度数据 Func<int, byte[], byte[]> writeLen = (index, byts) => { var len = byts.Length - index; ms.SetLength(0); ms.Write(byts, 0, index); writeLenByte(len); ms.Write(byts, index, len); return ms.ToArray(); }; if (rsa.PublicOnly || convertToPublic) { /****生成公钥****/ var param = rsa.ExportParameters(false); //写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入 ms.WriteByte(0x30); var index1 = (int)ms.Length; //固定内容 // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" ms.writeAll(_SeqOID); //从0x00开始的后续长度 ms.WriteByte(0x03); var index2 = (int)ms.Length; ms.WriteByte(0x00); //后续内容长度 ms.WriteByte(0x30); var index3 = (int)ms.Length; //写入Modulus writeBlock(param.Modulus); //写入Exponent writeBlock(param.Exponent); //计算空缺的长度 var byts = ms.ToArray(); byts = writeLen(index3, byts); byts = writeLen(index2, byts); byts = writeLen(index1, byts); return "-----BEGIN PUBLIC KEY-----n" + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "n-----END PUBLIC KEY-----"; } else { /****生成私钥****/ var param = rsa.ExportParameters(true); //写入总字节数,后续写入 ms.WriteByte(0x30); int index1 = (int)ms.Length; //写入版本号 ms.writeAll(_Ver); //PKCS8 多一段数据 int index2 = -1, index3 = -1; if (usePKCS8) { //固定内容 ms.writeAll(_SeqOID); //后续内容长度 ms.WriteByte(0x04); index2 = (int)ms.Length; //后续内容长度 ms.WriteByte(0x30); index3 = (int)ms.Length; //写入版本号 ms.writeAll(_Ver); } //写入数据 writeBlock(param.Modulus); writeBlock(param.Exponent); writeBlock(param.D); writeBlock(param.P); writeBlock(param.Q); writeBlock(param.DP); writeBlock(param.DQ); writeBlock(param.InverseQ); //计算空缺的长度 var byts = ms.ToArray(); if (index2 != -1) { byts = writeLen(index3, byts); byts = writeLen(index2, byts); } byts = writeLen(index1, byts); var flag = " PRIVATE KEY"; if (!usePKCS8) { flag = " RSA" + flag; } return "-----BEGIN" + flag + "-----n" + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "n-----END" + flag + "-----"; } } }

以上就是详解c#与js的rsa加密互通的详细内容,更多关于c#与js的rsa加密互通的资料请关注靠谱客其它相关文章!

最后

以上就是发嗲火最近收集整理的关于详解c#与js的rsa加密互通的全部内容,更多相关详解c#与js内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(163)

评论列表共有 0 条评论

立即
投稿
返回
顶部