Windows 인증서 저장소 실무 가이드 ── 사용자와 컴퓨터, 어느 쪽에 넣어야 하는가

· · 인증서, Windows, 보안, PKI, TLS, PowerShell, 업무 앱, 정보 시스템

“온라인자격확인 단말 갱신 때 클라이언트 인증서를 새 PC에 다시 넣었더니 연결할 수 없게 되었다”, “개발 머신에서는 은행 API에 연결되는데 Windows 서비스로 만들었더니 ‘인증서를 찾을 수 없습니다’라고 나온다”, “애초에 certmgr.msc에서 보이는 인증서와 certlm.msc에서 보이는 인증서 중 어느 쪽이 진짜인지 모르겠다” ── 클라이언트 인증서를 사용하는 Web API 연동을 수탁으로 개발하다 보면, 이런 종류의 상담이 정기적으로 들어옵니다.

의료기관의 온라인자격확인, 전자 신청, 은행계 API, 거래처와의 EDI. 예전에는 대기업 인프라 담당자만 다루던 클라이언트 인증서를, 지금은 중소기업의 정보 시스템 담당자나 업무 앱 개발자가 다루는 시대입니다. 그리고 인증서 관련 사고는 사실 몇 가지 패턴으로 압축됩니다. 넣을 위치를 잘못 고르는 것, 비밀 키 권한을 잊는 것, 만료를 잊는 것── 이 세 가지입니다.

이 글에서는 클라이언트 인증서를 사용하는 업무 앱 개발자와, 인증서 교체 작업을 맡게 되는 정보 시스템 담당자를 대상으로, ‘사용자 저장소와 컴퓨터 저장소 중 어느 쪽에 넣을 것인가’라는 판단을 축으로 삼아, Windows 인증서 저장소의 구조부터 비밀 키 권한 부여, PowerShell을 이용한 만료 점검, .NET에서의 활용 코드까지 한번에 정리합니다. 내용은 2026년 8월 시점 Microsoft Learn의 1차 정보에 기반합니다.

1. 먼저 결론

  • Windows의 인증서 저장소는 ‘사용자(CurrentUser)’와 ‘컴퓨터(LocalMachine)’ 두 계통입니다. 사용자 저장소는 계정마다 별개(레지스트리의 HKEY_CURRENT_USER 아래), 컴퓨터 저장소는 PC 전체에서 공통(HKEY_LOCAL_MACHINE 아래)입니다.12
  • 관리 도구도 두 가지입니다. certmgr.msc는 현재 사용자, certlm.msc는 로컬 컴퓨터의 저장소를 엽니다. PowerShell에서는 Cert:\CurrentUserCert:\LocalMachine입니다.34
  • 어느 쪽에 넣을지는 ‘그 인증서를 사용하는 프로그램이 누구로서 동작하는가’로 정합니다. 대화형 사용자 앱이라면 사용자 저장소, Windows 서비스・IIS・작업 스케줄러의 무인 실행이라면 컴퓨터 저장소가 원칙입니다(3장의 판단표).
  • ‘개발 중에는 동작했는데 서비스화하니 찾을 수 없다’의 원인은 거의 하나입니다. 개발자가 자신의 사용자 저장소에 넣은 인증서는, 다른 계정으로 동작하는 서비스의 CurrentUser에서는 보이지 않습니다(3장).
  • 인증서와 비밀 키는 별개입니다. 컴퓨터 저장소에 넣은 것만으로는 서비스 계정이 비밀 키를 읽을 수 없는 것이 보통입니다. certlm.msc의 ‘비밀 키 관리’에서 실행 계정에 읽기 권한을 부여합니다.5
  • pfx를 임포트할 때 비밀 키는 기본적으로 내보내기 불가 상태가 됩니다. Import-PfxCertificate-Exportable을 지정하지 않는 한 비밀 키를 다시 내보낼 수 없는 형태로 가져옵니다. 이것은 사고가 아니라 바람직한 기본값입니다.6
  • 만료는 점검 자동화로 방지합니다. Get-ChildItem Cert:\LocalMachine\My -ExpiringInDays 60처럼, 지정한 일수 이내에 만료되는 인증서를 기계적으로 추출할 수 있습니다.4
  • 지문(섬프린트)을 코드나 구성에 하드코딩하면, 인증서를 갱신할 때마다 죽습니다. 새 인증서는 지문이 반드시 바뀌기 때문입니다. 구성 외부화 + 신구 병행 기간이 설계의 기본입니다(5장・7장).

2. 인증서 저장소의 전체 구조 ── 두 개의 위치와 논리 저장소

2.1. 사용자와 컴퓨터, 두 가지 계통

Windows의 인증서 저장소는 크게 두 개의 ‘위치’로 나뉩니다.1

  • 컴퓨터(로컬 컴퓨터, LocalMachine)의 인증서 저장소: 해당 PC에 하나이며, PC상의 모든 사용자와 서비스에 공통입니다. 실체는 레지스트리의 HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates 아래에 있습니다.2
  • 사용자(현재 사용자, CurrentUser)의 인증서 저장소: 사용자 계정마다 별개입니다. 실체는 HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates 아래, 즉 사용자 프로필의 일부입니다.2

이 외에 서비스 계정 단위의 저장소도 있으며3, 실체는 서비스 이름별 레지스트리 키입니다.2 실무에서 우선 파악해야 할 것은 앞의 두 가지입니다.

중요한 사양이 하나 있습니다. 사용자 저장소의 각 논리 저장소는, ‘개인’을 제외하고, 컴퓨터 저장소의 같은 이름 저장소 내용을 상속해서 보여줍니다.1 예를 들어 컴퓨터 저장소의 ‘신뢰할 수 있는 루트 인증 기관’에 사내 CA의 인증서를 넣으면, 모든 사용자의 ‘신뢰할 수 있는 루트 인증 기관’에도 그 인증서가 나타납니다. 반대로 말하면, ‘개인’ 저장소만은 상속되지 않으므로, 클라이언트 인증서(=개인 저장소에 넣는 것)는 ‘누구에게 보여야 하는가’를 스스로 결정해야 합니다. 이 비대칭성이 이 글 전체의 핵심 주제입니다.

사용자-CurrentUser계정마다 별개컴퓨터-LocalMachinePC에 1개・모든 사용자와 서비스에 공통내용을 상속해서 보여줌상속상속개인-My※상속되지 않음-스스로 넣을 위치를 결정신뢰할 수 있는 루트 인증 기관-Root중간 인증 기관-CA신뢰할 수 있는 게시자-TrustedPublisher개인-My신뢰할 수 있는 루트 인증 기관-Root중간 인증 기관-CA신뢰할 수 있는 게시자-TrustedPublisher

2.2. 주요 논리 저장소

각 위치의 내부는 역할별 논리 저장소로 나뉩니다. certmgr.msc / certlm.msc에서 보이는 폴더가 그것이며, PowerShell이나 명령에서 볼 때는 영문 내부 이름을 사용합니다.24

표시 이름 내부 이름 무엇을 넣는 곳인가
개인 My 자신(이 PC・이 사용자)이 사용하는 인증서. 클라이언트 인증서・서버 인증서는 여기. 비밀 키와 연결되는 곳도 여기
신뢰할 수 있는 루트 인증 기관 Root 신뢰의 출발점이 되는 루트 CA 인증서. 여기에 넣은 CA 하위는 ‘신뢰됨’
중간 인증 기관 CA 루트와 말단 사이를 잇는 중간 CA 인증서. 체인 구축의 재료
신뢰할 수 있는 게시자 TrustedPublisher 서명된 소프트웨어의 게시자로서 신뢰하는 인증서(8장)

2.3. 세 개의 창 ── certmgr.msc / certlm.msc / Cert: 드라이브

같은 저장소를 보는 방법이 세 가지 있습니다.34

  • certmgr.msc: 현재 사용자의 저장소를 여는 관리 콘솔.
  • certlm.msc: 로컬 컴퓨터의 저장소를 여는 관리 콘솔.
  • PowerShell의 Cert: 드라이브: Cert:\CurrentUser\...Cert:\LocalMachine\... 계층으로 저장소를 파일 시스템처럼 조작할 수 있습니다. 인증서는 지문으로 식별됩니다.

덧붙여 mmc.exe에 인증서 스냅인을 수동으로 추가할 경우에는 ‘사용자 계정’, ‘컴퓨터 계정’, ‘서비스 계정’ 세 종류 중에서 대상을 선택합니다. 관리자가 아닌 사용자가 관리할 수 있는 것은 자신의 사용자 계정 저장소뿐입니다.3

문제 조사의 첫걸음은 ‘앱이 어느 저장소를 보고 있는가’와 ‘내가 어느 저장소를 보고 있는가’를 일치시키는 것입니다. certmgr.msc를 들여다보면서 서비스 장애를 조사해도, 보고 있는 위치가 다르므로 영원히 답이 나오지 않습니다.

3. 어느 쪽에 넣어야 하는가 ── 프로그램의 실행 형태로 정하는 판단표

판단 기준은 하나입니다. 그 인증서를 사용하는 프로그램은 누구의 계정으로 동작하는가.

실행 형태 실행 계정 넣을 저장소 비고
대화형 사용자가 실행하는 데스크톱 앱 로그온 사용자 본인 사용자(Cert:\CurrentUser\My) 사용하는 사람의 계정마다 도입이 필요. 공유 PC에서 여러 명이 사용한다면 컴퓨터 저장소도 검토
Windows 서비스 LocalSystem / NETWORK SERVICE / 전용 서비스 계정 컴퓨터(Cert:\LocalMachine\My) LocalSystem 이외(NETWORK SERVICE・전용 계정 등)는 비밀 키 읽기 권한 부여가 필수(4장). LocalSystem은 기본 SYSTEM 권한으로 읽을 수 있음
IIS상의 웹 앱 애플리케이션 풀의 ID 컴퓨터 위와 동일
작업 스케줄러의 무인 실행(사용자 로그온 여부와 관계없이 실행) 작업에 지정한 계정 컴퓨터 권장 실행 계정의 사용자 저장소로도 동작시킬 수 있지만, 프로필과 저장소가 보이는 방식의 검증이 늘어날 뿐 이점이 적음
브라우저에서의 전자 신청・웹 인증 로그온 사용자 본인 사용자 배포한 본인 외에는 사용하지 못하게 한다는 의미에서도 자연스러움

판단이 서지 않는다면, 무인으로 동작하는 것은 컴퓨터 저장소, 사람이 조작하는 것은 사용자 저장소입니다.

3.1. 단골 사고의 해부 ── “개발 중에는 동작했는데 서비스화하니 찾을 수 없다”

이 사고는 다음 절차로 정확히 재현할 수 있습니다.

  1. 개발자가 자신의 PC에서 pfx를 더블클릭해서 임포트한다. 마법사의 기본값은 ‘현재 사용자’이므로, 인증서는 개발자 계정의 사용자 저장소에 들어간다.
  2. 개발 중인 앱은 Visual Studio에서, 즉 개발자 계정으로 동작하므로, StoreLocation.CurrentUser를 열면 인증서를 찾을 수 있다. 동작한다.
  3. 운영 서버에서 Windows 서비스로 등록한다. 서비스는 NETWORK SERVICE나 전용 계정으로 동작한다.
  4. 서비스 코드가 여는 CurrentUser서비스 실행 계정의 사용자 저장소. 그곳은 비어 있다. ‘인증서를 찾을 수 없습니다.’
운영 서버개발 머신같은 프로그램을 배치코드가 여는 CurrentUser는서비스 계정의 사용자 저장소Windows 서비스로 등록실행 계정은 NETWORK SERVICE 등그곳은 비어 있음→ '인증서를 찾을 수 없습니다'개발자 계정의사용자 저장소에 들어감pfx를 더블클릭해서 임포트마법사의 기본값은 '현재 사용자'Visual Studio에서 실행=개발자 계정으로 동작CurrentUser를 열면 찾을 수 있음→ 동작함

핵심은 사용자 저장소가 ‘계정 수만큼 존재한다’는 점입니다. 관리자가 certmgr.msc를 열어 ‘제대로 들어 있는데요?’라고 확인해도, 그것은 관리자 자신의 저장소이지 서비스 계정의 저장소가 아닙니다. 대처는 임기응변식 복사가 아니라, 컴퓨터 저장소에 다시 넣고, 코드도 StoreLocation.LocalMachine으로 맞추는 것입니다. 그리고 다음 장의 권한 부여까지가 한 세트입니다.

4. 비밀 키와 접근 권한 ── 두 번째 단골 사고

4.1. 인증서와 비밀 키는 별개

인증서 저장소 목록에 보이는 것은 인증서(공개 정보)이지, 비밀 키 자체가 아닙니다. 클라이언트 인증에서 실제로 필요한 것은 비밀 키를 사용한 서명 처리이므로, ‘목록에 보인다’는 것과 ‘사용할 수 있다’는 것은 별개의 문제입니다. 이 점을 혼동하면, ‘인증서는 있는데 TLS 핸드셰이크에서 실패한다’, ‘Access Denied 계열의 내부 오류가 나온다’는, 겉으로는 알아보기 힘든 장애가 됩니다.

4.2. pfx 임포트 실무 ── 내보내기 가능 여부는 의사결정

인증서와 비밀 키의 쌍은 pfx(PKCS #12) 파일로 주고받으며, Import-PfxCertificate로 저장소에 가져올 수 있습니다.6

$pwd = Get-Credential -UserName '(아래에 비밀번호 입력)' -Message 'PFX 비밀번호'
Import-PfxCertificate -FilePath C:\certs\client.pfx `
    -CertStoreLocation Cert:\LocalMachine\My -Password $pwd.Password

여기서 중요한 것이, -Exportable을 붙이지 않는 한, 가져온 비밀 키는 다시 내보낼 수 없다는 기본 동작입니다.6 ‘나중에 이관할 수 있도록’이라며 무엇이든 내보내기 가능 상태로 넣는 것은, 비밀 키를 반출할 수 있는 경로를 하나 늘리는 행위입니다. 원본 pfx를 안전하게 보관하는 운용으로 하고, 저장소상의 비밀 키는 내보내기 불가가 기본── 이것이 저희 회사의 권장 사항입니다. 덧붙여 원본 pfx와 그 비밀번호의 보관이야말로 평문으로 방치되기 쉽습니다. 사고방식은 ‘Windows 앱의 기밀 정보 저장 ── DPAPI로 평문 설정을 피한다‘와 ‘PowerShell에서 자격 증명을 안전하게 다루기‘에서 정리하고 있습니다.

4.3. 서비스 계정에 대한 비밀 키 권한 부여

컴퓨터 저장소에 넣은 인증서의 비밀 키는, 기본적으로 관리자와 SYSTEM 외에는 읽을 수 없는 구성이 보통입니다. 이 때문에 LocalSystem으로 동작하는 서비스는 기본 상태 그대로 비밀 키를 읽을 수 있지만, NETWORK SERVICE・전용 서비스 계정・IIS 앱 풀 ID 등 그 외의 계정으로 동작시킬 경우에는 실행 계정에 명시적으로 읽기 권한을 부여해야 합니다. 절차는 인증서 스냅인 UI에서 진행할 수 있습니다.5

  1. certlm.msc(또는 컴퓨터 계정 대상의 인증서 스냅인)를 연다.
  2. ‘개인’ → ‘인증서’에서 대상 인증서를 마우스 오른쪽 버튼으로 클릭하고, ‘모든 작업’에서 ‘비밀 키 관리’를 연다.
  3. ‘보안’ 탭에서 실행 계정(NETWORK SERVICE, 전용 서비스 계정, IIS의 앱 풀 ID 등)을 추가하고, ‘읽기’를 허용한다.5

전체 제어는 필요 없습니다. 서명에만 사용한다면 읽기로 충분합니다. 반대로, 동작하지 않는다고 해서 Everyone에 전체 제어를 부여하는 것은 비밀 키를 평문 비밀번호 수준으로 취급하는 행위이므로 절대 피하십시오. 컴퓨터 저장소 배치와 비밀 키 권한 부여는 항상 한 세트── 이것을 절차서에 적어 두는 것만으로 이 계통의 사고는 사라집니다.

5. 만료 사고를 막는다 ── 점검・교체・대장

5.1. PowerShell로 점검한다

인증서의 유효기간은 NotAfter 속성에 들어 있습니다. Cert: 드라이브에 대한 Get-ChildItem으로 기계적으로 점검할 수 있습니다.4

# 컴퓨터 저장소의 "개인"을 유효기간 순으로 나열
Get-ChildItem Cert:\LocalMachine\My |
    Sort-Object NotAfter |
    Format-Table Thumbprint, Subject, NotAfter

# 60일 이내에 만료되는 것만 추출(0을 지정하면 이미 만료된 것)
Get-ChildItem -Path Cert:\LocalMachine\My -ExpiringInDays 60

-ExpiringInDays는 ‘지정한 일수 이내에 만료되는 인증서’를 반환하는 매개변수로, 0을 지정하면 이미 만료된 인증서가 나옵니다.4 이것을 월간 예약 작업으로 만들어 모든 서버를 돌리고, 결과를 메일이든 대장이든 집계하는── 그것만으로 ‘만료로 인해 월요일 아침부터 자격확인이 통과되지 않는’ 유형의 사고는 거의 방지할 수 있습니다.

5.2. 교체 절차 ── 신구 병행 기간과 지문의 함정

인증서 갱신은 ‘삭제하고 넣기’가 아니라 ‘추가한 뒤 전환하고, 확인한 뒤 삭제하기‘입니다.

  1. 새 인증서(pfx)를 같은 저장소에 임포트한다. 지문이 다르므로 신구는 같은 저장소에 공존할 수 있습니다.
  2. 새 인증서의 비밀 키 권한을 부여한다(4장). 갱신 시 잊기 쉬운 부분이 바로 여기입니다. 권한은 인증서의 비밀 키마다 붙는 것이므로, 인증서를 교체하면 부여도 다시 해야 합니다.
  3. 상대 시스템으로의 신고(인증서 사전 등록이 필요한 API의 경우)를, 기존 인증서로 운영을 계속한 채 먼저 마치고, 신구 어느 쪽이든 받아들일 수 있는 병행 기간을 확보한다. 전환을 먼저 하면 상대 측이 새 인증서를 거부해서 운영 통신이 멈춥니다.
  4. 앱의 구성을 새 인증서로 전환하고, 동작을 확인한다.
  5. 충분한 기간이 지난 후, 기존 인증서를 삭제한다.
1. 새 pfx를 같은 저장소에임포트-신구 공존2. 새 인증서의비밀 키 권한 부여3. 상대방에게 사전 등록-기존 인증서로 운영 계속4. 구성의 지문을 변경해전환・동작 확인5. 병행 기간 후기존 인증서 삭제

이때 가장 큰 함정은, 구성 파일이나 코드에 적힌 지문입니다. 지문은 인증서마다 고유하므로, 갱신하면 반드시 바뀝니다. 어딘가 한 곳이라도 예전 지문을 참조한 채로 남아 있으면, ‘인증서는 갱신했는데 연결이 안 된다’가 발생합니다. 지문이 어디에 적혀 있는지(앱 구성, IIS 바인딩, 스크립트, 상대방 신고)를 대장으로 관리하는 것이 확실합니다.

5.3. 인증서 대장을 권함

대장이라 해도 우선은 Excel 한 장이면 충분합니다. 최소한 용도/발급자/서브젝트/지문/들어 있는 위치(서버 이름+저장소)/비밀 키 권한을 가진 계정/유효기간/갱신 절차 링크/담당자 열을 만들어, 5.1의 점검 결과와 대조합니다. 인증서 사고의 실체는 기술 문제가 아니라 ‘아무도 목록을 가지고 있지 않다’는 문제이므로, 대장이 가장 효과적입니다.

6. 검증과 실패 해석 ── 체인과 루트 배포

6.1. 체인 검증의 기본과 certutil

‘이 인증서는 신뢰할 수 없습니다’ 계열의 오류는, 말단 인증서부터 루트 CA까지의 체인(인증 경로)이 어딘가에서 끊어져 있는 상태입니다. 원인 파악에는 certutil이 편리합니다.7

입수할 수 없음제시・AIA・저장소 어느 쪽도배포되지 않음기한 만료말단 인증서클라이언트 인증서・서버 인증서중간 CA 인증서저장 위치 - 중간 인증 기관-CA 저장소루트 CA 인증서저장 위치 - 신뢰할 수 있는 루트 인증 기관-Root체인을 구축할 수 없음단골 원인 1'신뢰할 수 없음' 오류단골 원인 2유효기간 오류단골 원인 3
:: 인증서 파일의 체인을 구축·검증한다(폐기 확인 URL 취득 포함)
certutil -urlfetch -verify client.cer

:: 대상 앱이 사용자 저장소를 사용한다면 -user를 붙여 같은 맥락으로 검증한다
certutil -user -urlfetch -verify client.cer

:: 저장소 내용을 덤프한다(-user를 붙이면 사용자 저장소)
certutil -store My
certutil -user -store My

certutil -verify는 인증서・CRL・체인의 검증을 수행하며, CACertFile을 지정하지 않으면 완전한 체인을 구축해서 검증합니다.7 출력은 길지만, 어느 계층에서 신뢰가 끊겼는지, 폐기 정보를 가져왔는지를 읽어낼 수 있습니다. 전형적인 원인은, (1) 중간 CA 인증서를 입수할 수 없음(TLS 상대가 보내주지 않고, 인증서의 AIA 정보로도 가져올 수 없고, ‘중간 인증 기관’ 저장소에도 들어 있지 않음), (2) 사내 CA의 루트가 ‘신뢰할 수 있는 루트 인증 기관’에 배포되어 있지 않음, (3) 인증서 자체의 기한 만료, 이 세 가지입니다. 중간 CA는 상대방의 제시나 AIA를 통한 자동 취득으로도 해결되므로, 저장소 배치는 ‘확실히 하는 수단 중 하나’로 받아들이십시오.

6.2. 사내 CA・자체 서명 루트 배포는 GPO/Intune으로

사내 CA나 검증용 자체 서명 인증서를 사용할 경우, 그 루트 인증서를 각 PC에 배포해야 합니다. 한 대씩 수작업으로 넣지 말고, 배포 체계에 태우십시오.

  • Active Directory 환경(GPO): 그룹 정책의 컴퓨터 구성\정책\Windows 설정\보안 설정\공개 키 정책에 있는 ‘신뢰할 수 있는 루트 인증 기관’에 인증서를 임포트하면, 대상 PC에 배포됩니다.8
  • Intune 관리 환경: ‘신뢰할 수 있는 인증서’ 프로필로 루트/중간 CA 인증서를 배포합니다. Windows에서는 배포 대상 저장소(컴퓨터의 루트/중간, 사용자의 중간)를 선택할 수 있습니다.9

2.1에서 언급했듯이, 컴퓨터 저장소의 루트에 넣으면 모든 사용자에게 신뢰됩니다.1 그렇기 때문에 반대의 위험도 직시해야 합니다. 자체 서명 인증서를 ‘신뢰할 수 있는 루트 인증 기관’에 넣는 운용은, 그 PC에 새로운 신뢰의 출발점을 심는 행위입니다. 그 비밀 키가 유출되면, 임의의 사이트나 소프트웨어로 위장하는 인증서를 발급받을 수 있는 발판이 됩니다. 항구적인 운용으로 하려면, 비밀 키를 적절히 보호한 사내 CA를 구축하거나 공인 CA의 인증서로 옮기는 것이 정도이며, 자체 서명 루트는 ‘검증 환경 한정・기한을 정해서’가 원칙입니다.

7. 개발자의 관점 ── .NET에서 저장소를 올바르게 사용한다

7.1. X509Store로 지문 검색한다

.NET에서는 X509Store로 저장소를 열고, Find로 인증서를 가져옵니다.1011

using System.Security.Cryptography.X509Certificates;

static X509Certificate2 GetClientCertificate(string thumbprint)
{
    using var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

    var found = store.Certificates.Find(
        X509FindType.FindByThumbprint, thumbprint, validOnly: true);

    if (found.Count == 0)
        throw new InvalidOperationException(
            $"인증서를 찾을 수 없습니다: 지문={thumbprint}, " +
            $"위치={store.Location}\\{store.Name}");

    var cert = found[0];
    if (!cert.HasPrivateKey)
        throw new InvalidOperationException(
            $"인증서는 있지만 비밀 키가 연결되어 있지 않습니다(.cer로부터의 " +
            $"임포트 등): 지문={thumbprint}, 위치={store.Location}\\{store.Name}");

    return cert;
}

3장의 판단이 여기에 직결됩니다. 서비스로 동작하는 코드라면 StoreLocation.LocalMachine, 대화형 앱이라면 StoreLocation.CurrentUser입니다. 또 하나, Find의 세 번째 인수 validOnly에 주의하십시오. true검증을 통과한 유효한 인증서만을 반환합니다.11 만료된 인증서를 붙잡지 않는 보험이 되는 한편, 체인이 신뢰되지 않는 테스트용 자체 서명 인증서도 ‘찾을 수 없음’ 쪽으로 떨어지므로, ‘들어 있는데 찾을 수 없다’일 때는 이 부분도 의심하십시오. 또한 찾지 못했을 때의 오류 메시지에는, 위 예시처럼 어느 저장소를 찾았는지를 반드시 넣으십시오. 3장에서 다룬 사고의 조사 시간이 자릿수 단위로 달라집니다.

7.2. HttpClient에 클라이언트 인증서를 싣는다

가져온 인증서는 HttpClientHandler.ClientCertificates에 추가해서 서버에 제시합니다. 이 컬렉션이, 인증서 기반 클라이언트 인증에서 서버에 제시되는 인증서의 집합입니다.12

var handler = new HttpClientHandler();
handler.ClientCertificates.Add(GetClientCertificate(thumbprint));

var client = new HttpClient(handler);
// 이후에는 일반적인 HttpClient로 사용

덧붙여 .NET Core 계열에서는, 인증서에 키 사용법(Key Usage) 속성이 있는 경우, ‘Digital Signature’를 포함하지 않으면 요청 전송에 사용되지 않는다는 점이 문서에 명시되어 있습니다.12 클라이언트 인증서 발급을 의뢰하는 입장이 되었다면, 용도(클라이언트 인증)를 정확히 전달하십시오. 또한 HttpClient는 생성 패턴을 잘못하면 소켓 고갈이나 DNS 추적 문제를 일으킵니다. 핸들러째로 장수명으로 만드는 설계는 ‘HttpClient를 using으로 감싸면 안 되는 이유‘에서 다룬 대로입니다.

7.3. 하드코딩한 지문이 교체 시 죽는 문제

지문 검색은 확실하지만, 지문을 코드에 심어 넣으면 인증서를 갱신할 때마다 빌드와 릴리스가 필요해집니다. 설계상의 대처는 다음 3단계입니다.

  • 최소한: 지문을 구성 파일(appsettings 등)로 외부화해서, 릴리스 없이 교체할 수 있도록 한다. 구성 위치는 5.3의 대장에 기재한다.
  • 한 단계 더: 서브젝트 이름이나 발급자로 검색하고, validOnly: true와 조합해서 ‘그 이름으로 현재 유효한 것 중 NotAfter가 가장 먼 것’을 선택한다. 신구 병행 기간에 새 인증서로 자동으로 전환됩니다. 다만 같은 이름의 의도치 않은 인증서를 붙잡을 위험이 있으므로, 발급자 확인과 로그 출력을 세트로 합니다. 또한 이 자동 전환이 성립하는 것은 상대방이 인증서 사전 등록을 필요로 하지 않는 경우뿐입니다. 사전 등록이 필요한 API(5.2)에서는, 임포트만 한 미등록 인증서로 멋대로 전환되어 통신이 멈출 수 있으므로, 등록 완료를 확인한 뒤 전환하는 구성 외부화 방식에 그치십시오.
  • 운용으로 마무리: 어떤 방식이든, 시작 시점에 ‘어느 인증서(지문・기한)를 선택했는가’를 로그에 남긴다. 장애 조사에서도 대장 대조에서도, 이 한 줄이 효과를 냅니다.

8. 코드 서명 인증서와의 관계 ── ‘신뢰할 수 있는 게시자’ 저장소

여기까지 다룬 것은 통신(TLS)을 위한 인증서였지만, 인증서 저장소에는 또 하나의 세계── 코드 서명──가 함께 존재합니다. 2.2의 표에 나온 ‘신뢰할 수 있는 게시자(TrustedPublisher)’ 저장소가 그 접점으로, 서명된 소프트웨어의 게시자 인증서를 신뢰됨으로 등록하는 장소입니다. 사용자・컴퓨터 양쪽 위치에 존재하며10, 사내 배포 앱의 게시자를 GPO로 각 PC의 TrustedPublisher에 배포하는 등의 운용에 사용됩니다.

앱을 ‘배포하는 쪽’으로서, 코드 서명이나 SmartScreen 경고(‘Windows에 의해 PC가 보호되었습니다’)에 대한 대처가 필요하신 분은, 별도 글 ‘Windows에서 ‘Windows에 의해 PC가 보호되었습니다’가 나타나는 이유‘에 정리했습니다. 이 글의 지식(저장소의 두 계통, 루트 배포)은 그대로 전제 지식으로 사용할 수 있습니다.

9. 정리

  • 인증서 저장소는 사용자(CurrentUser)와 컴퓨터(LocalMachine) 두 계통. certmgr.msc / certlm.msc / Cert: 드라이브는 같은 것을 보는 세 개의 창입니다. 조사의 첫걸음은 ‘어느 저장소를 이야기하고 있는가’를 맞추는 것.
  • 넣을 위치는 ‘프로그램이 누구로서 동작하는가’로 정합니다. 무인 실행(서비스・IIS・작업)은 컴퓨터 저장소, 대화형 앱은 사용자 저장소가 원칙입니다.
  • ‘개발 중에는 동작했는데 운영에서 찾을 수 없다’는, 개발자의 사용자 저장소와 서비스 계정의 사용자 저장소가 별개라는 것이 원인. 컴퓨터 저장소+StoreLocation.LocalMachine으로 맞춰 해결합니다.
  • 컴퓨터 저장소 배치와 ‘비밀 키 관리’에서의 읽기 권한 부여는 한 세트. 갱신 시 다시 부여하는 것도 잊지 말 것.
  • pfx 임포트는 기본적으로 내보내기 불가. -Exportable은 정말로 필요할 때만. 원본 pfx와 비밀번호의 보관도 설계에 포함시킵니다.
  • 만료는 Get-ChildItem Cert: ... -ExpiringInDays의 정기 점검과 인증서 대장으로 방지합니다. 교체는 ‘추가→전환→확인→삭제’ 순서로, 구성 안의 지문을 갱신하지 않고 빠뜨리는 것에 주의.
  • 체인 원인 파악은 certutil -urlfetch -verify. 사내 CA의 루트는 GPO/Intune으로 배포하고, 자체 서명을 루트에 넣는 운용은 검증 환경 한정・기한을 정해서.
  • 코드에서는 지문을 구성으로 외부화하고, 선택한 인증서를 로그에 남긴다. 그것만으로 인증서로 인한 장애 대응은 몰라보게 달라집니다.

관련 글

관련 상담 영역

합동회사 코무라소프트에서는 클라이언트 인증서를 사용하는 Web API 연동(은행계 API, 온라인자격확인 등)을 포함한 업무 앱 개발, ‘인증서를 찾을 수 없음’, ‘갱신했더니 연결이 안 됨’ 유형의 장애 조사, 인증서 교체 절차 정비를 다루고 있습니다. 저장소의 어디를 봐야 할지 모르겠다는 단계의 상담도 괜찮습니다.

참고 링크

  1. Microsoft Learn, Local Machine and Current User Certificate Stores. 컴퓨터의 인증서 저장소가 PC에 대해 로컬이며 모든 사용자에게 공통으로 HKEY_LOCAL_MACHINE 아래에 있다는 점, 사용자의 인증서 저장소가 사용자 계정마다 존재하며 HKEY_CURRENT_USER 아래에 있다는 점, 사용자 저장소가 ‘개인’ 저장소를 제외하고 컴퓨터 저장소의 내용을 상속한다는 점(컴퓨터의 ‘신뢰할 수 있는 루트 인증 기관’에 추가한 인증서가 각 사용자의 같은 저장소에도 나타난다는 점)에 대해.  2 3 4

  2. Microsoft Learn, System Store Locations. CERT_SYSTEM_STORE_CURRENT_USER / CERT_SYSTEM_STORE_LOCAL_MACHINE의 레지스트리 위치(각각 HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE의 Software\Microsoft\SystemCertificates), 정의된 논리 저장소가 MY・Root・Trust・CA라는 점, 서비스용 저장소가 서비스 이름별 레지스트리 키(Software\Microsoft\Cryptography\Services\ServiceName\SystemCertificates)에 있다는 점, 그룹 정책 배포용 저장소가 별도로 존재한다는 점에 대해.  2 3 4 5

  3. Microsoft Learn, How to: View certificates with the MMC snap-in. certlm.msc가 로컬 디바이스(로컬 컴퓨터)의 인증서를, certmgr.msc가 현재 사용자의 인증서를 관리하는 도구라는 점, 인증서 스냅인의 대상으로 ‘컴퓨터 계정’, ‘사용자 계정’, ‘서비스 계정’ 세 종류가 있다는 점, 관리자가 아닌 사용자는 자신의 사용자 계정 인증서만 관리할 수 있다는 점에 대해.  2 3 4

  4. Microsoft Learn, about_Certificate_Provider. PowerShell의 Cert: 드라이브가 CurrentUser와 LocalMachine 두 저장소 위치를 갖는 계층 네임스페이스라는 점, Get-ChildItem으로 저장소와 인증서를 나열할 수 있다는 점, -ExpiringInDays 매개변수가 지정한 일수 이내에 만료되는 인증서(0이면 이미 만료된 것)를 반환한다는 점, -CodeSigningCert 등의 동적 매개변수, NotAfter 속성에 유효기간이 저장된다는 점, 인증서가 지문으로 식별된다는 점에 대해.  2 3 4 5 6

  5. Microsoft Learn, How to Modify Private Key Permissions to Support Management Server or Streaming Server. 로컬 컴퓨터의 인증서 저장소를 대상으로 한 인증서 스냅인에서 ‘비밀 키 관리’(Manage Private Keys)를 열고, ‘보안’ 탭에서 서비스의 실행 계정(예: Network Service)에 ‘읽기’ 액세스 권한을 추가하는 절차에 대해.  2 3

  6. Microsoft Learn, Import-PfxCertificate. Import-PfxCertificate가 PFX 파일에서 인증서와 비밀 키를 지정한 저장소로 가져온다는 점, -Exportable 스위치를 지정하지 않으면 가져온 비밀 키를 내보낼 수 없다는 점, -CertStoreLocation・-Password・-FilePath 각 매개변수의 구문과 사용 예에 대해.  2 3

  7. Microsoft Learn, certutil. certutil -verify가 인증서・CRL・인증서 체인을 검증하며, CA 인증서 파일을 지정하지 않으면 완전한 체인을 구축해서 검증한다는 점, -urlfetch 옵션을 사용할 수 있다는 점, certutil -store가 인증서 저장소를 덤프하며, -user 옵션으로 컴퓨터 저장소 대신 사용자 저장소에 접근할 수 있다는 점에 대해.  2

  8. Microsoft Learn, Distribute Certificates to Client Computers by Using Group Policy. 그룹 정책의 ‘컴퓨터 구성\정책\Windows 설정\보안 설정\공개 키 정책’ 아래의 ‘신뢰할 수 있는 루트 인증 기관’에 인증서를 임포트해서, 도메인 내 클라이언트 컴퓨터에 배포하는 절차와, 필요한 권한(Domain Admins / Enterprise Admins에 준함)에 대해. 

  9. Microsoft Learn, Create trusted certificate profiles in Microsoft Intune. Intune의 ‘신뢰할 수 있는 인증서’ 프로필이 루트 또는 중간 CA 인증서를 관리 대상 디바이스에 배포하는 방식이라는 점, SCEP/PKCS 인증서 프로필의 전제로서 루트 CA에 대한 신뢰 확립에 사용된다는 점, Windows에서는 배포 대상 저장소로 ‘컴퓨터 인증서 저장소 - 루트’, ‘컴퓨터 인증서 저장소 - 중간’, ‘사용자 인증서 저장소 - 중간’을 선택할 수 있다는 점에 대해. 

  10. Microsoft Learn, X509Store Class. X509Store가 StoreName과 StoreLocation(CurrentUser / LocalMachine)을 지정해서 생성할 수 있고, Open 메서드와 OpenFlags(ReadOnly, OpenExistingOnly 등)로 저장소를 열며, Certificates 속성으로 인증서 컬렉션을 가져올 수 있다는 점, 표준 저장소 이름에 My・Root・CA・TrustedPublisher 등이 포함되며, TrustedPublisher 저장소가 CurrentUser・LocalMachine 양쪽에 존재한다는 점에 대해.  2

  11. Microsoft Learn, X509Certificate2Collection.Find(X509FindType, Object, Boolean) Method. Find 메서드가 X509FindType(FindByThumbprint 등)과 검색 값으로 인증서를 검색한다는 점, 세 번째 인수 validOnly에 true를 지정하면 검증을 통과한 유효한 인증서만 반환된다는 점에 대해.  2

  12. Microsoft Learn, HttpClientHandler.ClientCertificates Property. ClientCertificates 속성이 인증서 기반 클라이언트 인증에서 서버에 제시되는 X509CertificateCollection이라는 점, .NET Core에서는 인증서에 키 사용법 속성이 존재하는 경우 ‘Digital Signature’를 포함해야 한다는 점에 대해.  2

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

certmgr.msc와 certlm.msc는 무엇이 다른가요?
대상 저장소가 다릅니다. certmgr.msc는 로그온 중인 사용자의 인증서 저장소(현재 사용자, CurrentUser)를, certlm.msc는 컴퓨터의 인증서 저장소(로컬 컴퓨터, LocalMachine)를 엽니다. 컴퓨터 저장소는 PC상의 모든 사용자와 서비스에 공통이며, 관리에는 관리자 권한이 필요합니다. 관리자가 아닌 사용자가 관리할 수 있는 것은 자신의 사용자 저장소뿐입니다. 두 저장소 모두 내부는 '개인', '신뢰할 수 있는 루트 인증 기관' 등의 논리 저장소로 나뉘어 있으며, PowerShell에서는 Cert:\CurrentUser와 Cert:\LocalMachine으로 같은 구조가 보입니다.
클라이언트 인증서는 사용자와 컴퓨터 중 어느 저장소에 넣어야 하나요?
그 인증서를 사용하는 프로그램이 '누구로서' 동작하는지에 따라 정합니다. 대화형 사용자가 실행하는 데스크톱 앱이라면 사용하는 본인의 사용자 저장소(Cert:\CurrentUser\My)가 기본입니다. Windows 서비스・IIS 애플리케이션 풀・작업 스케줄러로 무인 실행하는 프로그램이라면, 컴퓨터 저장소(Cert:\LocalMachine\My)에 넣고, 실행 계정에 비밀 키 읽기 권한을 부여합니다. 사용자 저장소는 계정마다 별개이므로, 개발자가 자신의 사용자 저장소에 넣은 인증서는 다른 계정으로 동작하는 서비스에서는 보이지 않습니다. 이것이 '개발 중에는 동작했는데 운영에서 찾을 수 없다'는 사고의 전형적인 원인입니다.
Windows 서비스에서 인증서를 찾을 수 없거나 사용할 수 없을 때는 무엇을 확인해야 하나요?
확인은 두 단계입니다. 첫째로 '어느 저장소를 보고 있는가'. 코드가 StoreLocation.CurrentUser를 열고 있다면, 그것은 서비스 실행 계정의 사용자 저장소이며, 관리자가 certmgr.msc로 보고 있는 자신의 저장소와는 별개입니다. 인증서를 컴퓨터 저장소로 옮기고, 코드도 StoreLocation.LocalMachine에 맞춥니다. 둘째로 '비밀 키를 읽을 수 있는가'. 인증서 목록에 보이는 것과 비밀 키를 사용할 수 있는 것은 별개이며, 컴퓨터 저장소의 비밀 키는 기본적으로 관리자와 SYSTEM만 접근할 수 있는 구성이 일반적입니다. certlm.msc에서 대상 인증서의 '비밀 키 관리'를 열고, 서비스의 실행 계정(NETWORK SERVICE 등)에 '읽기'를 부여하십시오.
인증서 만료를 PowerShell로 사전에 찾아내려면?
Cert: 드라이브에 대한 Get-ChildItem으로 점검할 수 있습니다. 예를 들어 Get-ChildItem Cert:\LocalMachine\My | Sort-Object NotAfter | Format-Table Thumbprint, Subject, NotAfter로, 컴퓨터 저장소의 개인 저장소를 유효기간 순으로 나열할 수 있습니다. 또한 -ExpiringInDays 매개변수를 사용하면 '지정한 일수 이내에 만료되는 인증서'만 추출할 수 있으며, 0을 지정하면 이미 만료된 인증서가 나옵니다. 이것을 월간으로 모든 서버에 대해 실행하고, 결과를 인증서 대장과 대조하는 운용으로 해 두면, '만료로 인해 아침부터 연결할 수 없는' 유형의 사고는 거의 방지할 수 있습니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기