C# - 인증서를 윈도우에 설치하는 방법
요즘 SSL/TLS가 대두되면서 인증서를 꽤나 많이 사용하게 됩니다. 문제는 이게 일반 윈도우 사용자 입장에서는 은근히 복잡하다는 것인데요.
우선 대표적으로 인증서는 2군데 저장소가 있습니다. 하나는 현재 로그인 한 사용자의 저장소이고, 하나는 컴퓨터 전역적으로 접근할 수 있는 저장소입니다. 전자의 경우로 설치한 인증서를 보고 싶다면 명령행에 "certmgr"이라고 입력하고 후자의 인증서를 보고 싶다면 "certlm"이라고 입력하면 됩니다. 그럼 각각의 저장소에 대한 인증서 관리자가 실행되어 개별 영역에 인증서를 추가/삭제할 수 있습니다.
그런데 여기서 또다시 문제가 있습니다. 저장소마다 다른 세부 저장소가 있다는 점입니다.
- Personal
- Trusted Root Certification Authorities
- Intermediate Certification Authorities
- Trusted Publishers
- Untrusted Certificates
- Third-Party Root Certification Authorities
- Trusted People
- Remote Desktop
- Smart Card Trusted Roots
- ...기타 등등...
대개의 경우, 테스트 인증서와 같은 자신이 직접 만든 것들은 "Trusted Root Certification Authorities"에 설치합니다. 그 외의 경우에는 응용 프로그램이 요구하는 위치에 인증서가 등록되어야 하는데 가령 예전에 만든 Outlook 확장의 경우,
아웃룩 사용자를 위한 중국어 스팸 필터 Add-in
; https://www.sysnet.pe.kr/2/0/11306
(certlm으로 관리되는) "Local Computer" 영역의 저장소 중 "Trusted Root Certification Authorities"와 "Trusted Publishers"에 저장되는 것을 요구합니다. (물론, 정식 인증 기관으로부터 발급받은 경우라면 설치가 필요 없습니다.)
그런데, 저런 절차를 사용자에게 맡길 수는 없습니다. 따라서 프로그램으로 처리를 해야 하는데, 명령행에서 이를 수행할 수 있는 certmgr.exe가 있지만,
CertMgr
; https://learn.microsoft.com/en-us/windows/desktop/seccrypto/certmgr
아쉽게도 Windows SDK에 포함되어 있습니다. 이 경우 역시, 사용자에게 Windows SDK를 설치해야 한다고 가이드 할 수는 없습니다. 어쩔 수 없습니다. 이런 경우에는 코딩을 할 수밖에. ^^
다행히, .NET 2.0부터 제공하던 X509Store 타입을 이용하면 이 문제를 쉽게 해결할 수 있습니다.
X509Store Class
; https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store
How to programmatically install a certificate using C#
; https://stackoverflow.com/questions/12337721/how-to-programmatically-install-a-certificate-using-c-sharp
예를 들어, "
아웃룩 사용자를 위한 중국어 스팸 필터 Add-in" 글에서 사용한 인증서를 설치하고 싶다면 다음과 같이 코딩하면 됩니다.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace CertMgr2
{
class Program
{
static void Main(string[] args)
{
string certUrl = "http://sysnet.blob.core.windows.net/temp/stjeong.cer";
string certFilePath = null;
try
{
certFilePath = DownloadCertifcate(certUrl);
InstallCertificate(StoreLocation.LocalMachine, StoreName.Root, certFilePath);
InstallCertificate(StoreLocation.LocalMachine, StoreName.TrustedPublisher, certFilePath);
Console.WriteLine("Installed: " + certUrl);
}
finally
{
if (File.Exists(certFilePath) == true)
{
File.Decrypt(certFilePath);
}
}
}
private static string DownloadCertifcate(string certUrl)
{
string tempFilePath = Path.GetTempFileName();
WebClient wc = new WebClient();
wc.DownloadFile(certUrl, tempFilePath);
return tempFilePath;
}
private static void InstallCertificate(StoreLocation storeLocation, StoreName storeName, string certFilePath)
{
X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadWrite);
store.Add(new X509Certificate2(X509Certificate2.CreateFromCertFile(certFilePath)));
store.Close();
}
}
}
그리고 StoreLocation.LocalMachine에 인증서를 설치하기 때문에
관리자 권한을 요구하므로 manifest 파일을 다음과 같이 구성합니다.
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
(이 글의 소스 코드는
https://github.com/stjeong/CharacterRangeFilter에 올렸습니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]