C#使用BouncyCastle生成PKCS#12数字证书

2022-10-15,,

背景

生成数字证书用于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数字证书的相关教程结束。

《C#使用BouncyCastle生成PKCS#12数字证书.doc》

下载本文的Word格式文档,以方便收藏与打印。