BouncyCastleWrapper.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. using Microsoft.Extensions.Logging;
  2. using Org.BouncyCastle.Asn1.Pkcs;
  3. using Org.BouncyCastle.Asn1.Sec;
  4. using Org.BouncyCastle.Asn1.X509;
  5. using Org.BouncyCastle.Asn1;
  6. using Org.BouncyCastle.Crypto.Generators;
  7. using Org.BouncyCastle.Crypto.Operators;
  8. using Org.BouncyCastle.Crypto.Parameters;
  9. using Org.BouncyCastle.Crypto;
  10. using Org.BouncyCastle.Pkcs;
  11. using Org.BouncyCastle.Security;
  12. using Org.BouncyCastle.X509;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using Org.BouncyCastle.Math;
  19. using Org.BouncyCastle.OpenSsl;
  20. namespace CAUtilLib
  21. {
  22. public static class BouncyCastleWrapper
  23. {
  24. public static ILogger? Logger = null;
  25. static SecureRandom secureRandom = new SecureRandom();
  26. public static AsymmetricCipherKeyPair GenerateRsaKeyPair(int length = 4096)
  27. {
  28. var keygenParam = new KeyGenerationParameters(secureRandom, length);
  29. var keyGenerator = new RsaKeyPairGenerator();
  30. keyGenerator.Init(keygenParam);
  31. return keyGenerator.GenerateKeyPair();
  32. }
  33. public static AsymmetricCipherKeyPair GenerateEcKeyPair(string curveName)
  34. {
  35. var ecParam = SecNamedCurves.GetByName(curveName);
  36. var ecDomain = new ECDomainParameters(ecParam.Curve, ecParam.G, ecParam.N);
  37. var keygenParam = new ECKeyGenerationParameters(ecDomain, secureRandom);
  38. var keyGenerator = new ECKeyPairGenerator();
  39. keyGenerator.Init(keygenParam);
  40. return keyGenerator.GenerateKeyPair();
  41. }
  42. public static X509Certificate GenerateCertificate(
  43. X509Name issuer, X509Name subject,
  44. AsymmetricCipherKeyPair issuerKey,
  45. AsymmetricKeyParameter subjectPublic,
  46. BigInteger? issuerSerialNumber = null,
  47. bool isCertificateAuthority = false)
  48. {
  49. var selfserilaNumber = BigInteger.ProbablePrime(120, secureRandom);
  50. if (issuerSerialNumber is null)
  51. {
  52. issuerSerialNumber = selfserilaNumber;
  53. }
  54. ISignatureFactory signatureFactory;
  55. signatureFactory = new Asn1SignatureFactory(
  56. PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(),
  57. issuerKey.Private);
  58. signatureFactory = new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(), issuerKey.Private);
  59. var certGenerator = new X509V3CertificateGenerator();
  60. certGenerator.SetIssuerDN(issuer);
  61. certGenerator.SetSubjectDN(subject);
  62. certGenerator.SetSerialNumber(selfserilaNumber);
  63. certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(100));
  64. certGenerator.SetNotBefore(DateTime.UtcNow);
  65. certGenerator.SetPublicKey(subjectPublic);
  66. certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false,
  67. new AuthorityKeyIdentifier(
  68. SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKey.Public)
  69. )
  70. );
  71. certGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false,
  72. new SubjectKeyIdentifier(
  73. SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectPublic)
  74. )
  75. );
  76. if (isCertificateAuthority)
  77. {
  78. certGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true,
  79. new BasicConstraints(0));
  80. //certGenerator.AddExtension(X509Extensions.KeyUsage.Id, true,
  81. // new KeyUsage(KeyUsage.KeyCertSign));
  82. }
  83. return certGenerator.Generate(signatureFactory);
  84. }
  85. public static bool ValidateCertificateChain(List<X509Certificate> certificates)
  86. {
  87. for (int index = certificates.Count - 1; index > 0; index--)
  88. {
  89. var signedCertificate = certificates[index];
  90. var sourceCertificate = certificates[index - 1];
  91. if (!ValidateCert(signedCertificate, sourceCertificate.GetPublicKey()))
  92. {
  93. return false;
  94. }
  95. }
  96. return true;
  97. }
  98. public static bool ValidateCert(X509Certificate cert, List<X509Certificate> certChain)
  99. {
  100. if (!ValidateCertificateChain(certChain))
  101. {
  102. return false;
  103. }
  104. for (int index = certChain.Count - 1; index > -1; index--)
  105. {
  106. var signedCertificate = cert;
  107. var sourceCertificate = certChain[index];
  108. if (ValidateCert(signedCertificate, sourceCertificate.GetPublicKey()))
  109. {
  110. return true;
  111. }
  112. }
  113. return false;
  114. }
  115. public static bool ValidateCert(X509Certificate cert, ICipherParameters pubKey)
  116. {
  117. try
  118. {
  119. cert.CheckValidity(DateTime.UtcNow);
  120. var tbsCert = cert.GetTbsCertificate();
  121. var sig = cert.GetSignature();
  122. var signer = SignerUtilities.GetSigner(cert.SigAlgName);
  123. signer.Init(false, pubKey);
  124. signer.BlockUpdate(tbsCert, 0, tbsCert.Length);
  125. var verifyResult = signer.VerifySignature(sig);
  126. if (!verifyResult)
  127. {
  128. Logger?.LogDebug("validation failed");
  129. }
  130. return verifyResult;
  131. }
  132. catch (Exception e)
  133. {
  134. Logger?.LogDebug("validation failed: {0}", e.Message);
  135. return false;
  136. }
  137. }
  138. public static X509Certificate GenerateSelfSignedCertificate(AsymmetricCipherKeyPair kp, string commonName = "Zerova")
  139. {
  140. //var certName = new X509Name("CN=" + commomName);
  141. var certName = CreateX509Name(commonName: commonName);
  142. return GenerateCertificate(certName, certName, kp, kp.Public, isCertificateAuthority: true);
  143. }
  144. public static X509Certificate SignCertificate(
  145. Pkcs10CertificationRequest csr,
  146. X509Certificate rca,
  147. AsymmetricCipherKeyPair issuerKey,
  148. bool isCertificateAuthority = false)
  149. {
  150. var csrInfo = csr.GetCertificationRequestInfo();
  151. return GenerateCertificate(rca.SubjectDN, csrInfo.Subject, issuerKey, csr.GetPublicKey(), issuerSerialNumber: rca.SerialNumber, isCertificateAuthority: isCertificateAuthority);
  152. }
  153. public static Pkcs10CertificationRequest CreateCertificateReq(AsymmetricCipherKeyPair kp, string commonName, string organizationName)
  154. {
  155. var subject = CreateX509Name(commonName: commonName, organizationName: organizationName);
  156. var csr = new Pkcs10CertificationRequest(
  157. PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(),
  158. subject,
  159. kp.Public,
  160. null,
  161. kp.Private
  162. );
  163. return csr;
  164. }
  165. public static X509Name CreateX509Name(
  166. string? commonName = null,
  167. string? organizationName = null,
  168. string? country = null,
  169. string? state = null)
  170. {
  171. var values = new Dictionary<DerObjectIdentifier, string> { };
  172. if (!string.IsNullOrEmpty(commonName))
  173. {
  174. values.Add(X509Name.CN, commonName);
  175. }
  176. if (!string.IsNullOrEmpty(organizationName))
  177. {
  178. values.Add(X509Name.O, organizationName);
  179. }
  180. if (!string.IsNullOrEmpty(country))
  181. {
  182. values.Add(X509Name.C, country);
  183. }
  184. if (!string.IsNullOrEmpty(state))
  185. {
  186. values.Add(X509Name.ST, state);
  187. }
  188. return new X509Name(values.Keys.Reverse().ToList(), values);
  189. }
  190. public static X509Certificate ToBouncyCastle(this System.Security.Cryptography.X509Certificates.X509Certificate2 msCert)
  191. {
  192. return Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(msCert);
  193. }
  194. public static System.Security.Cryptography.X509Certificates.X509Certificate2 ToMs(this X509Certificate bcCert)
  195. {
  196. return System.Security.Cryptography.X509Certificates.X509Certificate2.CreateFromPem(ToStringAsPem(bcCert).Result);
  197. }
  198. public static string? GetFirstValue(this X509Name x509Name, DerObjectIdentifier identifier)
  199. {
  200. return x509Name.GetValueList(identifier).FirstOrDefault();
  201. }
  202. public static async Task<bool> TrySaveAsPemAsync(string name, object[] items)
  203. {
  204. try
  205. {
  206. using TextWriter textWriter = new StreamWriter(name);
  207. using PemWriter pemWriter = new PemWriter(textWriter);
  208. foreach (object obj in items)
  209. {
  210. pemWriter.WriteObject(obj);
  211. }
  212. await pemWriter.Writer.FlushAsync();
  213. return true;
  214. }
  215. catch (Exception ex)
  216. {
  217. Logger?.LogDebug("SaveAsPemAsync failed {0}", ex.Message);
  218. return false;
  219. }
  220. }
  221. public static Task<string> ToStringAsPem(object item)
  222. {
  223. return ToStringAsPem(new object[] { item });
  224. }
  225. public static async Task<string> ToStringAsPem(object[] items)
  226. {
  227. using StringWriter stringWriter = new StringWriter();
  228. using PemWriter pemWriter = new PemWriter(stringWriter);
  229. foreach (object obj in items)
  230. {
  231. pemWriter.WriteObject(obj);
  232. }
  233. await pemWriter.Writer.FlushAsync();
  234. return stringWriter.ToString();
  235. }
  236. public static Task<X509Certificate?> LoadPemCertFromFile(string name)
  237. {
  238. return TryLoadPemFromFile<X509Certificate>(name);
  239. }
  240. public static X509Certificate? LoadPemCertFromString(string source)
  241. {
  242. return TryLoadPemFromString<X509Certificate>(source);
  243. }
  244. public static Task<AsymmetricCipherKeyPair?> LoadPemKeyFromFile(string name)
  245. {
  246. return TryLoadPemFromFile<AsymmetricCipherKeyPair>(name);
  247. }
  248. public static AsymmetricCipherKeyPair? LoadPemKeyFromString(string source)
  249. {
  250. return TryLoadPemFromString<AsymmetricCipherKeyPair>(source);
  251. }
  252. internal static Task<Pkcs10CertificationRequest?> LoadPemCsrFromFile(string source)
  253. {
  254. return TryLoadPemFromFile<Pkcs10CertificationRequest>(source);
  255. }
  256. public static Pkcs10CertificationRequest? LoadPemCsrFromString(string source)
  257. {
  258. return TryLoadPemFromString<Pkcs10CertificationRequest>(source);
  259. }
  260. public static List<X509Certificate> LoadPemCrtChaingFromString(string source)
  261. {
  262. var readList = TryLoadPemListFromString(source);
  263. return readList.Where(x => x is X509Certificate).Select(x => (X509Certificate)x).ToList();
  264. }
  265. internal static async Task<T?> TryLoadPemFromFile<T>(string name)
  266. {
  267. try
  268. {
  269. using var fileReader = new StreamReader(name);
  270. var result = await fileReader.ReadToEndAsync();
  271. return TryLoadPemFromString<T>(result);
  272. }
  273. catch (Exception ex)
  274. {
  275. Logger?.LogDebug("TryLoadPemFromFile failed: {0}", ex.Message);
  276. return default(T);
  277. }
  278. }
  279. internal static T? TryLoadPemFromString<T>(string rawString)
  280. {
  281. try
  282. {
  283. using TextReader textReader = new StringReader(rawString);
  284. using PemReader pemReader = new PemReader(textReader);
  285. return (T)pemReader.ReadObject();
  286. }
  287. catch (Exception ex)
  288. {
  289. Logger?.LogDebug("TryLoadPemFromString failed: {0}", ex.Message);
  290. return default(T);
  291. }
  292. }
  293. internal static List<object> TryLoadPemListFromString(string rawString)
  294. {
  295. try
  296. {
  297. using TextReader textReader = new StringReader(rawString);
  298. using PemReader pemReader = new PemReader(textReader);
  299. List<object> toReturn = new List<object>();
  300. object result = null;
  301. do
  302. {
  303. result = pemReader.ReadObject();
  304. if (result is null)
  305. {
  306. break;
  307. }
  308. toReturn.Add(result);
  309. }
  310. while (true);
  311. return toReturn;
  312. }
  313. catch (Exception ex)
  314. {
  315. Logger?.LogDebug("TryLoadPemFromString failed: {0}", ex.Message);
  316. return default;
  317. }
  318. }
  319. }
  320. }