背景

  • 生成数字证书用于PDF文档数字签名
  • 数字证书需要考虑环境兼容性,如linux、windows
  • 网上资料不全或版本多样

本文章主要介绍了在C#中使用BouncyCastle生成PKCS#12个人信息交换语法标准的pfx证书、cer证书,旨在引导大家了解非对称加密算法,快速、轻松的使用证书对文本进行加密、解密,额外提供了RSAHelper类,包含加密、解密、签名、验签函数,支持无限长度、分段加解密,如有错误、欢迎留言指正;

pfx证书:含有公钥、私钥

cer证书:只含有公钥

非对称加密算法:常见的有很多,这其中最有名、最常用的还是RSA

RSA文件格式有很多中,PKCS#12只是其中一种,除此之外还有PKCS#1、PKCS#7、PKCS#8等,具体有什么区别,我这里不再进行阐述,自行百度;

不同语言采用的RSA文件格式不同,如java的私钥采用的是PKCS8,c#的私钥采用的是PKCS1,c#和java生成的私钥要让对方使用的就需要进行转换为对方语言适用的文件格式;

依赖

本文示例代码依赖于BouncyCastle.Crypto.dll,可以在项目中使用NuGet程序包引入。

源码

RSAHelper类

  1 public class RSAHelper
2 {
3 #region 使用私钥签名Sign(string data, string privateKey, RSAType rsaType, Encoding encoding)
4 /// <summary>
5 /// 使用私钥签名
6 /// </summary>
7 /// <param name="data">待签名串</param>
8 /// <param name="privateKey">私钥</param>
9 /// <param name="encoding">编码类型,推荐使用UTF8</param>
10 /// <param name="rsaType">签名类型,默认RSA2</param>
11 /// <returns></returns>
12 public static string Sign(string data, string privateKey, Encoding encoding, RSAType rsaType = RSAType.RSA2)
13 {
14 encoding = encoding ?? Encoding.UTF8;
15 byte[] dataBytes = encoding.GetBytes(data);
16 RSA _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey);
17 HashAlgorithmName _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;
18 var signatureBytes = _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
19 return Convert.ToBase64String(signatureBytes);
20 }
21 /// <summary>
22 /// 使用私钥签名,默认Encoding为Encoding.UTF8
23 /// </summary>
24 /// <param name="data">待签名串</param>
25 /// <param name="privateKey">私钥</param>
26 /// <param name="rsaType">签名类型,默认RSA2</param>
27 /// <returns></returns>
28 public static string Sign(string data, string privateKey, RSAType rsaType = RSAType.RSA2)
29 {
30 return Sign(data, privateKey, Encoding.UTF8, rsaType);
31 }
32 /// <summary>
33 /// 使用私钥签名
34 /// </summary>
35 /// <param name="parameters">待签参数</param>
36 /// <param name="privateKey">私钥</param>
37 /// <param name="encoding">编码类型,推荐使用UTF8</param>
38 /// <param name="rsaType">签名类型,默认RSA2</param>
39 /// <param name="removeSign">是否移除签名串,默认移除名为“sign”的签名串</param>
40 /// <returns></returns>
41 public static string SignParameters(IDictionary<string, string> parameters, string privateKey, Encoding encoding, RSAType rsaType = RSAType.RSA2, bool removeSign = true)
42 {
43
44 string data = GetSignContent(parameters, removeSign);
45 byte[] dataBytes = encoding.GetBytes(data);
46 RSA _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey);
47 HashAlgorithmName _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;
48 var signatureBytes = _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
49 return Convert.ToBase64String(signatureBytes);
50 }
51 /// <summary>
52 /// 使用私钥签名,默认Encoding为Encoding.UTF8
53 /// </summary>
54 /// <param name="parameters">待签参数</param>
55 /// <param name="privateKey">私钥</param>
56 /// <param name="rsaType">签名类型,默认RSA2</param>
57 /// <param name="removeSign">是否移除签名串,默认移除名为“sign”的签名串</param>
58 /// <returns></returns>
59 public static string SignParameters(IDictionary<string, string> parameters, string privateKey, RSAType rsaType = RSAType.RSA2, bool removeSign = true)
60 {
61 return SignParameters(parameters, privateKey, Encoding.UTF8, rsaType, removeSign);
62 }
63 #endregion
64
65 #region 使用公钥验证签名Verify(string data, string sign,string publickey,RSAType rsaType,Encoding encoding)
66 /// <summary>
67 /// 使用公钥验证签名
68 /// </summary>
69 /// <param name="data">原始数据</param>
70 /// <param name="sign">签名串</param>
71 /// <param name="publickey">公钥</param>
72 /// <param name="encoding">编码类型,推荐使用UTF8</param>
73 /// <param name="rsaType">签名类型,推默认RSA2</param>
74 /// <returns></returns>
75 public static bool Verify(string data, string sign, string publickey, Encoding encoding, RSAType rsaType = RSAType.RSA2)
76 {
77 byte[] dataBytes = encoding.GetBytes(data);
78 byte[] signBytes = Convert.FromBase64String(sign);
79 RSA _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publickey);
80 HashAlgorithmName _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;
81 var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
82 return verify;
83 }
84 /// <summary>
85 /// 使用公钥验证签名 默认Encoding为Encoding.UTF8
86 /// </summary>
87 /// <param name="data">原始数据</param>
88 /// <param name="sign">签名串</param>
89 /// <param name="publickey">公钥</param>
90 /// <param name="rsaType">签名类型,推默认RSA2</param>
91 /// <returns></returns>
92 public static bool Verify(string data, string sign, string publickey, RSAType rsaType = RSAType.RSA2)
93 {
94 return Verify(data, sign, publickey, Encoding.UTF8, rsaType);
95 }
96 /// <summary>
97 /// 使用公钥验证签名
98 /// </summary>
99 /// <param name="parameters">代验签参数</param>
100 /// <param name="publickey">公钥</param>
101 /// <param name="encoding">编码类型,推荐使用UTF8</param>
102 /// <param name="rsaType">签名类型,推荐使用RSA2</param>
103 /// <param name="removeSign">是否移除签名串,默认移除名为“sign”的签名串</param>
104 /// <returns></returns>
105 public static bool VerifyParameters(IDictionary<string, string> parameters, string publickey, Encoding encoding, RSAType rsaType = RSAType.RSA2, bool removeSign = true)
106 {
107 string sign = parameters["sign"];
108 parameters.Remove("sign");
109
110 byte[] dataBytes = encoding.GetBytes(GetSignContent(parameters, removeSign));
111 byte[] signBytes = Convert.FromBase64String(sign);
112 RSA _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publickey);
113 HashAlgorithmName _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;
114 var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
115 return verify;
116 }
117 /// <summary>
118 /// 使用公钥验证签名 默认Encoding为Encoding.UTF8
119 /// </summary>
120 /// <param name="parameters">代验签参数</param>
121 /// <param name="publickey">公钥</param>
122 /// <param name="rsaType">签名类型,推荐使用RSA2</param>
123 /// <param name="removeSign">是否移除签名串,默认移除名为“sign”的签名串</param>
124 /// <returns></returns>
125 public static bool VerifyParameters(IDictionary<string, string> parameters, string publickey, RSAType rsaType = RSAType.RSA2, bool removeSign = true)
126 {
127 return VerifyParameters(parameters, publickey, Encoding.UTF8, rsaType, removeSign);
128 }
129 #endregion
130
131 #region 获取/组装待签名串GetSignContent(IDictionary<string, string> parameters, bool removeSign = true)
132 /// <summary>
133 /// 获取/组装待签名串
134 /// </summary>
135 /// <param name="parameters">参数内容</param>
136 /// <param name="removeSign">是否移除签名串,默认移除名为“sign”的签名串</param>
137 /// <returns></returns>
138 public static string GetSignContent(IDictionary<string, string> parameters, bool removeSign = true)
139 {
140 if (removeSign && parameters.ContainsKey("sign"))
141 {
142 parameters.Remove("sign");
143 }
144 // 第一步:把字典按Key的字母顺序排序
145 IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
146 IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
147
148 // 第二步:把所有参数名和参数值串在一起
149 StringBuilder query = new StringBuilder("");
150 while (dem.MoveNext())
151 {
152 string key = dem.Current.Key;
153 string value = dem.Current.Value;
154 if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) // 空字段不加入签名/验签
155 {
156 query.Append(key).Append("=").Append(value).Append("&");
157 }
158 }
159 string content = query.ToString().Substring(0, query.Length - 1);
160
161 return content;
162 }
163 #endregion
164
165 #region 解密Decrypt(string cipherText,string privateKey)
166 /// <summary>
167 /// 解密(无限长度)
168 /// </summary>
169 /// <param name="cipherText">加密串</param>
170 /// <param name="privateKey">私钥</param>
171 /// <returns></returns>
172 public static string Decrypt(string cipherText, string privateKey)
173 {
174 RSA _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey);
175 if (_privateKeyRsaProvider == null)
176 {
177 throw new Exception("_privateKeyRsaProvider is null");
178 }
179 var inputBytes = Convert.FromBase64String(cipherText);
180 int bufferSize = _privateKeyRsaProvider.KeySize / 8;
181 var buffer = new byte[bufferSize];
182 using (MemoryStream inputStream = new MemoryStream(inputBytes),
183 outputStream = new MemoryStream())
184 {
185 while (true)
186 {
187 int readSize = inputStream.Read(buffer, 0, bufferSize);
188 if (readSize <= 0)
189 {
190 break;
191 }
192
193 var temp = new byte[readSize];
194 Array.Copy(buffer, 0, temp, 0, readSize);
195 var rawBytes = _privateKeyRsaProvider.Decrypt(temp, RSAEncryptionPadding.Pkcs1);
196 outputStream.Write(rawBytes, 0, rawBytes.Length);
197 }
198 return Encoding.UTF8.GetString(outputStream.ToArray());
199 }
200
201 //return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1));
202 }
203 /// <summary>
204 /// 分段解密
205 /// </summary>
206 /// <param name="encryptedInput"></param>
207 /// <param name="privateKey"></param>
208 /// <returns></returns>
209 private string RsaDecrypt(string encryptedInput, string privateKey)
210 {
211 if (string.IsNullOrEmpty(encryptedInput))
212 {
213 return string.Empty;
214 }
215
216 if (string.IsNullOrWhiteSpace(privateKey))
217 {
218 throw new ArgumentException("Invalid Private Key");
219 }
220
221 using (var rsaProvider = new RSACryptoServiceProvider())
222 {
223 var inputBytes = Convert.FromBase64String(encryptedInput);
224 rsaProvider.FromXmlString(privateKey);
225 int bufferSize = rsaProvider.KeySize / 8;
226 var buffer = new byte[bufferSize];
227 using (MemoryStream inputStream = new MemoryStream(inputBytes),
228 outputStream = new MemoryStream())
229 {
230 while (true)
231 {
232 int readSize = inputStream.Read(buffer, 0, bufferSize);
233 if (readSize <= 0)
234 {
235 break;
236 }
237
238 var temp = new byte[readSize];
239 Array.Copy(buffer, 0, temp, 0, readSize);
240 var rawBytes = rsaProvider.Decrypt(temp, false);
241 outputStream.Write(rawBytes, 0, rawBytes.Length);
242 }
243 return Encoding.UTF8.GetString(outputStream.ToArray());
244 }
245 }
246 }
247 #endregion
248
249 #region 加密 Encrypt(string text,string publickey)
250 /// <summary>
251 /// 加密(无限长度)
252 /// </summary>
253 /// <param name="text">待加密串</param>
254 /// <param name="publickey">公钥</param>
255 /// <returns></returns>
256 public static string Encrypt(string text, string publickey)
257 {
258 RSA _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publickey);
259 if (_publicKeyRsaProvider == null)
260 {
261 throw new Exception("_publicKeyRsaProvider is null");
262 }
263 var inputBytes = Encoding.UTF8.GetBytes(text);
264 int bufferSize = (_publicKeyRsaProvider.KeySize / 8) - 11;//单块最大长度
265 var buffer = new byte[bufferSize];
266 using (MemoryStream inputStream = new MemoryStream(inputBytes),
267 outputStream = new MemoryStream())
268 {
269 while (true)
270 { //分段加密
271 int readSize = inputStream.Read(buffer, 0, bufferSize);
272 if (readSize <= 0)
273 {
274 break;
275 }
276
277 var temp = new byte[readSize];
278 Array.Copy(buffer, 0, temp, 0, readSize);
279 var encryptedBytes = _publicKeyRsaProvider.Encrypt(temp, RSAEncryptionPadding.Pkcs1);
280 outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
281 }
282 return Convert.ToBase64String(outputStream.ToArray());//转化为字节流方便传输
283 }
284 }
285 /// <summary>
286 /// 分段加密
287 /// </summary>
288 /// <param name="rawInput"></param>
289 /// <param name="publicKey"></param>
290 /// <returns></returns>
291 private string RsaEncrypt(string rawInput, string publicKey)
292 {
293 if (string.IsNullOrEmpty(rawInput))
294 {
295 return string.Empty;
296 }
297
298 if (string.IsNullOrWhiteSpace(publicKey))
299 {
300 throw new ArgumentException("Invalid Public Key");
301 }
302
303 using (var rsaProvider = new RSACryptoServiceProvider())
304 {
305 var inputBytes = Encoding.UTF8.GetBytes(rawInput);//有含义的字符串转化为字节流
306 rsaProvider.FromXmlString(publicKey);//载入公钥
307 int bufferSize = (rsaProvider.KeySize / 8) - 11;//单块最大长度
308 var buffer = new byte[bufferSize];
309 using (MemoryStream inputStream = new MemoryStream(inputBytes),
310 outputStream = new MemoryStream())
311 {
312 while (true)
313 { //分段加密
314 int readSize = inputStream.Read(buffer, 0, bufferSize);
315 if (readSize <= 0)
316 {
317 break;
318 }
319
320 var temp = new byte[readSize];
321 Array.Copy(buffer, 0, temp, 0, readSize);
322 var encryptedBytes = rsaProvider.Encrypt(temp, false);
323 outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
324 }
325 return Convert.ToBase64String(outputStream.ToArray());//转化为字节流方便传输
326 }
327 }
328 }
329 #endregion
330
331 #region 私有方法
332 /// <summary>
333 /// 使用私钥创建RSA实例
334 /// </summary>
335 /// <param name="privateKey">私钥</param>
336 /// <returns></returns>
337 private static RSA CreateRsaProviderFromPrivateKey(string privateKey)
338 {
339 var privateKeyBits = Convert.FromBase64String(privateKey);
340
341 var rsa = RSA.Create();
342 var rsaParameters = new RSAParameters();
343
344 using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
345 {
346 byte bt = 0;
347 ushort twobytes = 0;
348 twobytes = binr.ReadUInt16();
349 if (twobytes == 0x8130)
350 binr.ReadByte();
351 else if (twobytes == 0x8230)
352 binr.ReadInt16();
353 else
354 throw new Exception("Unexpected value read binr.ReadUInt16()");
355
356 twobytes = binr.ReadUInt16();
357 if (twobytes != 0x0102)
358 throw new Exception("Unexpected version");
359
360 bt = binr.ReadByte();
361 if (bt != 0x00)
362 throw new Exception("Unexpected value read binr.ReadByte()");
363
364 rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr));
365 rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr));
366 rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr));
367 rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr));
368 rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr));
369 rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr));
370 rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr));
371 rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
372 }
373
374 rsa.ImportParameters(rsaParameters);
375 return rsa;
376 }
377
378 /// <summary>
379 /// 使用公钥创建RSA实例
380 /// </summary>
381 /// <param name="publicKeyString">公钥</param>
382 /// <returns></returns>
383 private static RSA CreateRsaProviderFromPublicKey(string publicKeyString)
384 {
385 // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
386 byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
387 byte[] seq = new byte[15];
388
389 var x509Key = Convert.FromBase64String(publicKeyString);
390
391 // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
392 using (MemoryStream mem = new MemoryStream(x509Key))
393 {
394 using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading
395 {
396 byte bt = 0;
397 ushort twobytes = 0;
398
399 twobytes = binr.ReadUInt16();
400 if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
401 binr.ReadByte(); //advance 1 byte
402 else if (twobytes == 0x8230)
403 binr.ReadInt16(); //advance 2 bytes
404 else
405 return null;
406
407 seq = binr.ReadBytes(15); //read the Sequence OID
408 if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct
409 return null;
410
411 twobytes = binr.ReadUInt16();
412 if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
413 binr.ReadByte(); //advance 1 byte
414 else if (twobytes == 0x8203)
415 binr.ReadInt16(); //advance 2 bytes
416 else
417 return null;
418
419 bt = binr.ReadByte();
420 if (bt != 0x00) //expect null byte next
421 return null;
422
423 twobytes = binr.ReadUInt16();
424 if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
425 binr.ReadByte(); //advance 1 byte
426 else if (twobytes == 0x8230)
427 binr.ReadInt16(); //advance 2 bytes
428 else
429 return null;
430
431 twobytes = binr.ReadUInt16();
432 byte lowbyte = 0x00;
433 byte highbyte = 0x00;
434
435 if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
436 lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
437 else if (twobytes == 0x8202)
438 {
439 highbyte = binr.ReadByte(); //advance 2 bytes
440 lowbyte = binr.ReadByte();
441 }
442 else
443 return null;
444 byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
445 int modsize = BitConverter.ToInt32(modint, 0);
446
447 int firstbyte = binr.PeekChar();
448 if (firstbyte == 0x00)
449 { //if first byte (highest order) of modulus is zero, don't include it
450 binr.ReadByte(); //skip this null byte
451 modsize -= 1; //reduce modulus buffer size by 1
452 }
453
454 byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
455
456 if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
457 return null;
458 int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
459 byte[] exponent = binr.ReadBytes(expbytes);
460
461 // ------- create RSACryptoServiceProvider instance and initialize with public key -----
462 var rsa = RSA.Create();
463 RSAParameters rsaKeyInfo = new RSAParameters
464 {
465 Modulus = modulus,
466 Exponent = exponent
467 };
468 rsa.ImportParameters(rsaKeyInfo);
469
470 return rsa;
471 }
472
473 }
474 }
475
476 /// <summary>
477 /// 导入密钥算法
478 /// </summary>
479 /// <param name="binr">BinaryReader</param>
480 /// <returns></returns>
481 private static int GetIntegerSize(BinaryReader binr)
482 {
483 byte bt = 0;
484 int count = 0;
485 bt = binr.ReadByte();
486 if (bt != 0x02)
487 return 0;
488 bt = binr.ReadByte();
489
490 if (bt == 0x81)
491 count = binr.ReadByte();
492 else
493 if (bt == 0x82)
494 {
495 var highbyte = binr.ReadByte();
496 var lowbyte = binr.ReadByte();
497 byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
498 count = BitConverter.ToInt32(modint, 0);
499 }
500 else
501 {
502 count = bt;
503 }
504
505 while (binr.ReadByte() == 0x00)
506 {
507 count -= 1;
508 }
509 binr.BaseStream.Seek(-1, SeekOrigin.Current);
510 return count;
511 }
512
513 private static bool CompareBytearrays(byte[] a, byte[] b)
514 {
515 if (a.Length != b.Length)
516 return false;
517 int i = 0;
518 foreach (byte c in a)
519 {
520 if (c != b[i])
521 return false;
522 i++;
523 }
524 return true;
525 }
526
527 #endregion
528 }
529
530 public enum RSAType
531 {
532 /// <summary>
533 /// SHA1
534 /// </summary>
535 RSA = 0,
536 /// <summary>
537 /// RSA2 密钥长度至少为2048
538 /// SHA256
539 /// </summary>
540 RSA2
541 }

生成证书源码:

  1 static void Main(string[] args)
2 {
3 /*
4 前言:最近有个需求是需要对文档进行签名,考虑到数字签名证书问题,所以生成一个自签名的数字证书;
5 描述:本示例基于BouncyCastle.Crypto组件提供的算法生成证书,
6 演示了生成了cer证书、pfx证书及加载pfx证书对字符串加密解密
7
8 */
9
10 var takeEffect = DateTime.Now; // 生效时间
11 var loseEffect = DateTime.Now.AddYears(2); // 失效时间
12 var password = "ABCD123456"; //证书密码
13 var signatureAlgorithm = "SHA256WITHRSA"; //签名算法
14 var friendlyName = $"{Guid.NewGuid().ToString("N")}dpps.fun"; // 别名
15
16 // 获取颁发者DN
17 X509Name issuer = GetIssuer();
18
19 // 获取使用者DN
20 X509Name subject = GetSubject();
21
22 // 证书存放目录
23 string file = System.Environment.CurrentDirectory + "\\Cert\\";
24 if (!Directory.Exists(file))
25 {
26 Directory.CreateDirectory(file);
27 }
28 string pfxPath = $"{file}{friendlyName}.pfx";
29 string certPath = $"{file}{friendlyName}.cer";
30
31 // 生成证书
32 GenerateCertificate(certPath, pfxPath, password, signatureAlgorithm, issuer, subject, takeEffect, loseEffect, friendlyName);
33
34 // 加载PFX证书
35 LoadingPfxCertificate(pfxPath, password);
36
37 Console.WriteLine("OK");
38 Console.ReadLine();
39 }
40
41 /// <summary>
42 /// 获取使用者DN.
43 /// </summary>
44 /// <returns>使用者DN.</returns>
45 private static X509Name GetSubject()
46 {
47 // 使用者DN
48 return new X509Name(
49 new ArrayList
50 {
51 X509Name.C,
52 X509Name.O,
53 X509Name.CN
54 },
55 new Hashtable
56 {
57 [X509Name.C] = "CN",
58 [X509Name.O] = "ICH",
59 [X509Name.CN] = "*.dpps.fun"
60 }
61 );
62 }
63
64 /// <summary>
65 /// 获取颁发者DN.
66 /// </summary>
67 /// <returns>颁发者DN.</returns>
68 private static X509Name GetIssuer()
69 {
70 // 颁发者DN
71 return new X509Name(
72 new ArrayList
73 {
74 X509Name.C,
75 X509Name.O,
76 X509Name.OU,
77 X509Name.L,
78 X509Name.ST,
79 X509Name.E,
80 },
81 new Hashtable
82 {
83 [X509Name.C] = "CN",// 证书的语言
84 [X509Name.O] = "dpps.fun",//设置证书的办法者
85 [X509Name.OU] = "dpps.fun Fulu RSA CA 2020",
86 [X509Name.L] = "dpps",
87 [X509Name.ST] = "dpps",
88 [X509Name.E] = "472067093@qq.com",
89 }
90 );
91 }
92
93
94 /// <summary>
95 /// 生成证书
96 /// </summary>
97 /// <param name="certPath">certPath(只含公钥)</param>
98 /// <param name="pfxPath">pfxPath(含公私钥)</param>
99 /// <param name="password">证书密码</param>
100 /// <param name="signatureAlgorithm">设置将用于签署此证书的签名算法</param>
101 /// <param name="issuer">设置此证书颁发者的DN</param>
102 /// <param name="subject">设置此证书使用者的DN</param>
103 /// <param name="takeEffect">证书生效时间</param>
104 /// <param name="loseEffect">证书失效时间</param>
105 /// <param name="friendlyName">设置证书友好名称(可选)</param>
106 /// <param name="keyStrength">密钥长度</param>
107 public static void GenerateCertificate(
108 string certPath,
109 string pfxPath,
110 string password,
111 string signatureAlgorithm,
112 X509Name issuer,
113 X509Name subject,
114 DateTime takeEffect,
115 DateTime loseEffect,
116 string friendlyName,
117 int keyStrength = 2048)
118 {
119 SecureRandom random = new SecureRandom(new CryptoApiRandomGenerator());
120 var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
121 var keyPairGenerator = new RsaKeyPairGenerator(); //RSA密钥对生成器
122 keyPairGenerator.Init(keyGenerationParameters);
123 var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
124 ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, subjectKeyPair.Private, random);
125 //the certificate generator
126
127 X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
128
129 var spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public);
130
131 //允许作为一个CA证书(可以颁发下级证书或进行签名)
132 certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
133
134 //使用者密钥标识符
135 certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifier(spki));
136
137 //授权密钥标识符
138 certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifier(spki));
139
140 certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
141
142 //证书序列号
143 BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
144
145 certificateGenerator.SetSerialNumber(serialNumber);
146
147 certificateGenerator.SetIssuerDN(issuer); //颁发者信息
148
149 certificateGenerator.SetSubjectDN(subject); //使用者信息
150
151 certificateGenerator.SetNotBefore(takeEffect); //证书生效时间
152
153 certificateGenerator.SetNotAfter(loseEffect); //证书失效时间
154
155 certificateGenerator.SetPublicKey(subjectKeyPair.Public);
156
157 Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);
158
159 //生成cer证书,公钥证
160 var certificate2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate))
161 {
162 FriendlyName = friendlyName, //设置友好名称
163 };
164 // cer公钥文件
165 var bytes = certificate2.Export(X509ContentType.Cert);
166 using (var fs = new FileStream(certPath, FileMode.Create))
167 {
168 fs.Write(bytes, 0, bytes.Length);
169 }
170
171 //另一种代码生成p12证书的方式(要求使用.net standard 2.1)
172 //certificate2 = certificate2.CopyWithPrivateKey(DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private));
173 //var bytes2 = certificate2.Export(X509ContentType.Pfx, password);
174 //using (var fs = new FileStream(pfxPath, FileMode.Create))
175 //{
176 // fs.Write(bytes2, 0, bytes2.Length);
177 //}
178
179 // 生成pfx证书,公私钥证
180 var certEntry = new X509CertificateEntry(certificate);
181 var store = new Pkcs12StoreBuilder().Build();
182 store.SetCertificateEntry(friendlyName, certEntry); //设置证书
183 var chain = new X509CertificateEntry[1];
184 chain[0] = certEntry;
185 store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), chain); //设置私钥
186 using (var fs = File.Create(pfxPath))
187 {
188 store.Save(fs, password.ToCharArray(), random); //保存
189 };
190 }
191
192 /// <summary>
193 /// 加载证书
194 /// </summary>
195 /// <param name="pfxPath"></param>
196 /// <param name="password"></param>
197 private static void LoadingPfxCertificate(string pfxPath, string password)
198 {
199 //加载证书
200 X509Certificate2 pfx = new X509Certificate2(pfxPath, password, X509KeyStorageFlags.Exportable);
201 var keyPair = DotNetUtilities.GetKeyPair(pfx.PrivateKey);
202 var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
203 var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
204 var privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
205 var publicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
206 Console.ForegroundColor = ConsoleColor.DarkYellow;
207
208 Console.WriteLine("Pfx证书私钥:");
209 Console.WriteLine(privateKey);
210 Console.WriteLine("Pfx证书公钥:");
211 Console.WriteLine(publicKey);
212
213 var beEncryptedData = "hello rsa";
214 Console.WriteLine($"加密原文:{beEncryptedData}");
215 var cipherText = RSAHelper.Encrypt(beEncryptedData, publicKey);
216 Console.WriteLine("加密结果:");
217 Console.WriteLine(cipherText);
218
219 var datares = RSAHelper.Decrypt(cipherText, privateKey);
220 Console.WriteLine($"解密结果:{datares}");
221 }

完全源码地址:https://github.com/daileass/CreateSelfSignedCertificateByBouncyCastle.git

C#使用BouncyCastle生成PKCS#12数字证书的更多相关文章

  1. .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书

    简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...

  2. 用Keytool和OpenSSL生成和签发数字证书

    一)keytool生成私钥文件(.key)和签名请求文件(.csr),openssl签发数字证书      J2SDK在目录%JAVA_HOME%/bin提供了密钥库管理工具Keytool,用于管理密 ...

  3. 常用加密算法学习总结之数字证书与TLS/SSL

    数字证书 对于一个安全的通信,应该有以下特征: 完整性:消息在传输过程中未被篡改 身份验证:确认消息发送者的身份 不可否认:消息的发送者无法否认自己发送了信息 显然,数字签名和消息认证码是不符合要求的 ...

  4. BouncyCastle产生一个PKCS#12规范的PFX/p12证书

    RT,在C#中实现,依赖.netFramework2.0 BouncyCastle中提供了PKCS12Store,Pkcs12StoreBuilder,AsymmetricKeyEntry,X509C ...

  5. JAVA JDK keytool 生成数字证书

    简介: 数字证书作为网络安全数据传输的凭证,web在传输时客户端(浏览器)和 服务端(服务器)先进行会话握手,在握手过程中服务端会验证客户端的是否已经在服务端做了认证,这是单向认证.如果是双向认证的话 ...

  6. JAVA数字证书制作生成

    1.加密算法 为了网络通讯中的报文安全,一般需要对报文进行加密,目前常用的加密算法有: 非对称加密算法:又称公钥加密算法,如RSA.DSA/DSS,最常用的就是RSA算法(算法公开,可自行百度了解算法 ...

  7. JDK 生成数字证书

    JDK(keytool.exe)生成数字证书 2010-11-21 15:52 QUOTE: keytool JAVA是个密钥和证书管理工具.它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数 ...

  8. 利用keytool工具生成数字证书

    一.制作数字证书  因测试微信小程序, 腾讯要求使用 https协议,所以需要使用证书.使用jdk工具制作数字证书流程如下: 1.查看JDK是否安装,使用命令java -version 2.切换目录至 ...

  9. PKCS#12

    http://blog.csdn.net/cuiran/article/details/7816696 数字证书介绍 一.什么是数字证书 数字证书就是互联网通讯中标志通讯各方身份信息的一系列数据,提供 ...

随机推荐

  1. ExtJS 布局-Absolute布局(Absolute layout)

    更新记录: 2022年5月31日 发布本篇 1.说明 使用xy配置项设置子组件在父容器中绝对位置,本质是将子组件的CSS的position设置为absolute,然后使用x和y配置项映射到CSS的to ...

  2. redis如何实现数据同步

    redis如何实现数据同步 两种,1全同步,2部分同步 全备份: 在slave启动时会向master发送sync消息,master收到slave这条消息之后,将启动后台备份进程,备份完成之后,将备份数 ...

  3. Bika LIMS 开源LIMS集—— SENAITE的安装

    安装环境 操作系统 Ubuntu 18.04 LTS Python 2.x. Plone 4 安装步骤 Ubuntu等Linux.Mac系统一般安装有Python的环境,但由于需要安装Python扩展 ...

  4. UiPath直播课程

    UiPath实现拉勾招聘信息的抓取和汇总 https://www.bilibili.com/video/av81859882 UiPath企业框架Reframework的介绍和使用 https://w ...

  5. RPA应用场景-报税机器人

    场景概述 报税机器人 所涉系统名称 税务网站 人工操作(时间/次) 53分钟 所涉人工数量 60 操作频率 每月 场景流程 1.通过RPA自动将财税信息从对应系统中导出 2.RPA根据不同的税务报表规 ...

  6. rhel修改系统语言

    修改系统语言的三种方式  1.yum install system-config-language //挂载本地源,然后安装         system-config-language     2. ...

  7. 浅议.NET遗留应用改造

    浅议.NET遗留应用改造 TLDR:本文介绍了遗留应用改造中的一些常见问题,并对改造所能开展的目标.原则.策略进行了概述. 一.背景概述 1.概述 或许仅"遗留应用"这个标题就比较 ...

  8. 零基础学Java(4)字符串

    字符串 从概念上讲,Java字符串就是Unicode字符序列.例如,字符串"Java\u2122"由5个Unicode字符J.a.v.a和组成.Java没有内置的字符串类型,而是在 ...

  9. Linux查看内网服务器的出口IP

    查看内网服务器的出口IPcurl ifconfig.me [root@vpnserver ~]# curl ifconfig.me111.10.100.100 [root@vpnserver ~]#

  10. 扩展-PageHelper分页插件

    1.PageHelper 分页插件简介 1) PageHelper是MyBatis中非常方便的第三方分页插件 2) 官方文档: https://github.com/pagehelper/Mybati ...