|
@@ -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();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
-}
|
|
|
+}
|