[성현모] KeficoMailService 추가, KMS 인증서 컨피그 추가

This commit is contained in:
SHM
2026-02-23 10:29:45 +09:00
parent 3f94a7b2b2
commit e60d499fa3
22 changed files with 445 additions and 19 deletions

View File

@ -7,6 +7,11 @@
"API": [
{
"ApiName": "kms",
"CertificateVerify": false,
"CertPemPath": "D:\\00_Download\\[MDS인텔리전스] 시스템액스 인증서_2026027\\SystemX-SHM_cert.pem",
"CertKeyPath": "D:\\00_Download\\[MDS인텔리전스] 시스템액스 인증서_2026027\\SystemX-SHM_cert.key",
//"CertPemPath": "D:\\00_Download\\[MDS인텔리전스] 시스템액스 인증서_20260129\\SystemX-SHM_cert.pem",
//"CertKeyPath": "D:\\00_Download\\[MDS인텔리전스] 시스템액스 인증서_20260129\\SystemX-SHM_cert.key",
"Functions": [
{
"Name": "EcuID_SupplierEcuID",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KeficoMailService
{
public class Address
{
public string AddressMail { get; set; } = string.Empty;
public string AddressName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KeficoMailService
{
public class HtmlFormType
{
public string FormType { get; set; } = string.Empty;
public string SystemName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{63FF04EE-D1A5-4406-9267-1EE1E9FBB9E7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>KeficoMailService</RootNamespace>
<AssemblyName>KeficoMailService</AssemblyName>
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Address.cs" />
<Compile Include="HtmlFormType.cs" />
<Compile Include="MailType.cs" />
<Compile Include="Manager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KeficoMailService
{
public enum MailType
{
Text,
Html
}
}

View File

@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace KeficoMailService
{
public class Manager
{
public string Host { get; set; } = "https://service.kefico.co.kr";
public readonly string GetFormHTML = "/KEFICO.XML/MAIL/MailManager.asmx";
public readonly string SendMailDetailHTML = "/KEFICO.XML/MAIL/MailManager.asmx";
public string GetFormHtml(HtmlFormType formType)
{
string result = string.Empty;
string url = $"{Host}{GetFormHTML}";
string soapEnvelope = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<soap12:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""http://www.w3.org/2001/XMLSchema""
xmlns:soap12=""http://www.w3.org/2003/05/soap-envelope"">
<soap12:Body>
<GetFormHTML xmlns=""http://kefico.co.kr/"">
<Type>
<SystemName>{formType.SystemName}</SystemName>
<FormType>{formType.FormType}</FormType>
</Type>
</GetFormHTML>
</soap12:Body>
</soap12:Envelope>";
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"Request URL: {url}");
Console.WriteLine($"Request soap: {soapEnvelope}");
byte[] data = Encoding.UTF8.GetBytes(soapEnvelope);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/soap+xml; charset=utf-8";
request.ContentLength = data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
using (WebResponse response = request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
//decode
XDocument doc = XDocument.Parse(result);
XNamespace ns = "http://kefico.co.kr/";
string htmlEncoded =
doc.Descendants(ns + "GetFormHTMLResult").First().Value;
// HTML Decode
result = WebUtility.HtmlDecode(htmlEncoded);
result = WebUtility.HtmlDecode(htmlEncoded);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("");
Console.WriteLine("Response");
Console.WriteLine(result);
}
return result;
}
public string SendMailDetail(Address from, List<Address> to, List<Address> cc, List<Address> bcc, string subject, string body, MailType type)
{
string result = string.Empty;
string url = $"{Host}{SendMailDetailHTML}";
string soapEnvelope = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<soap12:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap12=""http://www.w3.org/2003/05/soap-envelope"">
<soap12:Body>
<SendMailDetail xmlns=""http://kefico.co.kr/"">
<From>
<AddressMail>{from.AddressMail}</AddressMail>
<AddressName>{from.AddressName}</AddressName>
</From>
<To>{string.Join("",to.Select(x=> $@"
<Address>
<AddressMail>{x.AddressMail}</AddressMail>
<AddressName>{x.AddressName}</AddressName>
</Address>"))}
</To>
<CC>{string.Join("",cc.Select(x => $@"
<Address>
<AddressMail>{x.AddressMail}</AddressMail>
<AddressName>{x.AddressName}</AddressName>
</Address>"))}
</CC>
<Bcc>{string.Join("", bcc.Select(x => $@"
<Address>
<AddressMail>{x.AddressMail}</AddressMail>
<AddressName>{x.AddressName}</AddressName>
</Address>"))}
</Bcc>
<Title>{subject}</Title>
<Body>{body}</Body>
<Type>{type.ToString()}</Type>
</SendMailDetail>
</soap12:Body>
</soap12:Envelope>";
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("");
Console.WriteLine($"Request URL: {url}");
Console.WriteLine($"Request soap: {soapEnvelope}");
byte[] data = Encoding.UTF8.GetBytes(soapEnvelope);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/soap+xml; charset=utf-8";
request.ContentLength = data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
using (WebResponse response = request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
//decode
XDocument doc = XDocument.Parse(result);
XNamespace ns = "http://kefico.co.kr/";
string htmlEncoded =
doc.Descendants(ns + "SendMailDetailResponse").First().Value;
//decode
result = WebUtility.HtmlDecode(htmlEncoded);
result = WebUtility.HtmlDecode(result);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("");
Console.WriteLine("Response");
Console.WriteLine(result);
}
return result;
}
}
}

View File

@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
// 이러한 특성 값을 변경하세요.
[assembly: AssemblyTitle("KeficoMailService")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("KeficoMailService")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
[assembly: ComVisible(false)]
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
[assembly: Guid("63ff04ee-d1a5-4406-9267-1ee1e9fbb9e7")]
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
//
// 주 버전
// 부 버전
// 빌드 번호
// 수정 버전
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CIAMaster", "CIAMaster\CIAM
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KmsProxy", "KmsProxy\KmsProxy.csproj", "{73824ACB-4FB9-4E11-9A86-E05471B3C979}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeficoMailService", "KeficoMailService\KeficoMailService.csproj", "{63FF04EE-D1A5-4406-9267-1EE1E9FBB9E7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -33,6 +35,10 @@ Global
{73824ACB-4FB9-4E11-9A86-E05471B3C979}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73824ACB-4FB9-4E11-9A86-E05471B3C979}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73824ACB-4FB9-4E11-9A86-E05471B3C979}.Release|Any CPU.Build.0 = Release|Any CPU
{63FF04EE-D1A5-4406-9267-1EE1E9FBB9E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63FF04EE-D1A5-4406-9267-1EE1E9FBB9E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63FF04EE-D1A5-4406-9267-1EE1E9FBB9E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63FF04EE-D1A5-4406-9267-1EE1E9FBB9E7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -58,6 +58,10 @@
<Project>{0bdb3e8c-2f57-4780-8b6f-3ae3f426a6c3}</Project>
<Name>CPMeta</Name>
</ProjectReference>
<ProjectReference Include="..\KeficoMailService\KeficoMailService.csproj">
<Project>{63ff04ee-d1a5-4406-9267-1ee1e9fbb9e7}</Project>
<Name>KeficoMailService</Name>
</ProjectReference>
<ProjectReference Include="..\KmsProxy\KmsProxy.csproj">
<Project>{73824acb-4fb9-4e11-9a86-e05471b3c979}</Project>
<Name>KmsProxy</Name>

View File

@ -1,6 +1,8 @@
using CPMeta.Models;
using KeficoMailService;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace PlayGround.NetFramework
@ -9,28 +11,52 @@ namespace PlayGround.NetFramework
{
static void Main(string[] args)
{
//global set
string host = "192.168.0.42";
//html Send
Manager manager = new Manager();
//random value
string ProductId = "00010032-87a4-45ca-b627-b975d41e35df";
// string Mac1 = Guid.NewGuid().ToString();
// string Mac2 = Guid.NewGuid().ToString();
Address from = new Address { AddressMail = "TraNotify@kefico.co.kr", AddressName = "" };
List<Address> to = new List<Address> { new Address { AddressMail = "systemx.kjh@systemx.co.kr", AddressName = "" } };
List<Address> cc = new List<Address> { new Address { AddressMail = "systemx.shm@systemx.co.kr", AddressName = "" } };
List<Address> bcc = new List<Address> { };
string subject = "TRA 테스트용 메일";
string textSend = manager.SendMailDetail(from, to, cc, bcc, subject, "Tra Notify", MailType.Text);
//Get
Task.Run(async () =>
{
var res2 = await CPMeta.CPMeta.HealthCheck(host);
// var res2 = await CPMeta.CPMeta.GetWbmsMetaByProductId(host, ProductId);
// string form = manager.GetFormHtml(new HtmlFormType { FormType="", SystemName="" });
Console.ForegroundColor = ConsoleColor.DarkBlue;
Console.WriteLine($"Response: {res2} (Trace Guid:)");
Console.ForegroundColor = ConsoleColor.White;
//form = form.Replace("@HEADTITLE@", "헤드타이틀");
//form = form.Replace("@TITLE@", "타이틀");
//form = form.Replace("@BODY@", "내용");
//form = form.Replace("@URL@", "링크");
// string htmlSend = manager.SendMailDetail(from, to, cc, bcc, subject, form, MailType.Html);
// var res3 = await CPMeta.CPMeta.GetWbmsMetaByMacAddress(host, Mac1);
// var res4 = await CPMeta.CPMeta.GetWbmsMetaByMacAddress(host, Mac2);
}).Wait();
//Text Send
// //global set
// string host = "192.168.0.42";
// //random value
// string ProductId = "00010032-87a4-45ca-b627-b975d41e35df";
// // string Mac1 = Guid.NewGuid().ToString();
//// string Mac2 = Guid.NewGuid().ToString();
// //Get
// Task.Run(async () =>
// {
// var res2 = await CPMeta.CPMeta.HealthCheck(host);
// // var res2 = await CPMeta.CPMeta.GetWbmsMetaByProductId(host, ProductId);
// Console.ForegroundColor = ConsoleColor.DarkBlue;
// Console.WriteLine($"Response: {res2} (Trace Guid:)");
// Console.ForegroundColor = ConsoleColor.White;
// // var res3 = await CPMeta.CPMeta.GetWbmsMetaByMacAddress(host, Mac1);
// // var res4 = await CPMeta.CPMeta.GetWbmsMetaByMacAddress(host, Mac2);
// }).Wait();
return;

View File

@ -139,7 +139,7 @@ namespace SystemX.Core.Services
return response;
}
protected HttpClientHandler GetClientHandler()
protected virtual HttpClientHandler GetClientHandler()
{
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
@ -150,7 +150,7 @@ namespace SystemX.Core.Services
return clientHandler;
}
protected short SetTimeout(short timeOutSeconds)
protected virtual short SetTimeout(short timeOutSeconds)
{
short timeoutMin = 5;
short timeoutMax = 30;

View File

@ -12,6 +12,10 @@ namespace WebApi.Project.ProxyKMS.Models
public string ApiName { get; set; } = string.Empty;
public List<Function> Functions { get; set; } = new List<Function>();
public bool CertificateVerify { get; set; } = false;
public string CertPemPath { get; set; } = string.Empty;
public string CertKeyPath { get; set; } = string.Empty;
}
public class Function

View File

@ -1,5 +1,7 @@
using System;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
using SystemX.Core.DB;
using SystemX.Core.Services;
using WebApi.Library.Enums;
@ -51,5 +53,95 @@ namespace WebApi.Project.ProxyKMS.Services
return response;
}
public override async Task<RESPONSE?> PostJsonAsync<REQUEST, RESPONSE>(string url, REQUEST request, short timeOutSeconds = 5) where REQUEST : class where RESPONSE : class
{
RESPONSE response = null;
Guid guid = Guid.NewGuid();
using (HttpClient httpClient = new HttpClient(GetClientHandler()))
{
try
{
short timeOutSec = SetTimeout(timeOutSeconds);
httpClient.Timeout = new TimeSpan(0, 0, timeOutSec);
httpClient.BaseAddress = new Uri(url ?? "");
LogXnet.WriteLine($"[POST] Request({guid})::{url}{Environment.NewLine}{request?.ToJson()}", LogXLabel.HTTP);
DateTime requestTime = DateTime.Now;
response = await (await httpClient.PostAsJsonAsync(url, request)).Content.ReadFromJsonAsync<RESPONSE>();
LogXnet.WriteLine($"[POST] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}{Environment.NewLine}{response?.ToJson()}", LogXLabel.HTTP);
}
catch (Exception e)
{
LogXnet.WriteLine(e);
LogXnet.WriteLine(e?.InnerException?.InnerException?.Message, LogXLabel.Exception);
}
}
return response;
}
public virtual async Task<RESPONSE?> PutJsonAsync<REQUEST, RESPONSE>(string url, REQUEST request, short timeOutSeconds = 5) where REQUEST : class where RESPONSE : class
{
RESPONSE? response = default(RESPONSE);
Guid guid = Guid.NewGuid();
using (HttpClient httpClient = new HttpClient(GetClientHandler()))
{
try
{
var timeOutSec = SetTimeout(timeOutSeconds);
httpClient.Timeout = new TimeSpan(0, 0, timeOutSec);
httpClient.BaseAddress = new Uri($"{url}");
LogXnet.WriteLine($"[PUT] Request({guid})::{url}{Environment.NewLine}{request?.ToJson()}", LogXLabel.HTTP);
DateTime requestTime = DateTime.Now;
var res = await httpClient.PutAsJsonAsync(url, request);
response = await res.Content.ReadFromJsonAsync<RESPONSE>();
LogXnet.WriteLine($"[PUT] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}{Environment.NewLine}{response?.ToJson()}", LogXLabel.HTTP);
}
catch (Exception e)
{
LogXnet.WriteLine(e);
LogXnet.WriteLine(e?.InnerException?.InnerException?.Message, LogXLabel.Exception);
}
}
return response;
}
protected override HttpClientHandler GetClientHandler()
{
HttpClientHandler clientHandler = new HttpClientHandler();
//인증서가 경로에 있으면
if (File.Exists(KmsApi.CertPemPath) == true && File.Exists(KmsApi.CertKeyPath) == true)
{
Console.WriteLine($"Cert.Pem Path:{KmsApi.CertPemPath}", LogXLabel.Debug);
Console.WriteLine($"Cert.Key Path:{KmsApi.CertKeyPath}", LogXLabel.Debug);
var cert = X509Certificate2.CreateFromPemFile(KmsApi.CertPemPath, KmsApi.CertKeyPath);
cert = new X509Certificate2(cert.Export(X509ContentType.Pkcs12));
clientHandler.ClientCertificates.Add(cert);
}
else
{
Console.WriteLine($"File not exist. Cert.Pem Path:{KmsApi.CertPemPath}", LogXLabel.Warning);
Console.WriteLine($"File not exist. Cert.Key Path:{KmsApi.CertKeyPath}", LogXLabel.Warning);
}
//ssl 인증서 무시
Console.WriteLine($"CertificateVerify:{KmsApi.CertificateVerify}", LogXLabel.Debug);
if (KmsApi.CertificateVerify == false)
{
clientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
return true;
};
}
return clientHandler;
}
}
}