C#使用BouncyCastle生成PKCS#12数字证书
背景
- 生成数字证书用于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