shayne_lo vor 4 Monaten
Ursprung
Commit
de1e18d28e

+ 361 - 0
CAUtilLib/BouncyCastleWrapper.cs

@@ -0,0 +1,361 @@
+using Microsoft.Extensions.Logging;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.OpenSsl;
+
+namespace CAUtilLib
+{
+    public static class BouncyCastleWrapper
+    {
+        public static ILogger? Logger = null;
+        static SecureRandom secureRandom = new SecureRandom();
+
+        public static AsymmetricCipherKeyPair GenerateRsaKeyPair(int length = 4096)
+        {
+            var keygenParam = new KeyGenerationParameters(secureRandom, length);
+
+            var keyGenerator = new RsaKeyPairGenerator();
+            keyGenerator.Init(keygenParam);
+            return keyGenerator.GenerateKeyPair();
+        }
+
+        public static AsymmetricCipherKeyPair GenerateEcKeyPair(string curveName)
+        {
+            var ecParam = SecNamedCurves.GetByName(curveName);
+            var ecDomain = new ECDomainParameters(ecParam.Curve, ecParam.G, ecParam.N);
+            var keygenParam = new ECKeyGenerationParameters(ecDomain, secureRandom);
+
+            var keyGenerator = new ECKeyPairGenerator();
+            keyGenerator.Init(keygenParam);
+            return keyGenerator.GenerateKeyPair();
+        }
+
+        public static X509Certificate GenerateCertificate(
+            X509Name issuer, X509Name subject,
+            AsymmetricCipherKeyPair issuerKey,
+            AsymmetricKeyParameter subjectPublic,
+            BigInteger? issuerSerialNumber = null,
+            bool isCertificateAuthority = false)
+        {
+            var selfserilaNumber = BigInteger.ProbablePrime(120, secureRandom);
+            if (issuerSerialNumber is null)
+            {
+                issuerSerialNumber = selfserilaNumber;
+            }
+
+            ISignatureFactory signatureFactory;
+            signatureFactory = new Asn1SignatureFactory(
+                PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(),
+                issuerKey.Private);
+            signatureFactory = new Asn1SignatureFactory(PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(), issuerKey.Private);
+
+            var certGenerator = new X509V3CertificateGenerator();
+            certGenerator.SetIssuerDN(issuer);
+            certGenerator.SetSubjectDN(subject);
+            certGenerator.SetSerialNumber(selfserilaNumber);
+            certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(100));
+            certGenerator.SetNotBefore(DateTime.UtcNow);
+            certGenerator.SetPublicKey(subjectPublic);
+
+            certGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false,
+                new AuthorityKeyIdentifier(
+                    SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKey.Public)
+                    )
+                );
+
+            certGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false,
+                new SubjectKeyIdentifier(
+                    SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectPublic)
+                    )
+                );
+
+            if (isCertificateAuthority)
+            {
+                certGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true,
+                    new BasicConstraints(0));
+                //certGenerator.AddExtension(X509Extensions.KeyUsage.Id, true,
+                //    new KeyUsage(KeyUsage.KeyCertSign));
+            }
+
+            return certGenerator.Generate(signatureFactory);
+        }
+
+        public static bool ValidateCertificateChain(List<X509Certificate> certificates)
+        {
+            for (int index = certificates.Count - 1; index > 0; index--)
+            {
+                var signedCertificate = certificates[index];
+                var sourceCertificate = certificates[index - 1];
+                if (!ValidateCert(signedCertificate, sourceCertificate.GetPublicKey()))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static bool ValidateCert(X509Certificate cert, List<X509Certificate> certChain)
+        {
+            if (!ValidateCertificateChain(certChain))
+            {
+                return false;
+            }
+
+            for (int index = certChain.Count - 1; index > -1; index--)
+            {
+                var signedCertificate = cert;
+                var sourceCertificate = certChain[index];
+                if (ValidateCert(signedCertificate, sourceCertificate.GetPublicKey()))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public static bool ValidateCert(X509Certificate cert, ICipherParameters pubKey)
+        {
+            try
+            {
+                cert.CheckValidity(DateTime.UtcNow);
+                var tbsCert = cert.GetTbsCertificate();
+                var sig = cert.GetSignature();
+
+                var signer = SignerUtilities.GetSigner(cert.SigAlgName);
+                signer.Init(false, pubKey);
+                signer.BlockUpdate(tbsCert, 0, tbsCert.Length);
+                var verifyResult = signer.VerifySignature(sig);
+                if (!verifyResult)
+                {
+                    Logger?.LogDebug("validation failed");
+                }
+                return verifyResult;
+            }
+            catch (Exception e)
+            {
+                Logger?.LogDebug("validation failed: {0}", e.Message);
+                return false;
+            }
+        }
+
+        public static X509Certificate GenerateSelfSignedCertificate(AsymmetricCipherKeyPair kp, string commonName = "Zerova")
+        {
+            //var certName = new X509Name("CN=" + commomName);
+            var certName = CreateX509Name(commonName: commonName);
+            return GenerateCertificate(certName, certName, kp, kp.Public, isCertificateAuthority: true);
+        }
+
+        public static X509Certificate SignCertificate(
+            Pkcs10CertificationRequest csr,
+            X509Certificate rca, 
+            AsymmetricCipherKeyPair issuerKey,
+            bool isCertificateAuthority = false)
+        {
+            var csrInfo = csr.GetCertificationRequestInfo();
+            return GenerateCertificate(rca.SubjectDN, csrInfo.Subject, issuerKey, csr.GetPublicKey(), issuerSerialNumber: rca.SerialNumber, isCertificateAuthority: isCertificateAuthority);
+        }
+
+        public static Pkcs10CertificationRequest CreateCertificateReq(AsymmetricCipherKeyPair kp, string commonName, string organizationName)
+        {
+            var subject = CreateX509Name(commonName: commonName, organizationName: organizationName);
+
+            var csr = new Pkcs10CertificationRequest(
+                PkcsObjectIdentifiers.Sha512WithRsaEncryption.ToString(),
+                subject,
+                kp.Public,
+                null,
+                kp.Private
+                );
+
+            return csr;
+        }
+
+        public static X509Name CreateX509Name(
+            string? commonName = null,
+            string? organizationName = null,
+            string? country = null,
+            string? state = null)
+        {
+            var values = new Dictionary<DerObjectIdentifier, string> { };
+
+            if (!string.IsNullOrEmpty(commonName))
+            {
+                values.Add(X509Name.CN, commonName);
+            }
+            if (!string.IsNullOrEmpty(organizationName))
+            {
+                values.Add(X509Name.O, organizationName);
+            }
+            if (!string.IsNullOrEmpty(country))
+            {
+                values.Add(X509Name.C, country);
+            }
+            if (!string.IsNullOrEmpty(state))
+            {
+                values.Add(X509Name.ST, state);
+            }
+            return new X509Name(values.Keys.Reverse().ToList(), values);
+        }
+
+        public static X509Certificate ToBouncyCastle(this System.Security.Cryptography.X509Certificates.X509Certificate2 msCert)
+        {
+            return Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(msCert);
+        }
+
+        public static System.Security.Cryptography.X509Certificates.X509Certificate2 ToMs(this X509Certificate bcCert)
+        {
+            return System.Security.Cryptography.X509Certificates.X509Certificate2.CreateFromPem(ToStringAsPem(bcCert).Result);
+        }
+
+        public static string? GetFirstValue(this X509Name x509Name, DerObjectIdentifier identifier)
+        {
+            return x509Name.GetValueList(identifier).FirstOrDefault();
+        }
+
+        public static async Task<bool> TrySaveAsPemAsync(string name, object[] items)
+        {
+            try
+            {
+                using TextWriter textWriter = new StreamWriter(name);
+                using PemWriter pemWriter = new PemWriter(textWriter);
+                foreach (object obj in items)
+                {
+                    pemWriter.WriteObject(obj);
+                }
+                await pemWriter.Writer.FlushAsync();
+                return true;
+            }
+            catch (Exception ex)
+            {
+                Logger?.LogDebug("SaveAsPemAsync failed {0}", ex.Message);
+                return false;
+            }
+        }
+
+        public static Task<string> ToStringAsPem(object item)
+        {
+            return ToStringAsPem(new object[] { item });
+        }
+
+        public static async Task<string> ToStringAsPem(object[] items)
+        {
+            using StringWriter stringWriter = new StringWriter();
+            using PemWriter pemWriter = new PemWriter(stringWriter);
+            foreach (object obj in items)
+            {
+                pemWriter.WriteObject(obj);
+            }
+            await pemWriter.Writer.FlushAsync();
+            return stringWriter.ToString();
+        }
+
+        public static Task<X509Certificate?> LoadPemCertFromFile(string name)
+        {
+            return TryLoadPemFromFile<X509Certificate>(name);
+        }
+
+        public static X509Certificate? LoadPemCertFromString(string source)
+        {
+            return TryLoadPemFromString<X509Certificate>(source);
+        }
+
+        public static Task<AsymmetricCipherKeyPair?> LoadPemKeyFromFile(string name)
+        {
+            return TryLoadPemFromFile<AsymmetricCipherKeyPair>(name);
+        }
+
+        public static AsymmetricCipherKeyPair? LoadPemKeyFromString(string source)
+        {
+            return TryLoadPemFromString<AsymmetricCipherKeyPair>(source);
+        }
+
+        internal static Task<Pkcs10CertificationRequest?> LoadPemCsrFromFile(string source)
+        {
+            return TryLoadPemFromFile<Pkcs10CertificationRequest>(source);
+        }
+
+        public static Pkcs10CertificationRequest? LoadPemCsrFromString(string source)
+        {
+            return TryLoadPemFromString<Pkcs10CertificationRequest>(source);
+        }
+
+        public static List<X509Certificate> LoadPemCrtChaingFromString(string source)
+        {
+            var readList = TryLoadPemListFromString(source);
+            return readList.Where(x => x is X509Certificate).Select(x => (X509Certificate)x).ToList();
+        }
+
+        internal static async Task<T?> TryLoadPemFromFile<T>(string name)
+        {
+            try
+            {
+                using var fileReader = new StreamReader(name);
+                var result = await fileReader.ReadToEndAsync();
+                return TryLoadPemFromString<T>(result);
+            }
+            catch (Exception ex)
+            {
+                Logger?.LogDebug("TryLoadPemFromFile failed: {0}", ex.Message);
+                return default(T);
+            }
+        }
+
+        internal static T? TryLoadPemFromString<T>(string rawString)
+        {
+            try
+            {
+                using TextReader textReader = new StringReader(rawString);
+                using PemReader pemReader = new PemReader(textReader);
+                return (T)pemReader.ReadObject();
+            }
+            catch (Exception ex)
+            {
+                Logger?.LogDebug("TryLoadPemFromString failed: {0}", ex.Message);
+                return default(T);
+            }
+        }
+        internal static List<object> TryLoadPemListFromString(string rawString)
+        {
+            try
+            {
+                using TextReader textReader = new StringReader(rawString);
+                using PemReader pemReader = new PemReader(textReader);
+
+                List<object> toReturn = new List<object>();
+                object result = null;
+                do
+                {
+                    result = pemReader.ReadObject();
+                    if (result is null)
+                    {
+                        break;
+                    }
+                    toReturn.Add(result);
+                }
+                while (true);
+
+                return toReturn;
+            }
+            catch (Exception ex)
+            {
+                Logger?.LogDebug("TryLoadPemFromString failed: {0}", ex.Message);
+                return default;
+            }
+        }
+    }
+}

+ 60 - 659
CAUtilLib/CAUtil.cs

@@ -1,716 +1,117 @@
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.X509;
 using System;
-using System.Diagnostics;
-using System.Diagnostics.Metrics;
-using System.Reflection;
-using System.Runtime.ConstrainedExecution;
-using System.Security.AccessControl;
-using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
+using System.Collections.Generic;
+using System.Linq;
 using System.Text;
-using System.Xml.Linq;
+using System.Threading.Tasks;
 
 namespace CAUtilLib
 {
-    public partial class CaUtil
+    public class CaUtil
     {
-        /// <summary>
-        /// 建構子生成路徑可透過SetPath進行路徑覆寫
-        /// </summary>
-        public CaUtil(ILogger<CaUtil> logger)
-        {
-            if (string.IsNullOrWhiteSpace(this.path))
-            {
-                string path = Directory.GetCurrentDirectory();
-                //string parentPath = Directory.GetParent(Directory.GetParent(Directory.GetParent(Directory.GetParent(path).ToString()).ToString()).ToString()) + @"\";
-                string filePath = Path.Combine(path, "temp");
-                bool exists = Directory.Exists(filePath);
-                if (!exists)
-                {
-                    CreateFolder(filePath);
-                    MoveFile(@"zerova_v3.cnf", filePath + @"\zerova_v3.cnf");
-                }
-                this.path = filePath;
-            }
+        public string RootPath = "/home/cert";
 
-            this.logger = logger;
-        }
+        public const string RcaDirectoryName = "rca";
+        public const string TrustedDirectoryName = "tcrt";
 
-        private string path = "";
-        private readonly ILogger<CaUtil> logger;
+        public const string RcaKeyName = "rca_key.pem";
+        public const string RcaCertName = "rca.pem";
 
-        /// <summary>
-        /// 檔案路徑
-        /// </summary>
-        public string SavePath
-        {
-            get => path;
-            set
-            {
-                if (this.path == value)
-                {
-                    return;
-                }
+        public string RcaKeyPath => Path.Combine(RootPath, RcaDirectoryName, RcaKeyName);
+        public string RcaCertPath => Path.Combine(RootPath, RcaDirectoryName, RcaCertName);
+        public string TrustedCertsPath => Path.Combine(RootPath, TrustedDirectoryName);
 
-                this.path = value;
-                string filePath = this.path;
-                CreateFolder(filePath);
-                MoveFile(@"zerova_v3.cnf", Path.Combine(filePath, @"zerova_v3.cnf"));
-            }
-        }
-
-        public OSType OS { get; set; }
-
-        private string ExecuteShell => OS == OSType.Windows ? "cmd.exe" : "/bin/bash";
-        private string CatCmd => OS == OSType.Windows ? "type " : "cat";
-
-        /// <summary>
-        /// 生成RootCA憑證
-        /// </summary>
-        /// <param name="name">Root CA</param>
-        /// <param name="Days">憑證有效期限</param>
-        /// <param name="SerialNumber">序號</param>
-        /// <param name="Cn">主機名或網站地址</param>
-        /// <param name="organizationName">組織名稱</param>
-        /// <param name="Hash">使用SHA256/SHA512演算法進行簽名</param>
-        /// <param name="RsaKey">生成2048位元或4096位元的私鑰</param>
-        /// <param name="Caformat">生成crt 或者pem 格式</param>
-        /// <returns></returns>
-        public async Task<bool> CreateRootCA(
-            string name, 
-            string commonName,
-            string organizationName,
-            string Country = "TW",
-            string State = "Taipei",
-            int Days = 3650,
-            string SerialNumber = "",
-            HashAlgorithm Hash = HashAlgorithm.SHA512,
-            OpensslGenrsaRsa RsaKey = OpensslGenrsaRsa.Key4096)
+        public async Task<bool> CreateRootCA()
         {
-            if (string.IsNullOrEmpty(SerialNumber))
+            AsymmetricCipherKeyPair kp = BouncyCastleWrapper.GenerateRsaKeyPair(4096);
+            var saveKeyResult = await BouncyCastleWrapper.TrySaveAsPemAsync(RcaKeyPath, new object[] { kp.Private });
+            if (!saveKeyResult)
             {
-                SerialNumber = await GetOpenSSLRandSn();
+                return false;
             }
 
-            var status = false;
-            var keyName = "";
-            keyName = name;
-            string sn = "0x" + SerialNumber;
-            HashAlgorithm ha = Hash;
-            var sslKey = (int)RsaKey;
-
-            var subjString = CreateSubjectString(CommonName: commonName, Organization: organizationName, Country: Country, State: State);
-
-            if (await CreateKey(keyName, RsaKey) &&
-                await ExecShellCmd("openssl", $"req -x509 -new -nodes -set_serial {sn} -key {keyName}.key -{ha} -days {Days} -subj \"{subjString}\" -out {name}.crt ") &&
-                await MergeFile($"{name}.pem", $"{name}.crt", $"{keyName}.key")
-                //&& await ExecShellCmd("certutil", $"-f -addstore root {RootCa}.crt ") 
-                //&& await ExecShellCmd("certutil", $"-f -addstore root {RootCa}.crt ")
-                )
+            X509Certificate cert = BouncyCastleWrapper.GenerateSelfSignedCertificate(kp);
+            var saveCertResult = await BouncyCastleWrapper.TrySaveAsPemAsync(RcaCertPath, new object[] { cert, kp.Private });
+            if (!saveCertResult)
             {
-                return true;
+                return false;
             }
 
-            return false;
-            //create
-            string[] strs = new string[5];
-            strs[0] = "openssl genrsa  -out " + keyName + ".key " + sslKey + " ";
-            strs[1] = "openssl req -x509 -new -nodes  -set_serial " + sn + " -key " + keyName + ".key  -" + ha + " -days " + Days + "  -subj \"/C=TW/ST=Taipei/O=" + organizationName + "/OU=phihong_sub.IO/CN=" + commonName + "/emailAddress=phihong_sub@mail.com\" -out " + name + ".crt ";
-            strs[2] = CatCmd + name + ".crt " + keyName + ".key > " + name + ".pem ";
-            strs[3] = "certutil -f -addstore root " + name + ".crt ";
-            strs[4] = "certutil -f -addstore root " + name + ".crt ";
-
-            for (int i = 0; i < strs.Length; i++)
-            {
-                var process = new Process
-                {
-                    StartInfo = new ProcessStartInfo
-                    {
-                        FileName = ExecuteShell,
-                        Arguments = "/C " + strs[i],
-                        WorkingDirectory = this.path,
-                        StandardOutputEncoding = Encoding.UTF8,
-                        RedirectStandardOutput = true,
-                        RedirectStandardError = true,
-                        UseShellExecute = false,
-                        Verb = "runas"
-                    }
-                };
-
-                process.Start();
-                _ = process.StandardOutput.ReadToEndAsync().ContinueWith(( t => {
-                    logger.LogTrace(t.Result);
-                }));
-                _ = process.StandardError.ReadToEndAsync().ContinueWith((t => {
-                    logger.LogTrace(t.Result);
-                }));
-                await process.WaitForExitAsync();
-                if (process.ExitCode != 0)
-                {
-                    return false;
-                }
-            }
-
-            return status;
-        }
-
-        /// <summary>
-        /// 生成SubCA子憑證
-        /// </summary>
-        /// <param name="RootCaName">CA憑證名稱</param>
-        /// <param name="Days">憑證有效期限</param>
-        /// <param name="SubCA">子憑證名稱</param>
-        /// <param name="SerialNumber">序號</param>
-        /// <param name="commonName">主機名或網站地址</param>
-        /// <param name="organizationName">組織名稱</param>
-        /// <param name="Hash">使用SHA256/SHA512演算法進行簽名</param>
-        /// <param name="RsaKey">生成2048位元或4096位元的私鑰</param>
-        /// <param name="Caformat">生成crt 或者pem 格式</param>
-        /// <returns></returns>        
-        public async Task<bool> CreateSubCA(string RootCa, string Days,
-        string SubCA, string SerialNumber, string commonName, string organizationName, HashAlgorithm Hash, OpensslGenrsaRsa RsaKey, Certificateformat Caformat)
-        {
-
-            if (string.IsNullOrEmpty(SerialNumber))
-            {
-                SerialNumber = await GetOpenSSLRandSn();
-            }
-
-            var status = false;
-            var str = "";
-            string sn = "0x" + SerialNumber;
-            //hashAlgorithm ha = Hash;
-            var sslKey = (int)RsaKey;
-            Certificateformat format = Caformat;
-
-            if (await ExecShellCmd("openssl", $"genrsa -out {SubCA}.key {sslKey}") &&
-                await ExecShellCmd("openssl", $"req -new -{Hash} -nodes -key {SubCA}.key -out {SubCA}.csr -subj \"/C=TW/ST=Taipei/O={organizationName}/OU=phihong_sub.IO/CN={commonName}/emailAddress=phihong_sub@mail.com\" ") &&
-                await ExecShellCmd("openssl", $"x509 -set_serial {sn} -req -in {SubCA}.csr -CA {RootCa}.crt -CAkey {RootCa}.key -CAcreateserial -out {SubCA}.crt -days {Days} -sha256 -extfile zerova_v3.cnf  ") &&
-                await MergeFile($"{SubCA}.pem", $"{SubCA}.crt", $"{SubCA}.key"))
-            {
-                return true;
-            }
-            return false;
-
-
-            string[] strs = new string[4];
-            strs[0] = "openssl genrsa -out " + SubCA + ".key " + sslKey + "  ";
-            strs[1] = "openssl req -new -" + Hash + " -nodes -key " + SubCA + ".key -out " + SubCA + ".csr -subj \"/C=TW/ST=Taipei/O=" + organizationName + "/OU=phihong_sub.IO/CN=" + commonName + "/emailAddress=phihong_sub@mail.com\" ";
-            //var str8 = "openssl x509 -set_serial " + sn + " -req -in " + subKeyName + ".csr -CA " + caPath + "\\" + crtName + ".crt -CAkey " + this.path + "\\" + rootCaName + ".key -CAcreateserial -out " + subKeyName + ".crt -days " + days + "   -sha256 -extfile zerova_v3.cnf  ";
-            strs[2] = "openssl x509 -set_serial " + sn + " -req -in " + SubCA + ".csr -CA " + RootCa + ".crt -CAkey " + this.path + "\\" + RootCa + ".key -CAcreateserial -out " + SubCA + ".crt -days " + Days + "   -sha256 -extfile zerova_v3.cnf  ";
-            strs[3] = CatCmd + SubCA + ".crt " + SubCA + ".key > " + SubCA + ".pem ";
-
-
-            for (int i = 0; i < strs.Length; i++)
-            {
-                var process = new Process
-                {
-                    StartInfo = new ProcessStartInfo
-                    {
-                        FileName = ExecuteShell,
-                        Arguments = "/C " + strs[i],
-                        WorkingDirectory = this.path,//"D:\\project\\vs\\ConsoleApp2",
-                        RedirectStandardOutput = true,
-                        UseShellExecute = false,
-                        Verb = "runas"
-                    }
-                };
-
-                process.Start();
-                string output = process.StandardOutput.ReadToEnd();
-                await process.WaitForExitAsync();
-            }
-            return status;
-        }
-
-        public async Task<bool> SignCsr(string SubCA, string RootCa, int? Days = null, string? SerialNumber = null)
-        {
-            Days ??= 3650;
-
-            if (string.IsNullOrEmpty(SerialNumber))
-            {
-                SerialNumber = await GetOpenSSLRandSn();
-            }
-            string sn = "0x" + SerialNumber;
-
-            if (await ExecShellCmd("openssl", $"x509 -set_serial {sn} -req -in {SubCA}.csr -CA {RootCa}.crt -CAkey {RootCa}.key -CAcreateserial -out {SubCA}.crt -days {Days} -sha256 -extfile zerova_v3.cnf  "))
-            {
-                return true;
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// 生成Csr 檔案
-        /// </summary>
-        /// <param name="Key">key 名稱</param>
-        /// <param name="Csr">csr 名稱</param>
-        /// <param name="commonName">主機名或網站地址</param>
-        /// <param name="organizationName">組織名稱</param>
-        /// <returns></returns>
-        public async Task<bool> CreateCsr(
-            string Key,
-            string Csr,
-            string commonName, 
-            string organizationName,
-            HashAlgorithm Hash = HashAlgorithm.SHA512)
-        {
-            var status = false;
-            var subjString = CreateSubjectString(CommonName: commonName, Organization: organizationName, Country: "TW", State: "Taipei");
-
-            if (await ExecShellCmd("openssl", $"req -new -{Hash} -nodes -key {Key}.key -out {Csr}.csr -subj \"{subjString}\" "))
-            {
-                return true;
-            }
-            return false;
-
-            string[] strs = new string[4];
-            strs[0] = "openssl req -new -SHA256 -nodes -key " + Key + ".key -out " + Csr + ".csr -subj \"/C=TW/ST=Taipei/O=" + organizationName + "/OU=phihong_sub.IO/CN=" + commonName + "/emailAddress=phihong_sub@mail.com\"  ";
-            for (int i = 0; i < strs.Length; i++)
-            {
-                var process = new Process
-                {
-                    StartInfo = new ProcessStartInfo
-                    {
-                        FileName = ExecuteShell,
-                        Arguments = "/C " + strs[i],
-                        WorkingDirectory = this.path,//"D:\\project\\vs\\ConsoleApp2",
-                        RedirectStandardOutput = true,
-                        UseShellExecute = false,
-                        Verb = "runas"
-                    }
-                };
-
-                process.Start();
-                string output = process.StandardOutput.ReadToEnd();
-                await process.WaitForExitAsync();
-            }
-            return status;
-        }
-
-        private object CreateSubjectString(string CommonName = "", string Organization = "", string Country = "", string State = "")
-        {
-            ///C=TW/ST=Taipei/O={organizationName}/OU=phihong_sub.IO/CN={commonName}/emailAddress=phihong_sub@mail.com\
-            var toReturn = string.Empty;
-            if (!string.IsNullOrEmpty(Country))
-            {
-                toReturn += $"/C={Country}";
-            }
-            if (!string.IsNullOrEmpty(State))
-            {
-                toReturn += $"/ST={State}";
-            }
-            if (!string.IsNullOrEmpty(Organization))
-            {
-                toReturn += $"/O={Organization}";
-            }
-            if (!string.IsNullOrEmpty(CommonName))
-            {
-                toReturn += $"/CN={CommonName}";
-            }
-            return toReturn;
-        }
-
-        /// <summary>
-        /// 讀取憑證資訊
-        /// </summary>
-        /// <param name="path">檔案名稱</param>
-        /// <param name="fileName">檔案路徑</param>
-        /// <returns></returns>
-        public string ReadCertificateHashData(string path, string fileName)//改成接收pem string 格式
-        {
-            var json = "";
-            var file = Path.Combine(path, fileName);
-            X509Certificate2 certificate = new X509Certificate2(file);
-
-
-            X509Extension extension = certificate.Extensions["2.5.29.35"];
-            var issuer_key_hash = "";
-            if (extension != null)
-            {
-                issuer_key_hash = extension.Format(true);
-            }
-
-            string hashAlgorithm = certificate.SignatureAlgorithm.FriendlyName;
-
-
-            string serialNumber = certificate.SerialNumber;
-            byte[] issuerDER = certificate.IssuerName.RawData;
-            SHA1 sha1 = SHA1.Create();
-            byte[] hashBytes = sha1.ComputeHash(issuerDER);
-
-            var data = new
-            {
-                hashAlgorithm = hashAlgorithm,
-                issuerNameHash = BitConverter.ToString(hashBytes).Replace("-", ""),
-                issuerKeyHash = issuer_key_hash,
-                serialNumber = serialNumber,
-                thumbprint = certificate.Thumbprint
-            };
-            string thumbprint = certificate.Thumbprint;
-
-            json = JsonConvert.SerializeObject(data);
-            return json;
+            return true;
         }
 
-        /// <summary>
-        /// 讀取憑證資訊
-        /// </summary>
-        /// <param name="FileName">檔案名稱</param>
-        /// <returns></returns>
-        public string ReadCertificateHashData(string fileName)
+        public async Task<string?> SignCsr(string csr)
         {
-            var json = "";
-            var file = Path.Combine(this.path, fileName);
-            X509Certificate2 certificate = new X509Certificate2(file);
-
-            X509Extension extension = certificate.Extensions["2.5.29.35"];
-            var issuer_key_hash = "";
-            if (extension != null)
+            var parsedCsr = BouncyCastleWrapper.LoadPemCsrFromString(csr);
+            if (parsedCsr is null)
             {
-                issuer_key_hash = extension.Format(true);
+                return null;
             }
 
-            string hashAlgorithm = certificate.SignatureAlgorithm.FriendlyName;
-
-            string serialNumber = certificate.SerialNumber;
-            byte[] issuerDER = certificate.IssuerName.RawData;
-            SHA1 sha1 = SHA1.Create();
-            byte[] hashBytes = sha1.ComputeHash(issuerDER);
-
-            var data = new
-            {
-                hashAlgorithm,
-                issuerNameHash = BitConverter.ToString(hashBytes).Replace("-", ""),
-                issuerKeyHash = issuer_key_hash,
-                serialNumber,
-                thumbprint = certificate.Thumbprint
-            };
-            string thumbprint = certificate.Thumbprint;
-
-            json = JsonConvert.SerializeObject(data);
-            return json;
-        }
-
-        /// <summary>
-        /// PEM 格式的憑證的內容
-        /// </summary>
-        /// <param name="str">憑證內容</param>
-        /// <returns></returns>
-        public string ReadCertificateHashDataByString(string str)
-        {
-            var json = "";
-            byte[] UTF8bytes = Encoding.UTF8.GetBytes(str);
-            X509Certificate2 certificate = new X509Certificate2(UTF8bytes);
-
-
-            X509Extension extension = certificate.Extensions["2.5.29.35"];
-            var issuer_key_hash = "";
-            if (extension != null)
+            var rca = await BouncyCastleWrapper.LoadPemCertFromFile(RcaCertPath);
+            if (rca is null)
             {
-                issuer_key_hash = extension.Format(true);
+                return null;
             }
 
-            string hashAlgorithm = certificate.SignatureAlgorithm.FriendlyName;
-
-
-            string serialNumber = certificate.SerialNumber;
-            byte[] issuerDER = certificate.IssuerName.RawData;
-            SHA1 sha1 = SHA1.Create();
-            byte[] hashBytes = sha1.ComputeHash(issuerDER);
-
-            var data = new
+            var rcaKey = await BouncyCastleWrapper.LoadPemKeyFromFile(RcaKeyPath);
+            if (rcaKey is null)
             {
-                hashAlgorithm = hashAlgorithm,
-                issuerNameHash = BitConverter.ToString(hashBytes).Replace("-", ""),
-                issuerKeyHash = issuer_key_hash,
-                serialNumber = serialNumber,
-                thumbprint = certificate.Thumbprint
-            };
-            string thumbprint = certificate.Thumbprint;
-
-            json = JsonConvert.SerializeObject(data);
-            return json;
-        }
-
-
-        /// <summary>
-        /// 驗證client憑證
-        /// </summary>
-        /// <param name="StrCA">CA憑證</param>
-        /// <param name="StrSub">子憑證</param>
-        /// <param name="Url">url localhost:3000</param>
-        /// <returns></returns>
-        public async Task<bool> VerifyClient(string CA, string SubCA, string Url)
-        {
-            var str = "";
-            bool isExists = false;
-            if (await ExecShellCmd("openssl", $"s_client -connect {Url} -CAfile {CA} -cert {SubCA} -tls1_2 -state"))
-            {
-                return true;
+                return null;
             }
-            return false;
-
 
-            var str1 = "openssl s_client -connect " + Url + " -CAfile " + CA + " -cert " + SubCA + " -tls1_2 -state  ";
-
-            str = await ClientCmd(str1);
-            string searchString = "(ok)";
-            string line = GetLineFromString(str, searchString);
-            if (line != null)
-                isExists = line.Contains(searchString);
-            return isExists;
+            var generatedCrt = BouncyCastleWrapper.SignCertificate(parsedCsr, rca, rcaKey);
+            return await BouncyCastleWrapper.ToStringAsPem(generatedCrt);
         }
 
-        /// <summary>
-        /// Cmd字串指令
-        /// </summary>
-        /// <param name="str1">字串指令</param>
-        /// <returns></returns>
-        private async Task<string> ClientCmd(string str1)
+        public async Task<int> VerifyCrt(string crt)
         {
-            var status = false;
-
-            var str = "";
-            string[] strs = { str1 };
-            for (int i = 0; i < strs.Length; i++)
+            var parsedCrt = BouncyCastleWrapper.LoadPemCertFromString(crt);
+            if (parsedCrt is null)
             {
-                var process = new Process
-                {
-                    StartInfo = new ProcessStartInfo
-                    {
-                        FileName = ExecuteShell,
-                        Arguments = "/C " + strs[i],
-                        WorkingDirectory = this.path,//"D:\\project\\vs\\ConsoleApp2",
-                        RedirectStandardOutput = true,
-                        UseShellExecute = false,
-                        //Verb = "runas"
-                    }
-                };
-
-                process.Start();
-                var output = new List<string>();
-                while (process.StandardOutput.Peek() > -1)
-                {
-                    output.Add(process.StandardOutput.ReadLine());
-                }
-
-                process.Kill();
-                str = string.Join("", output.ToArray());
-
+                return -1;
             }
-            return str;
-        }
 
-        /// <summary>
-        ///  將Root CA與Sub CA進行憑證驗證
-        /// </summary>
-        /// <param name="Ca">CA檔案名稱</param>
-        /// <param name="Sub">SubCA憑證名稱</param>
-        /// <returns>True/Flase</returns>
-        public async Task<bool> VerifyCertificateByCertificate(string Sub, string Ca)
-        {
-            if (await ExecShellCmd("openssl", $"verify -CAfile {Ca} {Sub}"))
+            var isSignedByRca = await VerifyCrtSignedByRCA(parsedCrt!);
+            if (isSignedByRca)
             {
-                return true;
+                return 0;
             }
-            return false;
 
-            var status = true;
-            var str = "";
-            var str1 = "openssl verify -CAfile " + Ca + " " + Sub;
-
-            bool isExists = false;
-            str = await ClientCmd(str1);
-            string searchString = "OK";
-            string line = GetLineFromString(str, searchString);
-            if (line != null)
-                isExists = line.Contains(searchString);
-            return isExists;
-        }
-
-        public async Task<bool> VerifyCertificateByCertificates(string Sub, string CaPath)
-        {
-            if (await ExecShellCmd("openssl", $"verify -CApath {CaPath} {Sub}"))
+            var isSignedByTrusted = await VerifyCrtSignedByTrused(parsedCrt!);
+            if (isSignedByTrusted)
             {
-                return true;
+                return 1;
             }
-            return false;
-        }
 
-        public async Task<bool> CalculateHash(string CrtName,string hashName)
-        {
-            if (await ExecShellCmd("openssl", $"x509 -in {CrtName} -hash -noout -out {hashName}"))
-            {
-                return true;
-            }
-            return false;
+            return -1;
         }
 
-        /// <summary>
-        /// Crt轉Pem
-        /// </summary>
-        /// <param name="CaName">檔案名稱</param>
-        /// <returns></returns>
-        public async Task<bool> CrtToPem(string CaName)
+        private async Task<bool> VerifyCrtSignedByRCA(X509Certificate crt)
         {
-            if (await ExecShellCmd("openssl", $"x509 -in {CaName}.crt -out {CaName}.pem"))
+            var rcaKey = await BouncyCastleWrapper.LoadPemKeyFromFile(RcaKeyPath);
+            if (rcaKey is null)
             {
-                return true;
+                return false;
             }
-            return false;
 
-            var status = false;
-            var str = "openssl x509 -in " + CaName + ".crt -out " + CaName + ".pem  ";
-
-            var process = new Process
-            {
-                StartInfo = new ProcessStartInfo
-                {
-                    FileName = ExecuteShell,
-                    Arguments = "/C " + str,
-                    WorkingDirectory = this.path,
-                    RedirectStandardOutput = true,
-                    UseShellExecute = false,
-                    Verb = "runas"
-                }
-            };
-
-            process.Start();
-            string output = process.StandardOutput.ReadToEnd();
-            await process.WaitForExitAsync();
-            return status;
+            return BouncyCastleWrapper.ValidateCert(crt, rcaKey.Public);
         }
 
-        /// <summary>
-        /// Pem轉Crt
-        /// </summary>
-        /// <param name="CaName">檔案名稱</param>
-        /// <returns></returns>
-        public async Task<bool> PemToCrt(string CaName)
+        private async Task<bool> VerifyCrtSignedByTrused(X509Certificate crt)
         {
-            if (await ExecShellCmd("openssl", $"x509 -outform der -in {CaName}.pem -out {CaName}.crt"))
+            var trustedCerts = Directory.GetFiles(TrustedCertsPath);
+            foreach (var trustedCert in trustedCerts)
             {
-                return true;
-            }
-            return false;
-
-            var status = false;
-            var str = "openssl x509 -outform der -in " + CaName + ".pem -out " + CaName + ".crt  ";
-
-            var process = new Process
-            {
-                StartInfo = new ProcessStartInfo
+                var cert = await BouncyCastleWrapper.LoadPemCertFromFile(trustedCert);
+                var checkResult = BouncyCastleWrapper.ValidateCert(crt, cert.GetPublicKey());
+                if (checkResult)
                 {
-                    FileName = ExecuteShell,
-                    Arguments = "/C " + str,
-                    WorkingDirectory = this.path,
-                    RedirectStandardOutput = true,
-                    UseShellExecute = false,
-                    Verb = "runas"
+                    return true;
                 }
-            };
-
-            process.Start();
-            string output = process.StandardOutput.ReadToEnd();
-            await process.WaitForExitAsync();
-            return status;
-        }
-
-        public async Task<bool> CreateKey(string keyName,
-            OpensslGenrsaRsa RsaKey = OpensslGenrsaRsa.Key4096)
-        {
-            var sslKey = (int)RsaKey;
-            if (await ExecShellCmd("openssl", $"genrsa -out {keyName}.key {sslKey}"))
-            {
-                return true;
             }
             return false;
         }
-
-        /// <summary>
-        /// 產生資料夾
-        /// </summary>
-        /// <param name="path"></param>
-        /// <returns></returns>
-        private bool CreateFolder(string path)
-        {
-            string subPath = path;
-            bool status = true;
-            bool exists = Directory.Exists(subPath);
-            status = !exists;
-
-            if (status)
-            {
-                Directory.CreateDirectory(subPath);
-            }
-
-            return status;
-        }
-
-        /// <summary>
-        /// 確認檔案是否存在
-        /// </summary>
-        /// <param name="filePath"></param>
-        /// <returns></returns>
-        private bool CheckFiles(string FilePath)
-        {
-            bool status = true;
-            bool exists = File.Exists(FilePath);
-            status = exists;
-            return status;
-        }
-
-        /// <summary>
-        /// 檔案搬移
-        /// </summary>
-        /// <param name="file"></param>
-        /// <param name="moveTo"></param>
-        /// <returns></returns>
-        private bool MoveFile(string file, string moveTo)
-        {
-            bool status = false;
-            if (CheckFiles(file) &&
-                !CheckFiles(moveTo))
-            {
-                //File.Move(file, moveTo);
-                File.Copy(file, moveTo, true);
-                status = true;
-            }
-            else
-            {
-
-            }
-            return status;
-        }
-
-        /// <summary>
-        /// 取得第幾行的字串
-        /// </summary>
-        /// <param name="InputString"></param>
-        /// <param name="SearchString"></param>
-        /// <returns></returns>
-        private static string GetLineFromString(string InputString, string SearchString)
-        {
-            using (StringReader reader = new StringReader(InputString))
-            {
-                int lineNumber = 1;
-                string line = "";
-                while ((line = reader.ReadLine()) != null)
-                {
-                    if (line.Contains(SearchString))
-                    {
-                        return line;
-                    }
-                    lineNumber++;
-                }
-
-                return reader.ReadLine();
-            }
-        }
-
-
-
     }
-}
+}

+ 1 - 0
CAUtilLib/CAUtilLib.csproj

@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
   </ItemGroup>

+ 716 - 0
CAUtilLib/CaUtil_openssl.cs

@@ -0,0 +1,716 @@
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
+using System;
+using System.Diagnostics;
+using System.Diagnostics.Metrics;
+using System.Reflection;
+using System.Runtime.ConstrainedExecution;
+using System.Security.AccessControl;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Xml.Linq;
+
+namespace CAUtilLib
+{
+    public partial class CaUtil_openssl
+    {
+        /// <summary>
+        /// 建構子生成路徑可透過SetPath進行路徑覆寫
+        /// </summary>
+        public CaUtil_openssl(ILogger<CaUtil> logger)
+        {
+            if (string.IsNullOrWhiteSpace(this.path))
+            {
+                string path = Directory.GetCurrentDirectory();
+                //string parentPath = Directory.GetParent(Directory.GetParent(Directory.GetParent(Directory.GetParent(path).ToString()).ToString()).ToString()) + @"\";
+                string filePath = Path.Combine(path, "temp");
+                bool exists = Directory.Exists(filePath);
+                if (!exists)
+                {
+                    CreateFolder(filePath);
+                    MoveFile(@"zerova_v3.cnf", filePath + @"\zerova_v3.cnf");
+                }
+                this.path = filePath;
+            }
+
+            this.logger = logger;
+        }
+
+        private string path = "";
+        private readonly ILogger<CaUtil> logger;
+
+        /// <summary>
+        /// 檔案路徑
+        /// </summary>
+        public string SavePath
+        {
+            get => path;
+            set
+            {
+                if (this.path == value)
+                {
+                    return;
+                }
+
+                this.path = value;
+                string filePath = this.path;
+                CreateFolder(filePath);
+                MoveFile(@"zerova_v3.cnf", Path.Combine(filePath, @"zerova_v3.cnf"));
+            }
+        }
+
+        public OSType OS { get; set; }
+
+        private string ExecuteShell => OS == OSType.Windows ? "cmd.exe" : "/bin/bash";
+        private string CatCmd => OS == OSType.Windows ? "type " : "cat";
+
+        /// <summary>
+        /// 生成RootCA憑證
+        /// </summary>
+        /// <param name="name">Root CA</param>
+        /// <param name="Days">憑證有效期限</param>
+        /// <param name="SerialNumber">序號</param>
+        /// <param name="Cn">主機名或網站地址</param>
+        /// <param name="organizationName">組織名稱</param>
+        /// <param name="Hash">使用SHA256/SHA512演算法進行簽名</param>
+        /// <param name="RsaKey">生成2048位元或4096位元的私鑰</param>
+        /// <param name="Caformat">生成crt 或者pem 格式</param>
+        /// <returns></returns>
+        public async Task<bool> CreateRootCA(
+            string name, 
+            string commonName,
+            string organizationName,
+            string Country = "TW",
+            string State = "Taipei",
+            int Days = 3650,
+            string SerialNumber = "",
+            HashAlgorithm Hash = HashAlgorithm.SHA512,
+            OpensslGenrsaRsa RsaKey = OpensslGenrsaRsa.Key4096)
+        {
+            if (string.IsNullOrEmpty(SerialNumber))
+            {
+                SerialNumber = await GetOpenSSLRandSn();
+            }
+
+            var status = false;
+            var keyName = "";
+            keyName = name;
+            string sn = "0x" + SerialNumber;
+            HashAlgorithm ha = Hash;
+            var sslKey = (int)RsaKey;
+
+            var subjString = CreateSubjectString(CommonName: commonName, Organization: organizationName, Country: Country, State: State);
+
+            if (await CreateKey(keyName, RsaKey) &&
+                await ExecShellCmd("openssl", $"req -x509 -new -nodes -set_serial {sn} -key {keyName}.key -{ha} -days {Days} -subj \"{subjString}\" -out {name}.crt ") &&
+                await MergeFile($"{name}.pem", $"{name}.crt", $"{keyName}.key")
+                //&& await ExecShellCmd("certutil", $"-f -addstore root {RootCa}.crt ") 
+                //&& await ExecShellCmd("certutil", $"-f -addstore root {RootCa}.crt ")
+                )
+            {
+                return true;
+            }
+
+            return false;
+            //create
+            string[] strs = new string[5];
+            strs[0] = "openssl genrsa  -out " + keyName + ".key " + sslKey + " ";
+            strs[1] = "openssl req -x509 -new -nodes  -set_serial " + sn + " -key " + keyName + ".key  -" + ha + " -days " + Days + "  -subj \"/C=TW/ST=Taipei/O=" + organizationName + "/OU=phihong_sub.IO/CN=" + commonName + "/emailAddress=phihong_sub@mail.com\" -out " + name + ".crt ";
+            strs[2] = CatCmd + name + ".crt " + keyName + ".key > " + name + ".pem ";
+            strs[3] = "certutil -f -addstore root " + name + ".crt ";
+            strs[4] = "certutil -f -addstore root " + name + ".crt ";
+
+            for (int i = 0; i < strs.Length; i++)
+            {
+                var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        FileName = ExecuteShell,
+                        Arguments = "/C " + strs[i],
+                        WorkingDirectory = this.path,
+                        StandardOutputEncoding = Encoding.UTF8,
+                        RedirectStandardOutput = true,
+                        RedirectStandardError = true,
+                        UseShellExecute = false,
+                        Verb = "runas"
+                    }
+                };
+
+                process.Start();
+                _ = process.StandardOutput.ReadToEndAsync().ContinueWith(( t => {
+                    logger.LogTrace(t.Result);
+                }));
+                _ = process.StandardError.ReadToEndAsync().ContinueWith((t => {
+                    logger.LogTrace(t.Result);
+                }));
+                await process.WaitForExitAsync();
+                if (process.ExitCode != 0)
+                {
+                    return false;
+                }
+            }
+
+            return status;
+        }
+
+        /// <summary>
+        /// 生成SubCA子憑證
+        /// </summary>
+        /// <param name="RootCaName">CA憑證名稱</param>
+        /// <param name="Days">憑證有效期限</param>
+        /// <param name="SubCA">子憑證名稱</param>
+        /// <param name="SerialNumber">序號</param>
+        /// <param name="commonName">主機名或網站地址</param>
+        /// <param name="organizationName">組織名稱</param>
+        /// <param name="Hash">使用SHA256/SHA512演算法進行簽名</param>
+        /// <param name="RsaKey">生成2048位元或4096位元的私鑰</param>
+        /// <param name="Caformat">生成crt 或者pem 格式</param>
+        /// <returns></returns>        
+        public async Task<bool> CreateSubCA(string RootCa, string Days,
+        string SubCA, string SerialNumber, string commonName, string organizationName, HashAlgorithm Hash, OpensslGenrsaRsa RsaKey, Certificateformat Caformat)
+        {
+
+            if (string.IsNullOrEmpty(SerialNumber))
+            {
+                SerialNumber = await GetOpenSSLRandSn();
+            }
+
+            var status = false;
+            var str = "";
+            string sn = "0x" + SerialNumber;
+            //hashAlgorithm ha = Hash;
+            var sslKey = (int)RsaKey;
+            Certificateformat format = Caformat;
+
+            if (await ExecShellCmd("openssl", $"genrsa -out {SubCA}.key {sslKey}") &&
+                await ExecShellCmd("openssl", $"req -new -{Hash} -nodes -key {SubCA}.key -out {SubCA}.csr -subj \"/C=TW/ST=Taipei/O={organizationName}/OU=phihong_sub.IO/CN={commonName}/emailAddress=phihong_sub@mail.com\" ") &&
+                await ExecShellCmd("openssl", $"x509 -set_serial {sn} -req -in {SubCA}.csr -CA {RootCa}.crt -CAkey {RootCa}.key -CAcreateserial -out {SubCA}.crt -days {Days} -sha256 -extfile zerova_v3.cnf  ") &&
+                await MergeFile($"{SubCA}.pem", $"{SubCA}.crt", $"{SubCA}.key"))
+            {
+                return true;
+            }
+            return false;
+
+
+            string[] strs = new string[4];
+            strs[0] = "openssl genrsa -out " + SubCA + ".key " + sslKey + "  ";
+            strs[1] = "openssl req -new -" + Hash + " -nodes -key " + SubCA + ".key -out " + SubCA + ".csr -subj \"/C=TW/ST=Taipei/O=" + organizationName + "/OU=phihong_sub.IO/CN=" + commonName + "/emailAddress=phihong_sub@mail.com\" ";
+            //var str8 = "openssl x509 -set_serial " + sn + " -req -in " + subKeyName + ".csr -CA " + caPath + "\\" + crtName + ".crt -CAkey " + this.path + "\\" + rootCaName + ".key -CAcreateserial -out " + subKeyName + ".crt -days " + days + "   -sha256 -extfile zerova_v3.cnf  ";
+            strs[2] = "openssl x509 -set_serial " + sn + " -req -in " + SubCA + ".csr -CA " + RootCa + ".crt -CAkey " + this.path + "\\" + RootCa + ".key -CAcreateserial -out " + SubCA + ".crt -days " + Days + "   -sha256 -extfile zerova_v3.cnf  ";
+            strs[3] = CatCmd + SubCA + ".crt " + SubCA + ".key > " + SubCA + ".pem ";
+
+
+            for (int i = 0; i < strs.Length; i++)
+            {
+                var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        FileName = ExecuteShell,
+                        Arguments = "/C " + strs[i],
+                        WorkingDirectory = this.path,//"D:\\project\\vs\\ConsoleApp2",
+                        RedirectStandardOutput = true,
+                        UseShellExecute = false,
+                        Verb = "runas"
+                    }
+                };
+
+                process.Start();
+                string output = process.StandardOutput.ReadToEnd();
+                await process.WaitForExitAsync();
+            }
+            return status;
+        }
+
+        public async Task<bool> SignCsr(string SubCA, string RootCa, int? Days = null, string? SerialNumber = null)
+        {
+            Days ??= 3650;
+
+            if (string.IsNullOrEmpty(SerialNumber))
+            {
+                SerialNumber = await GetOpenSSLRandSn();
+            }
+            string sn = "0x" + SerialNumber;
+
+            if (await ExecShellCmd("openssl", $"x509 -set_serial {sn} -req -in {SubCA}.csr -CA {RootCa}.crt -CAkey {RootCa}.key -CAcreateserial -out {SubCA}.crt -days {Days} -sha256 -extfile zerova_v3.cnf  "))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// 生成Csr 檔案
+        /// </summary>
+        /// <param name="Key">key 名稱</param>
+        /// <param name="Csr">csr 名稱</param>
+        /// <param name="commonName">主機名或網站地址</param>
+        /// <param name="organizationName">組織名稱</param>
+        /// <returns></returns>
+        public async Task<bool> CreateCsr(
+            string Key,
+            string Csr,
+            string commonName, 
+            string organizationName,
+            HashAlgorithm Hash = HashAlgorithm.SHA512)
+        {
+            var status = false;
+            var subjString = CreateSubjectString(CommonName: commonName, Organization: organizationName, Country: "TW", State: "Taipei");
+
+            if (await ExecShellCmd("openssl", $"req -new -{Hash} -nodes -key {Key}.key -out {Csr}.csr -subj \"{subjString}\" "))
+            {
+                return true;
+            }
+            return false;
+
+            string[] strs = new string[4];
+            strs[0] = "openssl req -new -SHA256 -nodes -key " + Key + ".key -out " + Csr + ".csr -subj \"/C=TW/ST=Taipei/O=" + organizationName + "/OU=phihong_sub.IO/CN=" + commonName + "/emailAddress=phihong_sub@mail.com\"  ";
+            for (int i = 0; i < strs.Length; i++)
+            {
+                var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        FileName = ExecuteShell,
+                        Arguments = "/C " + strs[i],
+                        WorkingDirectory = this.path,//"D:\\project\\vs\\ConsoleApp2",
+                        RedirectStandardOutput = true,
+                        UseShellExecute = false,
+                        Verb = "runas"
+                    }
+                };
+
+                process.Start();
+                string output = process.StandardOutput.ReadToEnd();
+                await process.WaitForExitAsync();
+            }
+            return status;
+        }
+
+        private object CreateSubjectString(string CommonName = "", string Organization = "", string Country = "", string State = "")
+        {
+            ///C=TW/ST=Taipei/O={organizationName}/OU=phihong_sub.IO/CN={commonName}/emailAddress=phihong_sub@mail.com\
+            var toReturn = string.Empty;
+            if (!string.IsNullOrEmpty(Country))
+            {
+                toReturn += $"/C={Country}";
+            }
+            if (!string.IsNullOrEmpty(State))
+            {
+                toReturn += $"/ST={State}";
+            }
+            if (!string.IsNullOrEmpty(Organization))
+            {
+                toReturn += $"/O={Organization}";
+            }
+            if (!string.IsNullOrEmpty(CommonName))
+            {
+                toReturn += $"/CN={CommonName}";
+            }
+            return toReturn;
+        }
+
+        /// <summary>
+        /// 讀取憑證資訊
+        /// </summary>
+        /// <param name="path">檔案名稱</param>
+        /// <param name="fileName">檔案路徑</param>
+        /// <returns></returns>
+        public string ReadCertificateHashData(string path, string fileName)//改成接收pem string 格式
+        {
+            var json = "";
+            var file = Path.Combine(path, fileName);
+            X509Certificate2 certificate = new X509Certificate2(file);
+
+
+            X509Extension extension = certificate.Extensions["2.5.29.35"];
+            var issuer_key_hash = "";
+            if (extension != null)
+            {
+                issuer_key_hash = extension.Format(true);
+            }
+
+            string hashAlgorithm = certificate.SignatureAlgorithm.FriendlyName;
+
+
+            string serialNumber = certificate.SerialNumber;
+            byte[] issuerDER = certificate.IssuerName.RawData;
+            SHA1 sha1 = SHA1.Create();
+            byte[] hashBytes = sha1.ComputeHash(issuerDER);
+
+            var data = new
+            {
+                hashAlgorithm = hashAlgorithm,
+                issuerNameHash = BitConverter.ToString(hashBytes).Replace("-", ""),
+                issuerKeyHash = issuer_key_hash,
+                serialNumber = serialNumber,
+                thumbprint = certificate.Thumbprint
+            };
+            string thumbprint = certificate.Thumbprint;
+
+            json = JsonConvert.SerializeObject(data);
+            return json;
+        }
+
+        /// <summary>
+        /// 讀取憑證資訊
+        /// </summary>
+        /// <param name="FileName">檔案名稱</param>
+        /// <returns></returns>
+        public string ReadCertificateHashData(string fileName)
+        {
+            var json = "";
+            var file = Path.Combine(this.path, fileName);
+            X509Certificate2 certificate = new X509Certificate2(file);
+
+            X509Extension extension = certificate.Extensions["2.5.29.35"];
+            var issuer_key_hash = "";
+            if (extension != null)
+            {
+                issuer_key_hash = extension.Format(true);
+            }
+
+            string hashAlgorithm = certificate.SignatureAlgorithm.FriendlyName;
+
+            string serialNumber = certificate.SerialNumber;
+            byte[] issuerDER = certificate.IssuerName.RawData;
+            SHA1 sha1 = SHA1.Create();
+            byte[] hashBytes = sha1.ComputeHash(issuerDER);
+
+            var data = new
+            {
+                hashAlgorithm,
+                issuerNameHash = BitConverter.ToString(hashBytes).Replace("-", ""),
+                issuerKeyHash = issuer_key_hash,
+                serialNumber,
+                thumbprint = certificate.Thumbprint
+            };
+            string thumbprint = certificate.Thumbprint;
+
+            json = JsonConvert.SerializeObject(data);
+            return json;
+        }
+
+        /// <summary>
+        /// PEM 格式的憑證的內容
+        /// </summary>
+        /// <param name="str">憑證內容</param>
+        /// <returns></returns>
+        public string ReadCertificateHashDataByString(string str)
+        {
+            var json = "";
+            byte[] UTF8bytes = Encoding.UTF8.GetBytes(str);
+            X509Certificate2 certificate = new X509Certificate2(UTF8bytes);
+
+
+            X509Extension extension = certificate.Extensions["2.5.29.35"];
+            var issuer_key_hash = "";
+            if (extension != null)
+            {
+                issuer_key_hash = extension.Format(true);
+            }
+
+            string hashAlgorithm = certificate.SignatureAlgorithm.FriendlyName;
+
+
+            string serialNumber = certificate.SerialNumber;
+            byte[] issuerDER = certificate.IssuerName.RawData;
+            SHA1 sha1 = SHA1.Create();
+            byte[] hashBytes = sha1.ComputeHash(issuerDER);
+
+            var data = new
+            {
+                hashAlgorithm = hashAlgorithm,
+                issuerNameHash = BitConverter.ToString(hashBytes).Replace("-", ""),
+                issuerKeyHash = issuer_key_hash,
+                serialNumber = serialNumber,
+                thumbprint = certificate.Thumbprint
+            };
+            string thumbprint = certificate.Thumbprint;
+
+            json = JsonConvert.SerializeObject(data);
+            return json;
+        }
+
+
+        /// <summary>
+        /// 驗證client憑證
+        /// </summary>
+        /// <param name="StrCA">CA憑證</param>
+        /// <param name="StrSub">子憑證</param>
+        /// <param name="Url">url localhost:3000</param>
+        /// <returns></returns>
+        public async Task<bool> VerifyClient(string CA, string SubCA, string Url)
+        {
+            var str = "";
+            bool isExists = false;
+            if (await ExecShellCmd("openssl", $"s_client -connect {Url} -CAfile {CA} -cert {SubCA} -tls1_2 -state"))
+            {
+                return true;
+            }
+            return false;
+
+
+            var str1 = "openssl s_client -connect " + Url + " -CAfile " + CA + " -cert " + SubCA + " -tls1_2 -state  ";
+
+            str = await ClientCmd(str1);
+            string searchString = "(ok)";
+            string line = GetLineFromString(str, searchString);
+            if (line != null)
+                isExists = line.Contains(searchString);
+            return isExists;
+        }
+
+        /// <summary>
+        /// Cmd字串指令
+        /// </summary>
+        /// <param name="str1">字串指令</param>
+        /// <returns></returns>
+        private async Task<string> ClientCmd(string str1)
+        {
+            var status = false;
+
+            var str = "";
+            string[] strs = { str1 };
+            for (int i = 0; i < strs.Length; i++)
+            {
+                var process = new Process
+                {
+                    StartInfo = new ProcessStartInfo
+                    {
+                        FileName = ExecuteShell,
+                        Arguments = "/C " + strs[i],
+                        WorkingDirectory = this.path,//"D:\\project\\vs\\ConsoleApp2",
+                        RedirectStandardOutput = true,
+                        UseShellExecute = false,
+                        //Verb = "runas"
+                    }
+                };
+
+                process.Start();
+                var output = new List<string>();
+                while (process.StandardOutput.Peek() > -1)
+                {
+                    output.Add(process.StandardOutput.ReadLine());
+                }
+
+                process.Kill();
+                str = string.Join("", output.ToArray());
+
+            }
+            return str;
+        }
+
+        /// <summary>
+        ///  將Root CA與Sub CA進行憑證驗證
+        /// </summary>
+        /// <param name="Ca">CA檔案名稱</param>
+        /// <param name="Sub">SubCA憑證名稱</param>
+        /// <returns>True/Flase</returns>
+        public async Task<bool> VerifyCertificateByCertificate(string Sub, string Ca)
+        {
+            if (await ExecShellCmd("openssl", $"verify -CAfile {Ca} {Sub}"))
+            {
+                return true;
+            }
+            return false;
+
+            var status = true;
+            var str = "";
+            var str1 = "openssl verify -CAfile " + Ca + " " + Sub;
+
+            bool isExists = false;
+            str = await ClientCmd(str1);
+            string searchString = "OK";
+            string line = GetLineFromString(str, searchString);
+            if (line != null)
+                isExists = line.Contains(searchString);
+            return isExists;
+        }
+
+        public async Task<bool> VerifyCertificateByCertificates(string Sub, string CaPath)
+        {
+            if (await ExecShellCmd("openssl", $"verify -CApath {CaPath} {Sub}"))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        public async Task<bool> CalculateHash(string CrtName,string hashName)
+        {
+            if (await ExecShellCmd("openssl", $"x509 -in {CrtName} -hash -noout -out {hashName}"))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Crt轉Pem
+        /// </summary>
+        /// <param name="CaName">檔案名稱</param>
+        /// <returns></returns>
+        public async Task<bool> CrtToPem(string CaName)
+        {
+            if (await ExecShellCmd("openssl", $"x509 -in {CaName}.crt -out {CaName}.pem"))
+            {
+                return true;
+            }
+            return false;
+
+            var status = false;
+            var str = "openssl x509 -in " + CaName + ".crt -out " + CaName + ".pem  ";
+
+            var process = new Process
+            {
+                StartInfo = new ProcessStartInfo
+                {
+                    FileName = ExecuteShell,
+                    Arguments = "/C " + str,
+                    WorkingDirectory = this.path,
+                    RedirectStandardOutput = true,
+                    UseShellExecute = false,
+                    Verb = "runas"
+                }
+            };
+
+            process.Start();
+            string output = process.StandardOutput.ReadToEnd();
+            await process.WaitForExitAsync();
+            return status;
+        }
+
+        /// <summary>
+        /// Pem轉Crt
+        /// </summary>
+        /// <param name="CaName">檔案名稱</param>
+        /// <returns></returns>
+        public async Task<bool> PemToCrt(string CaName)
+        {
+            if (await ExecShellCmd("openssl", $"x509 -outform der -in {CaName}.pem -out {CaName}.crt"))
+            {
+                return true;
+            }
+            return false;
+
+            var status = false;
+            var str = "openssl x509 -outform der -in " + CaName + ".pem -out " + CaName + ".crt  ";
+
+            var process = new Process
+            {
+                StartInfo = new ProcessStartInfo
+                {
+                    FileName = ExecuteShell,
+                    Arguments = "/C " + str,
+                    WorkingDirectory = this.path,
+                    RedirectStandardOutput = true,
+                    UseShellExecute = false,
+                    Verb = "runas"
+                }
+            };
+
+            process.Start();
+            string output = process.StandardOutput.ReadToEnd();
+            await process.WaitForExitAsync();
+            return status;
+        }
+
+        public async Task<bool> CreateKey(string keyName,
+            OpensslGenrsaRsa RsaKey = OpensslGenrsaRsa.Key4096)
+        {
+            var sslKey = (int)RsaKey;
+            if (await ExecShellCmd("openssl", $"genrsa -out {keyName}.key {sslKey}"))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// 產生資料夾
+        /// </summary>
+        /// <param name="path"></param>
+        /// <returns></returns>
+        private bool CreateFolder(string path)
+        {
+            string subPath = path;
+            bool status = true;
+            bool exists = Directory.Exists(subPath);
+            status = !exists;
+
+            if (status)
+            {
+                Directory.CreateDirectory(subPath);
+            }
+
+            return status;
+        }
+
+        /// <summary>
+        /// 確認檔案是否存在
+        /// </summary>
+        /// <param name="filePath"></param>
+        /// <returns></returns>
+        private bool CheckFiles(string FilePath)
+        {
+            bool status = true;
+            bool exists = File.Exists(FilePath);
+            status = exists;
+            return status;
+        }
+
+        /// <summary>
+        /// 檔案搬移
+        /// </summary>
+        /// <param name="file"></param>
+        /// <param name="moveTo"></param>
+        /// <returns></returns>
+        private bool MoveFile(string file, string moveTo)
+        {
+            bool status = false;
+            if (CheckFiles(file) &&
+                !CheckFiles(moveTo))
+            {
+                //File.Move(file, moveTo);
+                File.Copy(file, moveTo, true);
+                status = true;
+            }
+            else
+            {
+
+            }
+            return status;
+        }
+
+        /// <summary>
+        /// 取得第幾行的字串
+        /// </summary>
+        /// <param name="InputString"></param>
+        /// <param name="SearchString"></param>
+        /// <returns></returns>
+        private static string GetLineFromString(string InputString, string SearchString)
+        {
+            using (StringReader reader = new StringReader(InputString))
+            {
+                int lineNumber = 1;
+                string line = "";
+                while ((line = reader.ReadLine()) != null)
+                {
+                    if (line.Contains(SearchString))
+                    {
+                        return line;
+                    }
+                    lineNumber++;
+                }
+
+                return reader.ReadLine();
+            }
+        }
+
+
+
+    }
+}

+ 1 - 1
CAUtilLib/CaUtil_1.cs → CAUtilLib/CaUtil_openssl_1.cs

@@ -18,7 +18,7 @@ namespace CAUtilLib
         public static implicit operator bool(ExecShellCmdResult result) => result.IsSuccess;
     }
 
-    public partial class CaUtil
+    public partial class CaUtil_openssl
     {
         public async Task<bool> MergeFile(string outFile, string inFile1, string inFile2)
         {

+ 1 - 0
CertificateAutorityServer/CertificateAutorityServer.csproj

@@ -9,6 +9,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
     <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.11" />
     <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />

+ 0 - 59
CertificateAutorityServer/Controllers/CertificateController.cs

@@ -1,59 +0,0 @@
-using CAUtilLib;
-using CertificateAutorityServer.Assist;
-using CertificateAutorityServer.Model.Rest;
-using CertificateAutorityServer.Service;
-using Microsoft.AspNetCore.Mvc;
-
-namespace CertificateAutorityServer.Controllers
-{
-    [ApiController]
-    [Route("cert")]
-    public class CertificateController : Controller
-    {
-        private readonly CertificateService certificateService;
-
-        public CertificateController(CertificateServiceFactory certificateServiceFactory)
-        {
-            this.certificateService = certificateServiceFactory.Create();
-        }
-
-        [HttpGet("RootCA")]
-        public IActionResult GetRootCAList()
-        {
-            return Ok();
-            //return Ok(certificateService.GetRootCAList());
-        }
-
-        [HttpGet("RootCA/{name}")]
-        public IActionResult GetRootCAPem(string name)
-        {
-            return Ok();
-        }
-
-        [HttpPost("RootCA")]
-        public async Task<IActionResult> CreateRootCA([FromBody] CreateRootCaReq req)
-        {
-            return Ok();
-            //var result = await certificateService.CreateRootCA(req);
-            //return result ? Ok() : Problem();
-        }
-
-        [HttpPost("SubCA")]
-        public async Task<IActionResult> CreateSubCA([FromBody] CreateSubCaReq req)
-        {
-            return Ok();
-            //var result = await certificateService.CreateSubCA(req);
-            //return result ? Ok() : Problem();
-        }
-
-        [HttpDelete("RootCA/{name}")]
-        public IActionResult RemoveRootCA(string name)
-        {
-            return Ok();
-            //certificateService.RemoveRootCA(name);
-            //return Ok();
-        }
-
-        /////////////////////////////////////////////
-    }
-}

+ 45 - 0
CertificateAutorityServer/Controllers/Rca/RcaController.cs

@@ -0,0 +1,45 @@
+using CertificateAutorityServer.Model.Rest;
+using CertificateAutorityServer.Service;
+using Microsoft.AspNetCore.Mvc;
+
+namespace CertificateAutorityServer.Controllers.Rca
+{
+    [ApiController]
+    [Route("rca")]
+    public class RcaController : Controller
+    {
+        public RcaController(CertificateService certificateService)
+        {
+            this.certificateService = certificateService;
+        }
+        private readonly CertificateService certificateService;
+
+        [HttpGet]
+        public async Task<IActionResult> GetAsPem()
+        {
+            var result = await certificateService.GetRcaAsPem();
+            return Ok(result);
+        }
+
+        [HttpPost("csr")]
+        public async Task<IActionResult> ReqCsrSign(SignCsrReq signCsrReq)
+        {
+            var result = await certificateService.SignCsrByRca(signCsrReq);
+            return Ok(result);
+        }
+
+        [HttpPost("check")]
+        public async Task<IActionResult> CheckCrtByRca(CheckCrtReq checkCrtReq)
+        {
+            var result = await certificateService.CheckCsrByRca(checkCrtReq.Crt);
+            return result ? Ok() : Problem();
+        }
+
+        [HttpPost("regenerate")]
+        public IActionResult ReCreateRca(bool addTrusted = true)
+        {
+            certificateService.ReGenerateRca(addTrusted);
+            return Ok();
+        }
+    }
+}

+ 37 - 0
CertificateAutorityServer/Controllers/Rca/TrustedCrtController.cs

@@ -0,0 +1,37 @@
+using CertificateAutorityServer.Model.Rest;
+using CertificateAutorityServer.Service;
+using Microsoft.AspNetCore.Mvc;
+
+namespace CertificateAutorityServer.Controllers.Rca
+{
+    [ApiController]
+    [Route("tcrt")]
+    public class TrustedCrtController : Controller
+    {
+        public TrustedCrtController(CertificateService certificateService)
+        {
+            this.certificateService = certificateService;
+        }
+        private readonly CertificateService certificateService;
+
+        [HttpGet]
+        public IActionResult GetList()
+        { 
+            return Ok(certificateService.GetTrustedCrts());
+        }
+
+        [HttpPost]
+        public IActionResult AddTrustedCrt(AddTrustedCertificateReq addTrustedCertificateReq)
+        {
+            certificateService.AddTrustedCrt(addTrustedCertificateReq.Crt);
+            return Ok();
+        }
+
+        [HttpPost("check")]
+        public async Task<IActionResult> CheckCrtByTrustedCrt(CheckCrtReq checkCrtReq)
+        {
+            var result = await certificateService.VerifyCrtSignedByTrused(checkCrtReq.Crt);
+            return result ? Ok() : Problem();
+        }
+    }
+}

+ 73 - 0
CertificateAutorityServer/Controllers/RootController.cs

@@ -0,0 +1,73 @@
+using CAUtilLib;
+using CertificateAutorityServer.Model.Rest;
+using CertificateAutorityServer.Service;
+using Microsoft.AspNetCore.Mvc;
+
+namespace CertificateAutorityServer.Controllers
+{
+    [ApiController]
+    [Route("crt")]
+    public class RootController : Controller
+    {
+        public RootController(CertificateService certificateService) {
+            this.certificateService = certificateService;
+        }
+        private readonly CertificateService certificateService;
+
+        [HttpGet("key")]
+        public IActionResult CreatNewKey(int length = 4096) {
+            var key = certificateService.GenerateRsaPrivateKeyPemString(length);
+            return Ok(key);
+        }
+
+        [HttpGet("rca")]
+        public async Task<IActionResult> CreatSelfSignedCertificate([FromForm] CreateRootCaReq createRootCaReq)
+        {
+            var subject = BouncyCastleWrapper.CreateX509Name(
+                commonName: createRootCaReq.CommonName,
+                organizationName: createRootCaReq.OrganizationName,
+                country: createRootCaReq.Country,
+                state: createRootCaReq.State);
+
+            var key = BouncyCastleWrapper.LoadPemKeyFromString(createRootCaReq.Key);
+            if (key is null)
+            {
+                return BadRequest("key parse failed");
+            }
+            var cert = BouncyCastleWrapper.GenerateCertificate(subject, subject, key, key.Public , isCertificateAuthority: true);
+            if (cert is null)
+            {
+                return BadRequest("certificate create failed");
+            }
+            var certStrig = await BouncyCastleWrapper.ToStringAsPem(cert);
+            return Ok(certStrig);
+        }
+
+        [HttpGet("csr")]
+        public IActionResult CreatCsr([FromForm] CreateCsrReq createCsrReq)
+        {
+            var csr = certificateService.CreateCertificateReq(createCsrReq);
+            return Ok(csr);
+        }
+
+        [HttpGet("crt")]
+        public async Task<IActionResult> CreatCrt([FromForm] SignCsrReq signCsrReq)
+        {
+            var csr = BouncyCastleWrapper.LoadPemCsrFromString(signCsrReq.Csr);
+            var rca = BouncyCastleWrapper.LoadPemCertFromString(signCsrReq.Rca);
+            var key = BouncyCastleWrapper.LoadPemKeyFromString(signCsrReq.RcaKey);
+            var crt = BouncyCastleWrapper.SignCertificate(csr, rca, key , isCertificateAuthority: signCsrReq.IsCa);
+            var crtString = await BouncyCastleWrapper.ToStringAsPem(crt);
+            return Ok(crtString);
+        }
+
+        [HttpPost("validate")]
+        public IActionResult ValidateCertificate([FromForm] CheckCrtReq checkCrtReq)
+        {
+            var cert = BouncyCastleWrapper.LoadPemCertFromString(checkCrtReq.Crt);
+            var certChain = BouncyCastleWrapper.LoadPemCrtChaingFromString(checkCrtReq.CertChain);
+            var result = BouncyCastleWrapper.ValidateCert(cert, certChain);
+            return result ? Ok() : BadRequest();
+        }
+    }
+}

+ 0 - 85
CertificateAutorityServer/Controllers/cert/CrtController.cs

@@ -1,85 +0,0 @@
-using CertificateAutorityServer.Model.Rest;
-using CertificateAutorityServer.Service;
-using Microsoft.AspNetCore.Mvc;
-
-namespace CertificateAutorityServer.Controllers.cert
-{
-    [Route("cert/crt")]
-    public class CrtController : Controller
-    {
-        private readonly CertificateService certificateService;
-
-        public CrtController(CertificateServiceFactory certificateServiceFactory)
-        {
-            this.certificateService = certificateServiceFactory.Create();
-        }
-
-        [HttpGet]
-        public async Task<IActionResult> CreateCrt([FromBody] CreateCrtReq req)
-        {
-            if (req is null)
-            {
-                return Problem();
-            }
-
-            bool createKey = string.IsNullOrEmpty(req.key);
-            string? createKeyResult = await GetOrCreateKey(req.key);
-            if (createKeyResult == null) { 
-                return Problem();
-            }
-            string key = createKeyResult;
-
-            string? createCsrResult = await certificateService.CreatCsr(new CreateCsrReq()
-            {
-                Key = key,
-                CommonName = req.CommonName,
-                OrganizationName = req.OrganizationName
-            });
-            if (createCsrResult is null)
-            {
-                return Problem();
-            }
-
-            string? signCsrResult = await certificateService.SignCsr(new SignCsrReq() { Csr = createCsrResult });
-            if (signCsrResult is null)
-            {
-                return Problem();
-            }
-            return Ok(createKey ? key + signCsrResult : signCsrResult);
-        }
-
-        [HttpPost]
-        public async Task<IActionResult> AddTrustedCertificate([FromBody] AddTrustedCertificateReq req)
-        {
-            bool result = await certificateService.AddTrustedCertificate(req.Crt);
-            return result ? Ok() : Problem();
-        }
-
-        [HttpPut]
-        public async Task<IActionResult> CheckCrt([FromBody] CheckCrtReq req)
-        {
-            if (string.IsNullOrEmpty(req.Crt))
-            {
-                return BadRequest();
-            }
-
-            int result = await certificateService.VerifyCrt(req);
-            return result != -1 ? Ok(result) : Problem(result.ToString());
-        }
-
-        private async ValueTask<string?> GetOrCreateKey(string key)
-        {
-            if (!string.IsNullOrEmpty(key))
-            {
-                return key;
-            }
-
-            var createKeyResult = await certificateService.CreatKey();
-            if (createKeyResult is null)
-            {
-                return null;
-            }
-            return createKeyResult;
-        }
-    }
-}

+ 0 - 41
CertificateAutorityServer/Controllers/cert/CsrController.cs

@@ -1,41 +0,0 @@
-using CertificateAutorityServer.Model.Rest;
-using CertificateAutorityServer.Service;
-using Microsoft.AspNetCore.Mvc;
-
-namespace CertificateAutorityServer.Controllers.cert
-{
-    [Route("cert/csr")]
-    public class CsrController : Controller
-    {
-        private readonly CertificateService certificateService;
-
-        public CsrController(CertificateServiceFactory certificateServiceFactory)
-        {
-            this.certificateService = certificateServiceFactory.Create();
-        }
-
-        [HttpGet]
-        public async Task<IActionResult> CreatCsr([FromBody] CreateCsrReq req)
-        {
-            if (string.IsNullOrEmpty(req.Key))
-            {
-                return BadRequest();
-            }
-
-            string? result = await certificateService.CreatCsr(req);
-            return result is null ? Problem() : Ok(result);
-        }
-
-        [HttpPost]
-        public async Task<IActionResult> SignCsr([FromBody] SignCsrReq req)
-        {
-            if (string.IsNullOrEmpty(req.Csr))
-            {
-                return BadRequest();
-            }
-
-            string? result = await certificateService.SignCsr(req);
-            return result is null ? Problem() : Ok(result);
-        }
-    }
-}

+ 0 - 23
CertificateAutorityServer/Controllers/cert/KeyController.cs

@@ -1,23 +0,0 @@
-using CertificateAutorityServer.Service;
-using Microsoft.AspNetCore.Mvc;
-
-namespace CertificateAutorityServer.Controllers.cert
-{
-    [Route("cert/key")]
-    public class KeyController : Controller
-    {
-        private readonly CertificateService certificateService;
-
-        public KeyController(CertificateServiceFactory certificateServiceFactory)
-        {
-            this.certificateService = certificateServiceFactory.Create();
-        }
-
-        [HttpGet]
-        public async Task<IActionResult> CreatKey()
-        {
-            string? result = await certificateService.CreatKey();
-            return result is null ? Problem() : Ok(result);
-        }
-    }
-}

+ 0 - 2
CertificateAutorityServer/Model/Config/CerServiceConfig.cs

@@ -3,8 +3,6 @@
     public class CerServiceConfig
     {
         public string RootPath { get; set; } = "/home/cert";
-        internal string RcaDirectoryName { get; set; } = "rca";
-        internal string TrustedDirectoryName { get; set; } = "tcrt";
         public RcaConfig DefaultRcaConfig { get; set; } = new RcaConfig();
     }
 

+ 1 - 1
CertificateAutorityServer/Model/Rest/CheckCrtReq.cs

@@ -3,6 +3,6 @@
     public class CheckCrtReq
     {
         public required string Crt { get; set; }
-        public string? Rca { get; set; }
+        public string? CertChain { get; set; }
     }
 }

+ 3 - 3
CertificateAutorityServer/Model/Rest/CreateCrtReq.cs

@@ -2,8 +2,8 @@
 {
     public class CreateCrtReq
     {
-        public string key { get; set; } = ""; 
-        public required string CommonName { get; set; }
-        public required string OrganizationName { get; set; }
+        public string key { get; set; } = string.Empty;
+        public string CommonName { get; set; } = string.Empty;
+        public string OrganizationName { get; set; } = string.Empty;
     }
 }

+ 1 - 0
CertificateAutorityServer/Model/Rest/CreateRootCaReq.cs

@@ -13,5 +13,6 @@ namespace CertificateAutorityServer.Model.Rest
         public string OrganizationName { get; set; } = string.Empty;
         public HashAlgorithm Hash { get; set; } = HashAlgorithm.SHA512;
         public OpensslGenrsaRsa RsaKey { get; set; } = OpensslGenrsaRsa.Key4096;
+        public string Key { get; set; } = string.Empty;
     }
 }

+ 3 - 2
CertificateAutorityServer/Model/Rest/SignCsrReq.cs

@@ -3,8 +3,9 @@
     public class SignCsrReq
     {
         public required string Csr { get; set; }
-        //public string? Rca { get; set; }
-        //public string? RcaKey { get; set; }
+        public required string Rca { get; set; }
+        public required string RcaKey { get; set; }
+        public bool IsCa { get; set; } = false;
         //public required string Name { get; set; }
         //public IFormFile? CsrFile { get; set; }
         //public int? Days { get; set; }

+ 3 - 1
CertificateAutorityServer/Program.cs

@@ -1,7 +1,9 @@
+using CertificateAutorityServer.Service;
+
 WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
 
 // Add services to the container.
-builder.AddCertificateService();
+builder.Services.AddSingleton<CertificateService>();
 
 builder.Services.AddControllers();
 // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle

+ 2 - 1
CertificateAutorityServer/Properties/launchSettings.json

@@ -5,7 +5,8 @@
       "launchBrowser": true,
       "launchUrl": "swagger",
       "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
+        "ASPNETCORE_ENVIRONMENT": "Development",
+        "DOTNET_gcServer": "0"
       },
       "dotnetRunMessages": true,
       "applicationUrl": "http://localhost:5156"

+ 103 - 208
CertificateAutorityServer/Service/CertificateService.cs

@@ -3,275 +3,170 @@ using CertificateAutorityServer.Assist;
 using CertificateAutorityServer.Model.Config;
 using CertificateAutorityServer.Model.Rest;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Ocsp;
+using Org.BouncyCastle.X509;
 
 namespace CertificateAutorityServer.Service
 {
     public class CertificateService
     {
-        public CertificateService(CaUtil caUtil, ILogger<CertificateService> logger)
+        public CertificateService(
+            IOptions<CerServiceConfig> config ,
+            ILogger<CertificateService> logger)
         {
-            this.caUtil = caUtil;
-            this.logger = logger;
-
-            RcaConfig = new RcaConfig();
-            RootPath = "/home/cert";
+            this.config = config.Value;
+            Init(config.Value);
         }
 
-        private string rootPath;
-        internal string RootPath
+        private string RootPath = "/home/cert";
+
+        private const string RcaDirectoryName = "rca";
+        private const string TrustedDirectoryName = "tcrt";
+
+        private const string RcaKeyName = "rca_key.pem";
+        private const string RcaCertName = "rca.pem";
+
+        private string RcaKeyPath => Path.Combine(RootPath, RcaDirectoryName, RcaKeyName);
+        private string RcaCertPath => Path.Combine(RootPath, RcaDirectoryName, RcaCertName);
+        private string TrustedCertsPath => Path.Combine(RootPath, TrustedDirectoryName);
+
+        private readonly CerServiceConfig config;
+
+        private void Init(CerServiceConfig value)
         {
-            get => rootPath;
-            set
-            {
-                if (rootPath == value)
-                {
-                    return;
-                }
-                rootPath = value;
-                caUtil.SavePath = rootPath;
-            }
+            this.RootPath = value.RootPath;
+            CheckAndCreateRootCA();
         }
 
-        internal string RcaDirectoryName { get; set; } = "rca";
-        internal string TrustedDirectoryName { get; set; } = "tcrt";
-        internal RcaConfig RcaConfig { get; set; }
-        //private static string RcaDirectoryName = "rca";
-        //private static string RcaName = "rca";
-        private readonly CaUtil caUtil;
-        private readonly ILogger<CertificateService> logger;
-
-        public async Task CheckAndCreateRootCA(string savePath)
+        public void CheckAndCreateRootCA()
         {
-            var rcaPath = Path.Combine(savePath, RcaDirectoryName);
-            if (File.Exists(Path.Combine(rcaPath, $"{RcaConfig.Name}.crt")))
+            CheckAndCreateFolder();
+            if (!File.Exists(RcaKeyPath))
             {
-                caUtil.SavePath = savePath;
+                AsymmetricCipherKeyPair kp = BouncyCastleWrapper.GenerateRsaKeyPair(4096);
+                var saveKeyResult = BouncyCastleWrapper.TrySaveAsPemAsync(RcaKeyPath, new object[] { kp.Private }).Result;
+                X509Certificate cert = BouncyCastleWrapper.GenerateSelfSignedCertificate(kp);
+                var saveCertResult = BouncyCastleWrapper.TrySaveAsPemAsync(RcaCertPath, new object[] { cert, kp.Private }).Result;
                 return;
             }
-
-            caUtil.SavePath = Path.Combine(savePath, RcaDirectoryName);
-
-            var result = await CreateRootCA(new CreateRootCaReq()
+            if (!File.Exists(RcaCertPath))
             {
-                Name = RcaConfig.Name,
-                CommonName = RcaConfig.CommonName, 
-                Country = RcaConfig.Country,
-                State = RcaConfig.State,
-                OrganizationName = RcaConfig.OrganiaztionName,
-                Days = RcaConfig.Days
-            });
-
-            caUtil.SavePath = savePath;
-
-            if (!result)
-            {
-                logger.LogCritical("create default root ca failed");
-                throw new Exception("create default root ca failed");
+                var kp = BouncyCastleWrapper.LoadPemKeyFromFile(RcaKeyPath).Result;
+                var cert = BouncyCastleWrapper.GenerateSelfSignedCertificate(kp, config.DefaultRcaConfig.CommonName);
+                var saveCertResult = BouncyCastleWrapper.TrySaveAsPemAsync(RcaCertPath, new object[] { cert, kp.Private }).Result;
             }
         }
 
-        public List<string> GetRootCAList()
+        public string GenerateRsaPrivateKeyPemString(int length = 4096)
         {
-            return Directory.GetFiles(caUtil.SavePath).ToList();
+            var kp = BouncyCastleWrapper.GenerateRsaKeyPair(length);
+            return BouncyCastleWrapper.ToStringAsPem(kp).Result;
         }
 
-        internal void RemoveRootCA(string name)
+        public string CreateCertificateReq(CreateCsrReq createCsrReq)
         {
-            var files = Directory.GetFiles(caUtil.SavePath);
-            var toRemoveFiles = files.Where(x => x.StartsWith(name));
-            foreach (var fileName in toRemoveFiles)
-            {
-                File.Delete(fileName);
-            }
+            var kp = BouncyCastleWrapper.LoadPemKeyFromString(createCsrReq.Key);
+            var csr = BouncyCastleWrapper.CreateCertificateReq(kp, createCsrReq.CommonName, createCsrReq.OrganizationName);
+            return BouncyCastleWrapper.ToStringAsPem(csr).Result;
         }
 
-        public async Task<bool> CreateRootCA(CreateRootCaReq req)
+        public async Task<string> GetRcaAsPem()
         {
-            bool result = await caUtil.CreateRootCA(
-                name: req.Name,
-                Days: req.Days,
-                SerialNumber: req.SerialNumber,
-                commonName: req.CommonName,
-                organizationName: req.OrganizationName,
-                Country: req.Country,
-                State: req.State,
-                Hash: req.Hash,
-                RsaKey: req.RsaKey);
-            return result;
+            var cert = await BouncyCastleWrapper.LoadPemCertFromFile(RcaCertPath);
+            return await BouncyCastleWrapper.ToStringAsPem(cert);
         }
 
-        public async Task<bool> CreateSubCA([FromForm] CreateSubCaReq req)
+        public async Task<string> SignCsrByRca(SignCsrReq signCsrReq)
         {
-            bool result = await caUtil.CreateSubCA(
-                RootCa: req.RootCa,
-                Days: req.Days,
-                SubCA: req.Name,
-                SerialNumber: req.SerialNumber,
-                commonName: req.CommonName,
-                organizationName: req.OrganizationName,
-                Hash: req.Hash,
-                RsaKey: req.RsaKey,
-                Caformat: req.Caformat);
-            return result;
+            var cert = await BouncyCastleWrapper.LoadPemCertFromFile(RcaKeyPath);
+            var rcaKey = await BouncyCastleWrapper.LoadPemKeyFromFile(RcaKeyPath);
+            var csr = BouncyCastleWrapper.LoadPemCsrFromString(signCsrReq.Csr);
+            var singedCert = BouncyCastleWrapper.SignCertificate(csr, cert, rcaKey);
+            return await BouncyCastleWrapper.ToStringAsPem(singedCert);
         }
 
-        public async Task<string?> SignCsr(SignCsrReq req)
+        public async Task<bool> CheckCsrByRca(string crt)
         {
-            var tempName = Guid.NewGuid().ToString();
-            var tempFile = Path.Combine(caUtil.SavePath, $"{tempName}.csr");
-
-            await CreateTempFile(tempFile, null, req.Csr);
-
-            //string rca = req.RCA is null ? Path.Combine(RcaDirectoryName, RcaConfig.Name) : req.RCA;
-            string rca = Path.Combine(RcaDirectoryName, RcaConfig.Name);
-            string rcaCrt = File.ReadAllText(Path.Combine(RootPath, rca + ".crt"));
+            var cert = BouncyCastleWrapper.LoadPemCertFromString(crt);
+            var rcaKey = await BouncyCastleWrapper.LoadPemKeyFromFile(RcaKeyPath);
+            return BouncyCastleWrapper.ValidateCert(cert, rcaKey.Public);
+        }
 
-            var result = await caUtil.SignCsr(
-                SubCA: tempName,
-                RootCa: rca,
-                Days: 365
-                );
-            if (!result)
+        public void ReGenerateRca(bool addTrusted = true)
+        {
+            var cert = BouncyCastleWrapper.LoadPemCertFromFile(RcaCertPath).Result;
+            if (addTrusted)
             {
-                return null;
+                AddTrustedCrt(cert);
             }
-            var resultFile = Path.Combine(caUtil.SavePath, $"{tempName}.crt");
-            var crt = await File.ReadAllTextAsync(resultFile);
-            File.Delete(resultFile);
-            File.Delete(tempFile);
 
-            //return crt + rcaCrt; //new TempPhysicalFileResult(resultFile, "application/octet-stream") { FileDownloadName = $"gen.crt" };
-            return crt;
-        }
-
-        internal async Task<int> VerifyCrt(CheckCrtReq req)
-        {
-            var isSignedByRca = await VerifyCrtSignedByRCA(req);
-            if (isSignedByRca)
+            if (File.Exists(RcaKeyPath))
             {
-                return 0;
+                File.Delete(RcaKeyPath);
             }
-            var isSignedByTrusted = await VerifyCrtSignedByTrused(req);
-            if (isSignedByTrusted)
+            if (File.Exists(RcaCertPath))
             {
-                return 1;
+                File.Delete(RcaCertPath);
             }
-            return -1;
+            CheckAndCreateRootCA();
         }
 
-        internal async Task<bool> VerifyCrtSignedByRCA(CheckCrtReq req)
+        public void AddTrustedCrt(string crtString)
         {
-            var tempName = Guid.NewGuid().ToString();
-            var tempFile = Path.Combine(caUtil.SavePath, $"{tempName}.crt");
-            await CreateTempFile(tempFile, null, req.Crt);
-
-            string rca = req.Rca is null ? Path.Combine(RcaDirectoryName, RcaConfig.Name) : req.Rca;
-            rca += ".crt";
-
-            var isSignedByRca = await caUtil.VerifyCertificateByCertificate(tempFile, rca);
-
-            File.Delete(tempFile);
-
-            return isSignedByRca;
+            var crt = BouncyCastleWrapper.LoadPemCertFromString(crtString);
+            AddTrustedCrt(crtString);
         }
 
-        internal async Task<bool> VerifyCrtSignedByTrused(CheckCrtReq req)
+        public void AddTrustedCrt(X509Certificate crt)
         {
-            var tempName = Guid.NewGuid().ToString();
-            var tempFile = Path.Combine(RootPath, $"{tempName}.crt");
-            await CreateTempFile(tempFile, null, req.Crt);
-
-            string rca = req.Rca is null ? Path.Combine(RcaDirectoryName, RcaConfig.Name) : req.Rca;
-            rca += ".crt";
-
-            var isSignedByRca = await caUtil.VerifyCertificateByCertificates(tempFile, Path.Combine(RootPath, TrustedDirectoryName));
-
-            File.Delete(tempFile);
-
-            return isSignedByRca;
+            var path = Path.Combine(TrustedCertsPath,  crt.SerialNumber.ToString());
+            BouncyCastleWrapper.TrySaveAsPemAsync(path, new object[] { crt }).Wait();
         }
 
-        internal async Task<bool> AddTrustedCertificate(string crt)
+        public List<string> GetTrustedCrts()
         {
-            var tempName = Guid.NewGuid().ToString();
-            var tempFile = Path.Combine(RootPath, $"{tempName}.crt");
-            await CreateTempFile(tempFile, null, crt);
-            var tempName2 = Guid.NewGuid().ToString();
-            var tempFile2 = Path.Combine(RootPath, tempName2);
-            var calResult = await caUtil.CalculateHash(tempFile, tempName2);
-            if (!calResult)
-            {
-                return false;
-            }
-            var hashString = File.ReadAllText(tempFile2).Trim();
-            var storePosition = Path.Combine(RootPath ,TrustedDirectoryName, $"{hashString}.0");
-            File.Copy(tempFile, storePosition);
-            //CalculateHash
-            File.Delete(tempFile);
-            File.Delete(tempFile2);
-
-            return true;
+            var files = Directory.GetFiles(TrustedCertsPath).Select(x=> Path.GetFileName(x)).ToList();
+            return files;
         }
 
-        public async Task<string?> CreatKey()
+        public Task<bool> VerifyCrtSignedByTrused(string crtString)
         {
-            var tempName = Guid.NewGuid().ToString();
-            var tempFileName = $"{tempName}.key";
-            var tempFile = Path.Combine(caUtil.SavePath, tempFileName);
-
-            var result = await caUtil.CreateKey(tempName);
-            if (!result)
-            {
-                return null;
-            }
-            var key = await File.ReadAllTextAsync(tempFile);
-            File.Delete(tempFile);
-
-            return key;
+            var crt = BouncyCastleWrapper.LoadPemCertFromString(crtString);
+            return VerifyCrtSignedByTrused(crtString);
         }
 
-        public async Task<string?> CreatCsr(CreateCsrReq req)
+        public async Task<bool> VerifyCrtSignedByTrused(X509Certificate crt)
         {
-            var tempKeyName = Guid.NewGuid().ToString();
-            var tempKeyFile = Path.Combine(caUtil.SavePath, $"{tempKeyName}.key");
-
-            await CreateTempFile(tempKeyFile, null, req.Key);
-
-            var tempCsrName = Guid.NewGuid().ToString();
-            var tempCsrFile = Path.Combine(caUtil.SavePath, $"{tempCsrName}.csr");
-
-            var result = await caUtil.CreateCsr(tempKeyName, tempCsrName, req.CommonName, req.OrganizationName);
-            if (!result)
+            var trustedCerts = Directory.GetFiles(TrustedCertsPath);
+            foreach (var trustedCert in trustedCerts)
             {
-                return null;
+                var cert = await BouncyCastleWrapper.LoadPemCertFromFile(trustedCert);
+                var checkResult = BouncyCastleWrapper.ValidateCert(crt, cert.GetPublicKey());
+                if (checkResult)
+                {
+                    return true;
+                }
             }
-            var key = await File.ReadAllTextAsync(tempCsrFile);
-            File.Delete(tempCsrFile);
-            File.Delete(tempKeyFile);
-
-            return key;
+            return false;
         }
 
-        private async Task CreateTempFile(string tempFile, IFormFile? csrFile, string? csr)
+        private void CheckAndCreateFolder()
         {
-            using (var oStream = System.IO.File.OpenWrite(tempFile))
+            if (!Directory.Exists(config.RootPath))
             {
-                if (csrFile != null)
-                {
-                    using (var csrData = csrFile.OpenReadStream())
-                    {
-                        await csrData.CopyToAsync(oStream);
-                    }
-                }
-
-                if (!string.IsNullOrEmpty(csr))
-                {
-                    var debug = csr.Length;
-                    using (var sWreiter = new StreamWriter(oStream))
-                    {
-                        await sWreiter.WriteAsync(csr);
-                    }
-                }
+                Directory.CreateDirectory(config.RootPath);
+            }
+            var caFolder = Path.Combine(config.RootPath, CaUtil.RcaDirectoryName);
+            if (!Directory.Exists(caFolder))
+            {
+                Directory.CreateDirectory(caFolder);
+            }
+            var tcFolder = Path.Combine(config.RootPath, CaUtil.TrustedDirectoryName);
+            if (!Directory.Exists(tcFolder))
+            {
+                Directory.CreateDirectory(tcFolder);
             }
         }
     }

+ 0 - 74
CertificateAutorityServer/Service/CertificateServiceFactory.cs

@@ -1,74 +0,0 @@
-using CAUtilLib;
-using CertificateAutorityServer.Model.Config;
-using CertificateAutorityServer.Service;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-
-namespace Microsoft.AspNetCore.Builder
-{
-    public static class CertificateServiceServiceProviderExtention
-    {
-        public static WebApplicationBuilder AddCertificateService(this WebApplicationBuilder webApplicationBuilder)
-        {
-            var serviceProvider = webApplicationBuilder.Services;
-            var option = webApplicationBuilder.Configuration;
-
-            serviceProvider.Configure<CerServiceConfig>(option.GetSection("CerConfig"));
-
-            serviceProvider.AddTransient<CaUtil>();
-            serviceProvider.AddTransient<CertificateService>();
-            serviceProvider.AddSingleton<CertificateServiceFactory>();
-
-            return webApplicationBuilder;
-        }
-    }
-}
-
-namespace CertificateAutorityServer.Service
-{
-    public class CertificateServiceFactory
-    {
-        private readonly IServiceProvider serviceProvider;
-        //private readonly string cerPath;
-        private readonly CerServiceConfig config;
-
-        public CertificateServiceFactory(IOptions<CerServiceConfig> config, IServiceProvider serviceProvider)
-        {
-            this.serviceProvider = serviceProvider;
-            this.config = config.Value;
-
-            CheckAndCreateFolder(this.config);
-
-            var certificateService = Create();
-            certificateService.CheckAndCreateRootCA(config.Value.RootPath).Wait();
-        }
-
-        public CertificateService Create()
-        {
-            var toReturn = serviceProvider.GetRequiredService<CertificateService>();
-            toReturn.RootPath = this.config.RootPath;
-            toReturn.RcaDirectoryName = this.config.RcaDirectoryName;
-            toReturn.TrustedDirectoryName = this.config.TrustedDirectoryName;
-            return toReturn;
-        }
-
-        private void CheckAndCreateFolder(CerServiceConfig config)
-        {
-            if (!Directory.Exists(config.RootPath))
-            {
-                Directory.CreateDirectory(config.RootPath);
-            }
-            var caFolder = Path.Combine(config.RootPath, config.RcaDirectoryName);
-            if (!Directory.Exists(caFolder))
-            {
-                Directory.CreateDirectory(caFolder);
-            }
-            var tcFolder = Path.Combine(config.RootPath, config.TrustedDirectoryName);
-            if (!Directory.Exists(tcFolder))
-            {
-                Directory.CreateDirectory(tcFolder);
-            }
-        }
-    }
-}