Creating an X509Certificate2 under IIS throws 'The system cannot find the file specified'
X509Certificate2 클래스를 사용하기 위해서 pfx 라는 인증서 파일경로를 지정해서 사용해야 합니다. 하지만 iis 환경에서 가동하면 "지정된 파일을 찾을 수 없습니다." 라는 메시지로 인해 오류 발생합니다.
var certificate = new X509Certificate2(_CertificatePfx, _CertificatePfxPassword,
X509KeyStorageFlags.DefaultKeySet);
특이하게 iisexpress 로 개발 환경에서 사용하면 문제없이 작동되는것을 보면 권한이 상당히
"{\"Message\":\"An error has occurred.\",\"ExceptionMessage\":\"지정된 파일을 찾을 수 없습니다.\\r\\n\",\"ExceptionType\":\"System.Security.Cryptography.CryptographicException\",\"StackTrace\":\" 위치: System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)\\r\\n 위치: System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)\\r\\n 위치: System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags)\\r\\n 위치: System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)\\r\\n 위치: UnivDefault.Web.Controllers.ApiCertificateController.Post(CertificateInfo info) 파일 D:\\\\UNIV Developer Center\\\\Development\\\\Internal\\\\WorkingHourSolution\\\\UnivDefault.Web\\\\Controllers\\\\ApiCertificateController.cs:줄 63\\r\\n 위치: lambda_method(Closure , Object , Object[] )\\r\\n 위치: System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_2.<GetExecutor>b__2(Object instance, Object[] methodParameters)\\r\\n 위치: System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\\r\\n--- 예외가 throw된 이전 위치의 스택 추적 끝 ---\\r\\n 위치: System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\\r\\n 위치: System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\\r\\n 위치: System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()\\r\\n--- 예외가 throw된 이전 위치의 스택 추적 끝 ---\\r\\n 위치: System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\\r\\n 위치: System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\\r\\n 위치: System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()\\r\\n--- 예외가 throw된 이전 위치의 스택 추적 끝 ---\\r\\n 위치: System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\\r\\n 위치: System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\\r\\n 위치: System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()\"}"
해결점은 iis 의 pool 고급설정에서 Process Model 속성 중에, Load User Profile 이라는 값이 기본적으로 false 되어 있습니다. 이를 true 로 지정하게 되면 제대로 오류 없이 작동합니다.
한국어 버전은 "사용자 프로필 로드" 라고 번역되어 있으며, 번역은 사용자 프로필을 로드 여부라는 간단한 내용만 언급되어 있어서 이 부분이 어떤 역할을 하는지 알 수는 없었습니다.
이유
X509Certificate2 클래스를 사용하여 PFX 파일을 로드할 때는 Windows의 사용자 프로필이 필요합니다. PFX 파일은 개인 키를 포함하며, 이 개인 키는 Windows의 사용자 프로필 내에 있는 특정 저장소에 액세스해야만 복호화될 수 있습니다.
IIS에서 "Load User Profile" 옵션이 false로 설정되어 있다면, IIS는 워커 프로세스를 시작할 때 사용자 프로필을 로드하지 않습니다. 따라서 해당 worker process 가 실행하는 ASP.NET MVC 애플리케이션은 PFX 파일의 개인 키에 접근하기 위한 필요한 정보를 찾지 못하게 됩니다. 이것이 X509Certificate2를 사용하여 PFX 파일을 로드하려고 할 때 오류가 발생하는 주된 이유입니다.
따라서, IIS에서 ASP.NET MVC 애플리케이션을 호스팅하고 있으며 해당 애플리케이션이 PFX 파일을 처리해야 하는 경우, "Load User Profile" 옵션을 true로 설정해야 합니다. 이렇게 하면 각 워커 프로세스가 고유한 Windows 사용자 프로필을 가질 수 있으며, 이를 통해 필요한 저장소에 접근하여 개인 키를 복호화할 수 있게 됩니다.