using Microsoft.AspNetCore.Mvc; using OpcUaManager.Models; using OpcUaManager.Services; namespace OpcUaManager.Controllers; [ApiController] [Route("api/cert")] [Produces("application/json")] public class CertController : ControllerBase { private readonly CertService _certService; private readonly ILogger _logger; public CertController(CertService certService, ILogger logger) { _certService = certService; _logger = logger; } /// /// X.509 클라이언트 인증서를 생성하고 pki/own/certs/ 에 PFX로 저장합니다. /// /// /// 원본 Program.cs 의 인증서 체계(pki/ 폴더, Exportable|MachineKeySet 플래그)를 유지합니다. /// /// Sample request: /// /// POST /api/cert/generate /// { /// "clientHostName": "dbsvr", /// "applicationName": "OpcTestClient", /// "serverHostName": "opc-server-01", /// "serverIp": "192.168.0.20", /// "pfxPassword": "", /// "validDays": 365 /// } /// [HttpPost("generate")] [ProducesResponseType(typeof(CertCreateResult), 200)] [ProducesResponseType(typeof(ApiError), 400)] public async Task Generate([FromBody] CertCreateRequest req) { if (string.IsNullOrWhiteSpace(req.ClientHostName) || string.IsNullOrWhiteSpace(req.ApplicationName)) return BadRequest(new ApiError { Error = "필수값 누락", Detail = "ClientHostName, ApplicationName 은 필수입니다." }); _logger.LogInformation("인증서 생성 요청: {Host}/{App}", req.ClientHostName, req.ApplicationName); var result = await _certService.GenerateAsync(req); if (!result.Success) return BadRequest(new ApiError { Error = "인증서 생성 실패", Detail = result.Message }); return Ok(result); } /// pki/own/certs/ 폴더의 PFX 파일 목록을 반환합니다. [HttpGet("list")] [ProducesResponseType(typeof(IEnumerable), 200)] public IActionResult List() => Ok(_certService.ListCertificates()); /// PFX 파일을 다운로드합니다. [HttpGet("download/{fileName}")] public IActionResult Download(string fileName) { // 경로 traversal 방지 if (fileName.Contains("..") || fileName.Contains('/') || fileName.Contains('\\')) return BadRequest(new ApiError { Error = "잘못된 파일 이름" }); string path = Path.Combine("pki", "own", "certs", fileName); if (!System.IO.File.Exists(path)) return NotFound(new ApiError { Error = "파일 없음", Detail = path }); byte[] bytes = System.IO.File.ReadAllBytes(path); return File(bytes, "application/x-pkcs12", fileName); } }