인증서 생성기 화면 및 관련 작업

This commit is contained in:
2026-02-23 00:07:22 +09:00
parent 4e006a5a5f
commit 4ea351946a
53 changed files with 1437 additions and 656 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
namespace OpcPks.Core.Models
{
public class CertRequestModel
{
public string ApplicationName { get; set; } = "OpcPksClient";
public bool IsRedundant { get; set; } = false;
// 서버 호스트네임 (예: HONPKS)
public string PrimaryHostName { get; set; } = "";
public string SecondaryHostName { get; set; } = ""; // Redundant일 때 필수
// FTE 네트워크 구조 반영 (네트워크 대역 분리)
public string PrimaryIpA { get; set; } = ""; // 192.168.0.x 대역
public string PrimaryIpB { get; set; } = ""; // 192.168.1.x 대역 (FTE 이중화 시)
public string SecondaryIpA { get; set; } = ""; // 192.168.0.y 대역
public string SecondaryIpB { get; set; } = ""; // 192.168.1.y 대역
public int ValidityYears { get; set; } = 5;
}
}

View File

@@ -0,0 +1,121 @@
using Opc.Ua;
using System.Security.Cryptography.X509Certificates;
using OpcPks.Core.Models;
namespace OpcPks.Core.Services
{
public class CertificateGenerator
{
private readonly string _pfxPath;
private readonly string _ownCertPath;
public CertificateGenerator(string baseDataPath)
{
// 경로 설정 (현재 tree 구조 반영)
_pfxPath = Path.Combine(baseDataPath, "pki/own/private/OpcTestClient.pfx");
_ownCertPath = Path.Combine(baseDataPath, "pki/own/certs/OpcTestClient.der");
}
public async Task<bool> CreateHoneywellCertificateAsync(CertRequestModel model)
{
try
{
// 1. [중요] 기존 인증서 백업
BackupExistingCertificate();
Console.WriteLine("🔐 하니웰 맞춤형 인증서 생성을 시작합니다...");
// SAN 리스트 구축 (localhost 기본 포함)
var subjectAlternativeNames = new List<string> { "localhost", "127.0.0.1" };
// Primary 정보 (FTE Yellow/Green)
if (!string.IsNullOrEmpty(model.PrimaryHostName))
{
subjectAlternativeNames.Add(model.PrimaryHostName);
subjectAlternativeNames.Add($"{model.PrimaryHostName}A");
}
if (!string.IsNullOrEmpty(model.PrimaryIpA)) subjectAlternativeNames.Add(model.PrimaryIpA);
if (!string.IsNullOrEmpty(model.PrimaryIpB)) subjectAlternativeNames.Add(model.PrimaryIpB);
// Secondary 정보 (Redundant 선택 시)
if (model.IsRedundant)
{
if (!string.IsNullOrEmpty(model.SecondaryHostName))
{
subjectAlternativeNames.Add(model.SecondaryHostName);
subjectAlternativeNames.Add($"{model.SecondaryHostName}B");
}
if (!string.IsNullOrEmpty(model.SecondaryIpA)) subjectAlternativeNames.Add(model.SecondaryIpA);
if (!string.IsNullOrEmpty(model.SecondaryIpB)) subjectAlternativeNames.Add(model.SecondaryIpB);
}
// 2. 인증서 생성 (KeySize 2048, 하니웰 규격)
// var certificate = CertificateFactory.CreateCertificate(
// model.ApplicationName,
// model.ApplicationName,
// null,
// subjectAlternativeNames,
// null,
// 2048,
// DateTime.UtcNow.AddDays(-1),
// model.ValidityYears * 12,
// null
// );
// 2. 인증서 생성 (로그에 표시된 15개 인자 서명 순서 엄격 준수)
ushort keySize = 2048;
ushort lifetimeInMonths = (ushort)(model.ValidityYears * 12);
var certificate = CertificateFactory.CreateCertificate(
"Directory", // 1. storeType (string)
Path.GetDirectoryName(_pfxPath), // 2. storePath (string)
null, // 3. password (string)
$"urn:localhost:{model.ApplicationName}", // 4. applicationUri (string)
model.ApplicationName, // 5. applicationName (string)
model.ApplicationName, // 6. subjectName (string)
subjectAlternativeNames, // 7. domainNames (IList<string>)
keySize, // 8. keySize (ushort)
DateTime.UtcNow.AddDays(-1), // 9. startTime (DateTime)
lifetimeInMonths, // 10. lifetimeInMonths (ushort)
(ushort)256, // 11. hashSizeInBits (ushort)
false, // 12. isCA (bool)
null, // 13. issuer (X509Certificate2)
null, // 14. privateKey (byte[])
0 // 15. hashAlgorithm (int)
);
// 3. 파일 저장
Directory.CreateDirectory(Path.GetDirectoryName(_pfxPath)!);
Directory.CreateDirectory(Path.GetDirectoryName(_ownCertPath)!);
// PFX (개인키 포함) 저장
byte[] pfxBytes = certificate.Export(X509ContentType.Pfx, "");
await File.WriteAllBytesAsync(_pfxPath, pfxBytes);
// DER (공개키만) 저장 - 하니웰 서버에 수동으로 신뢰 등록할 때 필요할 수 있음
byte[] derBytes = certificate.Export(X509ContentType.Cert);
await File.WriteAllBytesAsync(_ownCertPath, derBytes);
Console.WriteLine($"✅ 새 인증서 생성 완료 및 백업 성공.");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"❌ 인증서 생성 실패: {ex.Message}");
return false;
}
}
private void BackupExistingCertificate()
{
if (File.Exists(_pfxPath))
{
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string backupPath = _pfxPath + "." + timestamp + ".bak";
File.Copy(_pfxPath, backupPath, true);
Console.WriteLine($"📦 기존 인증서 백업됨: {Path.GetFileName(backupPath)}");
}
}
}
}

View File

@@ -13,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("OpcPks.Core")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+171aaf6115cde8e9e930d14886fafa5fe4c1e4c0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4e006a5a5f65f597fafaa3777b8a1073944eada2")]
[assembly: System.Reflection.AssemblyProductAttribute("OpcPks.Core")]
[assembly: System.Reflection.AssemblyTitleAttribute("OpcPks.Core")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class.
// MSBuild WriteCodeFragment 클래스에서 생성되었습니다.

View File

@@ -1 +1 @@
fb717191c02dff3f083190ed14d847598609d6461f7931778b4e474636da4508
c75e210b6893d92e0b1636a6b8f564c5a308ff97701a72ccf681ea4473190030

View File

@@ -1 +1 @@
2304a9d0dd6384b5068ce141d8c63b025727e909890abc7b08e5f2fff4bf5261
cea4cbfb0b4d3fd205bf727dfb75d0c31872eee74f74e79b0a11980153badb1b

View File

@@ -10,11 +10,11 @@
"projectUniqueName": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/OpcPks.Core.csproj",
"projectName": "OpcPks.Core",
"projectPath": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/OpcPks.Core.csproj",
"packagesPath": "/home/pacer/.nuget/packages/",
"packagesPath": "/root/.nuget/packages/",
"outputPath": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/pacer/.nuget/NuGet/NuGet.Config"
"/root/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"

View File

@@ -4,12 +4,12 @@
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/home/pacer/.nuget/packages/</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/home/pacer/.nuget/packages/</NuGetPackageFolders>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/root/.nuget/packages/</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/root/.nuget/packages/</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.8.1</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="/home/pacer/.nuget/packages/" />
<SourceRoot Include="/root/.nuget/packages/" />
</ItemGroup>
</Project>

View File

@@ -555,7 +555,7 @@
]
},
"packageFolders": {
"/home/pacer/.nuget/packages/": {}
"/root/.nuget/packages/": {}
},
"project": {
"version": "1.0.0",
@@ -563,11 +563,11 @@
"projectUniqueName": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/OpcPks.Core.csproj",
"projectName": "OpcPks.Core",
"projectPath": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/OpcPks.Core.csproj",
"packagesPath": "/home/pacer/.nuget/packages/",
"packagesPath": "/root/.nuget/packages/",
"outputPath": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/pacer/.nuget/NuGet/NuGet.Config"
"/root/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"

View File

@@ -1,19 +1,19 @@
{
"version": 2,
"dgSpecHash": "4hhhTNv5rdj9W8LEgtqRzvNnobbH+nWA4apshlgfd3j4OdsEpRlMXC2Q3X2ScEegniOPFgZFJJ9l+i9qtr7LrA==",
"dgSpecHash": "Yr08mh1oXg2H9eup6pJDv3DAEJIeIlBI+QyHLkFeXakigkBdjFhrDkS0UWc3z6IMWBK5yeeRM5RBBASJtTIrbA==",
"success": true,
"projectFilePath": "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/OpcPks.Core.csproj",
"expectedPackageFiles": [
"/home/pacer/.nuget/packages/microsoft.extensions.dependencyinjection.abstractions/8.0.1/microsoft.extensions.dependencyinjection.abstractions.8.0.1.nupkg.sha512",
"/home/pacer/.nuget/packages/microsoft.extensions.logging.abstractions/8.0.1/microsoft.extensions.logging.abstractions.8.0.1.nupkg.sha512",
"/home/pacer/.nuget/packages/newtonsoft.json/13.0.3/newtonsoft.json.13.0.3.nupkg.sha512",
"/home/pacer/.nuget/packages/npgsql/8.0.4/npgsql.8.0.4.nupkg.sha512",
"/home/pacer/.nuget/packages/opcfoundation.netstandard.opc.ua.client/1.5.374.78/opcfoundation.netstandard.opc.ua.client.1.5.374.78.nupkg.sha512",
"/home/pacer/.nuget/packages/opcfoundation.netstandard.opc.ua.configuration/1.5.374.78/opcfoundation.netstandard.opc.ua.configuration.1.5.374.78.nupkg.sha512",
"/home/pacer/.nuget/packages/opcfoundation.netstandard.opc.ua.core/1.5.374.78/opcfoundation.netstandard.opc.ua.core.1.5.374.78.nupkg.sha512",
"/home/pacer/.nuget/packages/opcfoundation.netstandard.opc.ua.security.certificates/1.5.374.78/opcfoundation.netstandard.opc.ua.security.certificates.1.5.374.78.nupkg.sha512",
"/home/pacer/.nuget/packages/system.formats.asn1/8.0.1/system.formats.asn1.8.0.1.nupkg.sha512",
"/home/pacer/.nuget/packages/system.security.cryptography.cng/5.0.0/system.security.cryptography.cng.5.0.0.nupkg.sha512"
"/root/.nuget/packages/microsoft.extensions.dependencyinjection.abstractions/8.0.1/microsoft.extensions.dependencyinjection.abstractions.8.0.1.nupkg.sha512",
"/root/.nuget/packages/microsoft.extensions.logging.abstractions/8.0.1/microsoft.extensions.logging.abstractions.8.0.1.nupkg.sha512",
"/root/.nuget/packages/newtonsoft.json/13.0.3/newtonsoft.json.13.0.3.nupkg.sha512",
"/root/.nuget/packages/npgsql/8.0.4/npgsql.8.0.4.nupkg.sha512",
"/root/.nuget/packages/opcfoundation.netstandard.opc.ua.client/1.5.374.78/opcfoundation.netstandard.opc.ua.client.1.5.374.78.nupkg.sha512",
"/root/.nuget/packages/opcfoundation.netstandard.opc.ua.configuration/1.5.374.78/opcfoundation.netstandard.opc.ua.configuration.1.5.374.78.nupkg.sha512",
"/root/.nuget/packages/opcfoundation.netstandard.opc.ua.core/1.5.374.78/opcfoundation.netstandard.opc.ua.core.1.5.374.78.nupkg.sha512",
"/root/.nuget/packages/opcfoundation.netstandard.opc.ua.security.certificates/1.5.374.78/opcfoundation.netstandard.opc.ua.security.certificates.1.5.374.78.nupkg.sha512",
"/root/.nuget/packages/system.formats.asn1/8.0.1/system.formats.asn1.8.0.1.nupkg.sha512",
"/root/.nuget/packages/system.security.cryptography.cng/5.0.0/system.security.cryptography.cng.5.0.0.nupkg.sha512"
],
"logs": []
}