คู่มือปฏิบัติที่เก็บใบรับรองของ Windows — จะใส่ฝั่งผู้ใช้หรือคอมพิวเตอร์
· Go Komura · ใบรับรอง, Windows, ความปลอดภัย, PKI, TLS, PowerShell, แอปธุรกิจ, ระบบสารสนเทศ
«ตอนอัปเดตเครื่องยืนยันสิทธิ์ออนไลน์ ใส่ใบรับรองลูกข่ายลงพีซีใหม่แล้วเชื่อมไม่ได้» «บนเครื่องพัฒนาต่อ API ธนาคารได้ พอทำให้เป็นบริการ Windows กลับบอกว่า «หาใบรับรองไม่พบ»» «ใบรับรองที่เห็นใน certmgr.msc กับที่เห็นใน certlm.msc อันไหนของจริงก็ไม่รู้» ── เมื่อรับพัฒนาซอฟต์แวร์ตามสั่งของการเชื่อม Web API ที่มีใบรับรองลูกข่าย คำปรึกษาชนิดนี้มาเป็นระยะ
การยืนยันสิทธิ์ออนไลน์ของสถานพยาบาล การยื่นอิเล็กทรอนิกส์ API ตระกูลธนาคาร EDI กับคู่ค้า ใบรับรองลูกข่ายที่เคยมีแค่ผู้รับผิดชอบโครงสร้างพื้นฐานขององค์กรใหญ่ที่แตะ ตอนนี้เป็นยุคที่เจ้าหน้าที่สารสนเทศของธุรกิจขนาดกลางและเล็กและนักพัฒนาแอปธุรกิจต้องจัดการ และอุบัติเหตุรอบใบรับรองจริง ๆ รวมได้แค่ไม่กี่แบบ ใส่ผิดที่ ลืมสิทธิ์คีย์ลับ ลืมวันหมดอายุ ── สามอย่างนี้
บทความนี้มุ่งนักพัฒนาแอปธุรกิจที่ใช้ใบรับรองลูกข่าย และเจ้าหน้าที่สารสนเทศที่ถูกมอบงานสลับใบรับรอง โดยยึดการตัดสิน «จะใส่ที่เก็บผู้ใช้หรือที่เก็บคอมพิวเตอร์» เป็นแกน แล้วจัดรวดเดียวจากโครงที่เก็บใบรับรองของ Windows การให้สิทธิ์คีย์ลับ การทำรายการวันหมดอายุด้วย PowerShell ไปจนโค้ดการใช้จาก .NET เนื้อหาอิงแหล่งปฐมภูมิของ Microsoft Learn ณ สิงหาคม 2026
1. สรุปก่อนเลย
- ที่เก็บใบรับรองของ Windows มีสองสาย คือ «ผู้ใช้ (CurrentUser)» กับ «คอมพิวเตอร์ (LocalMachine)» ที่เก็บผู้ใช้คนละชุดต่อบัญชี (ใต้ HKEY_CURRENT_USER ในรีจิสทรี) ที่เก็บคอมพิวเตอร์ร่วมกันทั้งพีซี (ใต้ HKEY_LOCAL_MACHINE)12
- เครื่องมือจัดการก็มีสอง certmgr.msc เปิดที่เก็บของผู้ใช้ปัจจุบัน certlm.msc เปิดที่เก็บของคอมพิวเตอร์ท้องถิ่น จาก PowerShell คือ
Cert:\CurrentUserกับCert:\LocalMachine34 - จะใส่ที่ไหนตัดสินจาก «โปรแกรมที่ใช้ใบรับรองนั้นทำงานในฐานะใคร» แอปของผู้ใช้โต้ตอบคือที่เก็บผู้ใช้ บริการ Windows, IIS, การรันไร้คนของ Task Scheduler โดยหลักคือที่เก็บคอมพิวเตอร์ (ตารางตัดสินหมวด 3)
- สาเหตุของ «ตอนพัฒนางาน พอทำให้เป็นบริการแล้วหาไม่เจอ» เกือบมีอย่างเดียว ใบรับรองที่นักพัฒนาใส่ในที่เก็บผู้ใช้ของตนเอง CurrentUser ของบริการที่ทำงานด้วยบัญชีอื่นมองไม่เห็น (หมวด 3)
- ใบรับรองกับคีย์ลับคนละอย่าง แค่ใส่ที่เก็บคอมพิวเตอร์ บัญชีบริการมักอ่านคีย์ลับไม่ได้ ให้สิทธิ์อ่านแก่บัญชีที่รันด้วย «จัดการคีย์ลับ» ของ certlm.msc5
- ตอนนำเข้า pfx คีย์ลับตามค่าเริ่มต้นส่งออกไม่ได้
Import-PfxCertificateนำเข้าในรูปที่ส่งออกคีย์ลับซ้ำไม่ได้ เว้นแต่ระบุ-Exportableนี่ไม่ใช่อุบัติเหตุ แต่เป็นค่าเริ่มต้นที่พึงประสงค์6 - วันหมดอายุกันด้วยการทำรายการอัตโนมัติ สกัดใบรับรองที่หมดภายในจำนวนวันที่ระบุได้อย่างเครื่องกล เช่น
Get-ChildItem Cert:\LocalMachine\My -ExpiringInDays 604 - ถ้าฮาร์ดโค้ด thumbprint ในโค้ดหรือโครง จะตายทุกครั้งที่ต่ออายุใบรับรอง เพราะใบรับรองใหม่ thumbprint ย่อมเปลี่ยน ฐานของการออกแบบคือการดึงโครงออก + ช่วงคู่ขนานเก่าใหม่ (หมวด 5 และ 7)
แผนที่ความรู้ของบทความนี้
ที่เก็บใบรับรองของ Windows แยกเป็นสองสาย คือผู้ใช้ (CurrentUser) กับคอมพิวเตอร์ (LocalMachine) และการจะใส่ใบรับรองลูกข่ายที่ใดถูกกำหนดด้วย «โปรแกรมนั้นทำงานในฐานะใคร» บริการ Windows ที่รันไร้คนเป็นชุดของที่เก็บคอมพิวเตอร์กับการให้สิทธิ์คีย์ลับ และวันหมดอายุของใบรับรองกับ thumbprint ที่ฮาร์ดโค้ดเป็นสาเหตุประจำของความล้มเหลวของการเชื่อมจากใบรับรอง
flowchart LR
accTitle: แผนที่ความรู้คู่มือปฏิบัติที่เก็บใบรับรองของ Windows
accDescr: แผนภาพที่แสดงความสัมพันธ์ระหว่างที่เก็บใบรับรอง (ผู้ใช้/คอมพิวเตอร์) ใบรับรองลูกข่าย คีย์ลับ โซ่ใบรับรอง CA รากกับ CA กลาง วันหมดอายุกับ thumbprint และความล้มเหลวของการเชื่อม ขั้นตอนสลับ บัญชี และความเสี่ยงของใบรับรองที่ลงนามเอง
certificate_store["ที่เก็บใบรับรอง"]
client_certificate["ใบรับรองลูกข่าย"]
windows_service["บริการ Windows"]
localmachine_store["ที่เก็บใบรับรองของคอมพิวเตอร์"]
private_key["คีย์ลับ"]
private_key_acl["สิทธิ์เข้าถึงคีย์ลับ"]
certificate_chain["โซ่ใบรับรอง"]
intermediate_ca["ใบรับรอง CA กลาง"]
root_ca["ใบรับรอง CA ราก"]
certificate_expiry["วันหมดอายุของใบรับรอง"]
certificate_failure["ความล้มเหลวของการเชื่อมจากใบรับรอง"]
thumbprint_hardcode["การฮาร์ดโค้ด thumbprint"]
group_policy["Group Policy"]
intune["Microsoft Intune"]
currentuser_store["ที่เก็บใบรับรองของผู้ใช้"]
certmgr_msc["certmgr.msc"]
certlm_msc["certlm.msc"]
cert_drive["ไดรฟ์ Cert:"]
pfx["ไฟล์ PFX"]
certutil["certutil"]
code_signing_cert["ใบรับรองลงนามโค้ด"]
trusted_publisher_store["ที่เก็บผู้เผยแพร่ที่เชื่อถือ"]
desktop_app["แอปเดสก์ท็อปของผู้ใช้โต้ตอบ"]
cert_renewal["การสลับใบรับรอง"]
cert_ledger["บัญชีใบรับรอง"]
self_signed_cert["ใบรับรองที่ลงนามเองแบบเฉพาะหน้า"]
trust_anchor_risk["ความเสี่ยงการใช้จุดยึดความเชื่อถือในทางที่ผิด"]
cert_selection["การเลือกใบรับรอง (ค้นหา)"]
personal_store["ที่เก็บ «ส่วนบุคคล» (My)"]
store_mismatch["การสับสนที่เก็บ"]
iis_apppool["พูลแอปพลิเคชัน IIS"]
task_scheduler["การรันอัตโนมัติของ Task Scheduler"]
exportable_import["การนำเข้าแบบส่งออกได้"]
key_exfiltration_risk["ความเสี่ยงการนำคีย์ลับออก"]
everyone_full_control["การให้ Full Control แก่ Everyone"]
partner_registration["การลงทะเบียนใบรับรองล่วงหน้ากับปลายทาง"]
cert_switchover["การสลับไปใบรับรองใหม่"]
x509store["คลาส X509Store"]
validonly_search["การค้นใบรับรองที่ระบุ validOnly"]
key_usage["แอตทริบิวต์ Key Usage"]
config_externalization["การดึง thumbprint ออกไปโครง"]
cert_choice_logging["การเขียนบันทึกใบรับรองที่เลือก"]
windows_service -.->|"ใช้"| localmachine_store
client_certificate -->|"ต้องมี"| private_key
windows_service -.->|"ต้องมี"| private_key_acl
certificate_chain -.->|"ต้องมี"| intermediate_ca
certificate_chain -->|"ต้องมี"| root_ca
certificate_expiry -.->|"อาจก่อให้เกิด"| certificate_failure
thumbprint_hardcode -.->|"อาจก่อให้เกิด"| certificate_failure
certificate_failure -.->|"ตรวจยืนยันด้วย"| certificate_chain
root_ca -.->|"กำหนดค่าด้วย"| group_policy
root_ca -.->|"กำหนดค่าด้วย"| intune
currentuser_store -->|"ตรวจยืนยันด้วย"| certmgr_msc
localmachine_store -->|"ตรวจยืนยันด้วย"| certlm_msc
certificate_store -->|"ตรวจยืนยันด้วย"| cert_drive
certificate_expiry -->|"ตรวจยืนยันด้วย"| cert_drive
private_key -.->|"ถูกเก็บใน"| pfx
certificate_chain -->|"ตรวจยืนยันด้วย"| certutil
code_signing_cert -.->|"ถูกเก็บใน"| trusted_publisher_store
desktop_app -.->|"ใช้"| currentuser_store
root_ca -.->|"ถูกเก็บใน"| localmachine_store
private_key_acl -.->|"กำหนดค่าด้วย"| certlm_msc
cert_renewal -.->|"ป้องกัน"| certificate_failure
cert_ledger -.->|"ป้องกัน"| certificate_expiry
self_signed_cert -.->|"อาจก่อให้เกิด"| trust_anchor_risk
thumbprint_hardcode -->|"ไม่แนะนำให้ใช้กับ"| cert_selection
self_signed_cert -->|"ไม่แนะนำให้ใช้กับ"| root_ca
cert_ledger -->|"แนวทางที่แนะนำสำหรับ"| certificate_expiry
currentuser_store -->|"สืบทอดเนื้อหาของ"| localmachine_store
client_certificate -->|"ถูกเก็บใน"| personal_store
store_mismatch -->|"อาจก่อให้เกิด"| certificate_failure
iis_apppool -.->|"ใช้"| localmachine_store
iis_apppool -->|"ต้องมี"| private_key_acl
task_scheduler -.->|"ใช้"| localmachine_store
exportable_import -->|"อาจก่อให้เกิด"| key_exfiltration_risk
everyone_full_control -->|"ไม่แนะนำให้ใช้กับ"| private_key_acl
cert_renewal -.->|"ต้องมี"| private_key_acl
partner_registration -.->|"ควรทำก่อน"| cert_switchover
x509store -->|"ใช้"| certificate_store
validonly_search -.->|"บรรเทา"| certificate_failure
client_certificate -.->|"ต้องมี"| key_usage
config_externalization -->|"แนวทางที่แนะนำสำหรับ"| cert_selection
cert_choice_logging -->|"แนวทางที่แนะนำสำหรับ"| certificate_failure
ในแผนภาพ เส้นทึบหมายถึงความสัมพันธ์ที่ถือเสมอ และเส้นประหมายถึงความสัมพันธ์ที่มีเงื่อนไข (เงื่อนไขอยู่ในการอธิบายของแต่ละความสัมพันธ์ในหน้าละเอียด) รายการความสัมพันธ์ทั้งหมด (รวม 41 รายการ พร้อมหลักฐานและระดับความเชื่อมั่น) และนิยามของแนวคิดหลักรวบรวมไว้ที่ หน้าละเอียดของแผนที่ความรู้ (เป็นภาษาญี่ปุ่น) ข้อมูล: JSON-LD / Turtle
2. ภาพรวมที่เก็บใบรับรอง — สองตำแหน่งกับที่เก็บตรรกะ
2.1. สองสายของผู้ใช้กับคอมพิวเตอร์
ที่เก็บใบรับรองของ Windows แยกเป็นสอง «ตำแหน่ง» ใหญ่1
- ที่เก็บใบรับรองของคอมพิวเตอร์ (คอมพิวเตอร์ท้องถิ่น, LocalMachine): มีหนึ่งต่อพีซี และ ร่วมกันทุกผู้ใช้และบริการบนพีซี ตัวจริงอยู่ใต้
HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificatesในรีจิสทรี2 - ที่เก็บใบรับรองของผู้ใช้ (ผู้ใช้ปัจจุบัน, CurrentUser): คนละชุดต่อบัญชีผู้ใช้ ตัวจริงอยู่ใต้
HKEY_CURRENT_USER\Software\Microsoft\SystemCertificatesคือส่วนหนึ่งของโปรไฟล์ผู้ใช้2
นอกจากนี้ยังมีที่เก็บต่อบัญชีบริการ3 ตัวจริงเป็นคีย์รีจิสทรีต่อชื่อบริการ2 สิ่งที่ควรจับก่อนในงานปฏิบัติคือสองอย่างแรก
มีสเปกสำคัญอย่างหนึ่ง ที่เก็บตรรกะแต่ละอันของที่เก็บผู้ใช้ ยกเว้น «ส่วนบุคคล» สืบทอดเนื้อหาของที่เก็บชื่อเดียวกันในที่เก็บคอมพิวเตอร์แล้วแสดง1 ตัวอย่างเช่น ถ้าใส่ใบรับรอง CA ในองค์กรลง «ผู้ออกใบรับรองรากที่เชื่อถือ» ของที่เก็บคอมพิวเตอร์ ใบรับรองนั้นจะปรากฏใน «ผู้ออกใบรับรองรากที่เชื่อถือ» ของทุกผู้ใช้ด้วย พูอีกอย่าง ที่เก็บ «ส่วนบุคคล» อย่างเดียวไม่ถูกสืบทอด ดังนั้นใบรับรองลูกข่าย (= สิ่งที่ใส่ในที่เก็บส่วนบุคคล) ต้องตัดสินเองว่า «ต้องให้ใครเห็นได้» ความไม่สมมาตรนี้คือตัวเอกของทั้งบทความ
flowchart TB
accTitle: ที่เก็บคอมพิวเตอร์กับที่เก็บผู้ใช้
accDescr: ที่เก็บคอมพิวเตอร์มีหนึ่งต่อพีซีและร่วมกันทุกผู้ใช้กับบริการ ส่วนที่เก็บผู้ใช้คนละชุดต่อบัญชี และนอกจากส่วนบุคคลจะสืบทอดเนื้อจากฝั่งคอมพิวเตอร์แล้วแสดง
subgraph LM["คอมพิวเตอร์ (LocalMachine)<br/>หนึ่งต่อพีซี・ร่วมกันทุกผู้ใช้และบริการ"]
LMMY["ส่วนบุคคล (My)"]
LMROOT["ผู้ออกใบรับรองรากที่เชื่อถือ (Root)"]
LMCA["ผู้ออกใบรับรองระดับกลาง (CA)"]
LMTP["ผู้เผยแพร่ที่เชื่อถือ (TrustedPublisher)"]
end
subgraph CU["ผู้ใช้ (CurrentUser)<br/>คนละชุดต่อบัญชี"]
CUMY["ส่วนบุคคล (My)<br/>※ไม่ถูกสืบทอด=ที่ที่ต้องตัดสินเองว่าจะใส่"]
CUROOT["ผู้ออกใบรับรองรากที่เชื่อถือ (Root)"]
CUCA["ผู้ออกใบรับรองระดับกลาง (CA)"]
CUTP["ผู้เผยแพร่ที่เชื่อถือ (TrustedPublisher)"]
end
LMROOT -.->|"สืบทอดเนื้อแล้วเห็น"| CUROOT
LMCA -.->|"สืบทอด"| CUCA
LMTP -.->|"สืบทอด"| CUTP
ภาพ 1: ที่เก็บผู้ใช้สืบทอดเนื้อของที่เก็บคอมพิวเตอร์ยกเว้นส่วนบุคคล จึงต้องตัดสินเองว่าจะใส่ใบรับรองลูกข่ายที่ไหน.
2.2. ที่เก็บตรรกะหลัก
ภายในแต่ละตำแหน่งแบ่งเป็นที่เก็บตรรกะตามบทบาท โฟลเดอร์ที่เห็นใน certmgr.msc / certlm.msc คือสิ่งนั้น และเมื่อดูจาก PowerShell หรือคำสั่งใช้ชื่อภายในภาษาอังกฤษ24
| ชื่อที่แสดง | ชื่อภายใน | เป็นที่ใส่อะไร |
|---|---|---|
| ส่วนบุคคล | My | ใบรับรองที่ตนเอง (พีซีนี้・ผู้ใช้นี้) ใช้ ใบรับรองลูกข่ายและใบรับรองเซิร์ฟเวอร์อยู่ตรงนี้ ที่ผูกกับคีย์ลับก็ตรงนี้ |
| ผู้ออกใบรับรองรากที่เชื่อถือ | Root | ใบรับรอง CA รากที่เป็นจุดตั้งต้นของความเชื่อถือ ภายใต้ CA ที่ใส่ที่นี่จะ «ถูกเชื่อถือ» |
| ผู้ออกใบรับรองระดับกลาง | CA | ใบรับรอง CA กลางที่เชื่อมระหว่างรากกับปลาย เป็นวัสดุสร้างโซ่ |
| ผู้เผยแพร่ที่เชื่อถือ | TrustedPublisher | ใบรับรองที่เชื่อถือในฐานะผู้เผยแพร่ซอฟต์แวร์ที่ลงนามแล้ว (หมวด 8) |
2.3. สามหน้าต่างมอง — certmgr.msc / certlm.msc / ไดรฟ์ Cert:
- certmgr.msc: คอนโซลจัดการที่เปิดที่เก็บของผู้ใช้ปัจจุบัน
- certlm.msc: คอนโซลจัดการที่เปิดที่เก็บของคอมพิวเตอร์ท้องถิ่น
- ไดรฟ์
Cert:ของ PowerShell: จัดการที่เก็บแบบระบบไฟล์ด้วยชั้นCert:\CurrentUser\...และCert:\LocalMachine\...ใบรับรองถูกระบุด้วย thumbprint
อนึ่ง เมื่อเพิ่มสแนปอินใบรับรองใน mmc.exe ด้วยมือ ให้เลือกเป้าหมายจากสามชนิด คือ «บัญชีผู้ใช้» «บัญชีคอมพิวเตอร์» «บัญชีบริการ» ผู้ใช้ที่ไม่ใช่ผู้ดูแลจัดการได้แค่ที่เก็บของบัญชีผู้ใช้ตนเอง3
ขั้นแรกของการสอบสวนปัญหาคือ ทำให้ «แอปกำลังมองที่เก็บใด» กับ «ตนเองกำลังมองที่เก็บใด» ตรงกัน ต่อให้สำรวจความล้มเหลวของบริการขณะมอง certmgr.msc ที่มองอยู่คนละที่ คำตอบจะไม่ออกตลอดกาล
3. จะใส่ที่ไหน — ตารางตัดสินจากรูปแบบการรันของโปรแกรม
เกณฑ์ตัดสินมีอย่างเดียว โปรแกรมที่ใช้ใบรับรองนั้น ทำงานด้วยบัญชีของใคร
| รูปแบบการรัน | บัญชีที่รัน | ที่เก็บที่ใส่ | หมายเหตุ |
|---|---|---|---|
| แอปเดสก์ท็อปที่ผู้ใช้โต้ตอบเป็นผู้เริ่ม | ผู้ใช้ที่ลงชื่อเข้าใช้เอง | ผู้ใช้ (Cert:\CurrentUser\My) | ต้องนำเข้าใช้ทีละบัญชีของคนที่ใช้ หากพีซีร่วมที่หลายคนใช้ ให้พิจารณาที่เก็บคอมพิวเตอร์ด้วย |
| บริการ Windows | LocalSystem / NETWORK SERVICE / บัญชีบริการเฉพาะ | คอมพิวเตอร์ (Cert:\LocalMachine\My) | นอก LocalSystem (NETWORK SERVICE, บัญชีเฉพาะ ฯลฯ) ต้องให้สิทธิ์อ่านคีย์ลับ (หมวด 4) LocalSystem อ่านได้ด้วยสิทธิ์ SYSTEM ตามค่าเริ่มต้น |
| เว็บแอปบน IIS | รหัสประจำตัวของพูลแอป | คอมพิวเตอร์ | เช่นเดียวกับบน |
| การรันไร้คนของ Task Scheduler (รันไม่ว่าผู้ใช้ลงชื่อเข้าใช้หรือไม่) | บัญชีที่ระบุในงาน | แนะนำคอมพิวเตอร์ | รันด้วยที่เก็บผู้ใช้ของบัญชีที่รันก็ได้ แต่ได้แค่เพิ่มการตรวจโปรไฟล์และวิธีที่ที่เก็บถูกเห็น โดยประโยชน์น้อย |
| การยื่นอิเล็กทรอนิกส์และการรับรองเว็บในเบราว์เซอร์ | ผู้ใช้ที่ลงชื่อเข้าใช้เอง | ผู้ใช้ | เป็นธรรมชาติในความหมายว่าไม่ให้คนอื่นนอกผู้ที่ถูกแจกใช้ |
ถ้าลังเล สิ่งที่ทำงานไร้คนคือที่เก็บคอมพิวเตอร์ สิ่งที่คนใช้งานคือที่เก็บผู้ใช้
3.1. ผ่าอุบัติเหตุประจำ — «ตอนพัฒนางาน พอทำให้เป็นบริการแล้วหาไม่เจอ»
อุบัติเหตุนี้ทำซ้ำได้อย่างแม่นด้วยขั้นตอนต่อไปนี้
- นักพัฒนาดับเบิลคลิก pfx บนพีซีตนเองแล้วนำเข้า ค่าเริ่มต้นของตัวช่วยคือ «ผู้ใช้ปัจจุบัน» ดังนั้นใบรับรองเข้า ที่เก็บผู้ใช้ของบัญชีนักพัฒนา
- แอปตอนพัฒนาทำงานจาก Visual Studio คือด้วยบัญชีนักพัฒนา จึงเปิด
StoreLocation.CurrentUserแล้วเจอใบรับรอง ทำงาน - ลงทะเบียนเป็นบริการ Windows บนเซิร์ฟเวอร์งานจริง บริการทำงานด้วย NETWORK SERVICE หรือบัญชีเฉพาะ
CurrentUserที่โค้ดของบริการเปิดคือ ที่เก็บผู้ใช้ของบัญชีที่รันบริการ ที่นั่นว่าง «หาใบรับรองไม่พบ»
flowchart TB
accTitle: ตอนพัฒนางาน พอทำให้เป็นบริการแล้วหาไม่เจอ
accDescr: ใบรับรองที่ใส่ในที่เก็บผู้ใช้ของนักพัฒนา มองไม่เห็นจาก CurrentUser ของบริการที่ทำงานด้วยบัญชีอื่น
subgraph DEV["เครื่องพัฒนา"]
D1["นำเข้าด้วยการดับเบิลคลิก pfx<br/>ค่าเริ่มต้นของตัวช่วยคือ «ผู้ใช้ปัจจุบัน»"] --> D2["เข้าที่เก็บผู้ใช้ของ<br/>บัญชีนักพัฒนา"]
D2 --> D3["รันจาก Visual Studio<br/>=ทำงานด้วยบัญชีนักพัฒนา"]
D3 --> D4["เปิด CurrentUser แล้วเจอ<br/>→ ทำงาน"]
end
subgraph PROD["เซิร์ฟเวอร์งานจริง"]
P1["ลงทะเบียนเป็นบริการ Windows<br/>บัญชีที่รันคือ NETWORK SERVICE ฯลฯ"] --> P2["CurrentUser ที่โค้ดเปิดคือ<br/>ที่เก็บผู้ใช้ของบัญชีบริการ"]
P2 --> P3["ที่นั่นว่าง<br/>→ «หาใบรับรองไม่พบ»"]
end
D4 -.->|"วางโปรแกรมเดียวกัน"| P1
ภาพ 2: ที่เก็บผู้ใช้ของนักพัฒนากับที่เก็บผู้ใช้ของบัญชีบริการคนละชุด การทำงานตอนพัฒนาไม่รับประกันว่าจะเจอในงานจริง.
จุดคือที่เก็บผู้ใช้ «มีเท่าจำนวนบัญชี» ต่อให้ผู้ดูแลเปิด certmgr.msc แล้วยืนยันว่า «ใส่แล้วนะ?» นั่นคือที่เก็บของผู้ดูแลเอง ไม่ใช่ที่เก็บของบัญชีบริการ การรับมือไม่ใช่การคัดลอกเฉพาะหน้า แต่คือ ใส่ใหม่ในที่เก็บคอมพิวเตอร์ และจับโค้ดให้ตรง StoreLocation.LocalMachine และการให้สิทธิ์ในหมวดถัดไปเป็นชุดเดียวกัน
4. คีย์ลับกับสิทธิ์การเข้าถึง — อุบัติเหตุประจำที่สอง
4.1. ใบรับรองกับคีย์ลับคนละอย่าง
สิ่งที่เห็นในรายการที่เก็บใบรับรองคือใบรับรอง (ข้อมูลสาธารณะ) ไม่ใช่ตัวคีย์ลับ สิ่งที่จำเป็นจริงในการรับรองลูกข่ายคือการประมวลผลลายเซ็นด้วยคีย์ลับ ดังนั้น «เห็นในรายการ» กับ «ใช้ได้» คนละปัญหา หากปนตรงนี้ จะได้ความล้มเหลวที่ดูยากอย่าง «มีใบรับรองแต่แฮนด์เชค TLS ล้ม» «ข้อผิดพลาดภายในตระกูล Access Denied»
4.2. งานปฏิบัตินำเข้า pfx — การส่งออกได้หรือไม่คือการตัดสินใจ
คู่ใบรับรองกับคีย์ลับถูกส่งต่อด้วยไฟล์ pfx (PKCS #12) และนำเข้าที่เก็บด้วย Import-PfxCertificate6
$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 ฯลฯ ต้องให้สิทธิ์อ่านอย่างชัดแก่บัญชีที่รัน ขั้นตอนทำจาก UI ของสแนปอินใบรับรองได้5
- เปิด certlm.msc (หรือสแนปอินใบรับรองที่เป้าหมายเป็นบัญชีคอมพิวเตอร์)
- คลิกขวาใบรับรองเป้าหมายใน «ส่วนบุคคล» → «ใบรับรอง» แล้วเปิด «จัดการคีย์ลับ» จาก «งานทั้งหมด»
- ในแท็บ «ความปลอดภัย» เพิ่มบัญชีที่รัน (NETWORK SERVICE, บัญชีบริการเฉพาะ, รหัสประจำตัวพูลแอป IIS ฯลฯ) แล้วอนุญาต «อ่าน»5
ไม่ต้องมี Full Control ถ้าใช้แค่ลงนาม อ่านก็พอ ในทางกลับกัน การให้ Full Control แก่ Everyone เพราะไม่ทำงาน คือการลดคีย์ลับให้ถูกปฏิบัติเทียบรหัสผ่านข้อความธรรมดา จึงห้ามเด็ดขาด การวางในที่เก็บคอมพิวเตอร์กับการให้สิทธิ์คีย์ลับเป็นชุดเดียวกันเสมอ ── แค่เขียนนี้ในสมุดขั้นตอน อุบัติเหตุสายนี้หาย
5. กันอุบัติเหตุวันหมดอายุ — การทำรายการ การสลับ และบัญชี
5.1. ทำรายการด้วย PowerShell
วันหมดอายุของใบรับรองอยู่ในคุณสมบัติ NotAfter ทำรายการแบบเครื่องกลด้วย Get-ChildItem ต่อไดรฟ์ Cert: ได้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. ขั้นตอนการสลับ — ช่วงคู่ขนานเก่าใหม่กับกับดัก thumbprint
การต่ออายุใบรับรองไม่ใช่ «ลบแล้วใส่» แต่คือ «เพิ่มแล้วสลับ ยืนยันแล้วค่อยลบ»
- นำเข้าใบรับรองใหม่ (pfx) ไปที่เก็บเดียวกัน thumbprint ต่างกัน ดังนั้นเก่าใหม่ร่วมอยู่ในที่เก็บเดียวกันได้
- ให้สิทธิ์คีย์ลับของใบรับรองใหม่ (หมวด 4) จุดที่ลืมง่ายตอนต่ออายุคือตรงนี้ สิทธิ์ติดทีละคีย์ลับของใบรับรอง ดังนั้นพอสลับใบรับรองต้องให้ใหม่ด้วย
- การแจ้งระบบปลายทาง (เมื่อเป็น API ที่ต้องลงทะเบียนใบรับรองล่วงหน้า) ให้ทำ ขณะยังดำเนินงานด้วยใบรับรองเก่า ก่อน เพื่อให้มีช่วงคู่ขนานที่รับได้ทั้งเก่าใหม่ ถ้าสลับก่อน ฝั่งปลายทางปฏิเสธใบรับรองใหม่แล้วการสื่อสารงานจริงหยุด
- สลับโครงของแอปไปใบรับรองใหม่ แล้วตรวจการทำงาน
- หลังช่วงที่พอ ลบใบรับรองเก่า
flowchart LR
accTitle: การต่ออายุใบรับรองคือเพิ่มแล้วสลับ
accDescr: นำเข้า pfx ใหม่ไปที่เก็บเดียวกัน ให้สิทธิ์คีย์ลับ ลงทะเบียนล่วงหน้ากับปลายทาง แล้วสลับ thumbprint และหลังช่วงคู่ขนานลบใบรับรองเก่า
I["1. นำเข้า pfx ใหม่ไปที่เก็บเดียวกัน<br/>(เก่าใหม่อยู่ร่วม)"] --> P["2. ให้สิทธิ์คีย์ลับของ<br/>ใบรับรองใหม่"]
P --> R["3. ลงทะเบียนล่วงหน้ากับปลายทาง<br/>(ดำเนินงานด้วยใบรับรองเก่าต่อไป)"]
R --> SW["4. เขียน thumbprint ในโครงใหม่แล้ว<br/>สลับ・ตรวจการทำงาน"]
SW --> DEL["5. หลังช่วงคู่ขนาน<br/>ลบใบรับรองเก่า"]
ภาพ 3: ลำดับการสลับใบรับรองคือเพิ่ม แล้วให้สิทธิ์ แล้วลงทะเบียนล่วงหน้า แล้วสลับโครง แล้วลบของเก่า และอย่าลืมอัปเดต thumbprint.
กับดักใหญ่สุดตอนนี้คือ thumbprint ที่เขียนในไฟล์โครงหรือโค้ด thumbprint ไม่ซ้ำต่อใบรับรอง ดังนั้นพอต่ออายุย่อมเปลี่ยน หากที่ใดที่หนึ่งยังอ้างอิง thumbprint เก่า จะเกิด «ต่ออายุใบรับรองแล้วแต่เชื่อมไม่ได้» ทางชัวร์คือจัดการด้วยบัญชีว่า thumbprint ถูกเขียนที่ไหน (โครงแอป การผูก IIS สคริปต์ การแจ้งปลายทาง)
5.3. แนะนำบัญชีใบรับรอง
แม้เรียกบัญชี แค่ Excel หนึ่งแผ่นก็พอ อย่างน้อยสร้างคอลัมน์ การใช้/ผู้ออก/subject/thumbprint/ตำแหน่งที่อยู่ (ชื่อเซิร์ฟเวอร์+ที่เก็บ)/บัญชีที่มีสิทธิ์คีย์ลับ/วันหมดอายุ/ลิงก์ไปขั้นตอนต่ออายุ/ผู้รับผิดชอบ แล้วเทียบกับผลทำรายการใน 5.1 ความเป็นจริงของอุบัติเหตุใบรับรองไม่ใช่ปัญหาทางเทคนิค แต่เป็นปัญหา «ไม่มีใครถือรายการ» ดังนั้นบัญชีได้ผลที่สุด
6. การถอดการตรวจและการล้มเหลว — โซ่กับการแจกราก
6.1. พื้นฐานการตรวจโซ่กับ certutil
ข้อผิดพลาดตระกูล «ใบรับรองนี้ไม่ถูกเชื่อถือ» คือสถานะที่ โซ่ (เส้นทางพิสูจน์) จากใบรับรองปลายถึง CA รากขาดที่ไหนสักแห่ง การแยกสะดวกด้วย certutil7
flowchart TB
accTitle: สามสาเหตุประจำที่การตรวจโซ่ขาด
accDescr: ข้อผิดพลาดความเชื่อถือเกิดจากอย่างใดอย่างหนึ่ง คือหาใบรับรอง CA กลางไม่ได้ รากยังไม่ถูกแจก หรือใบรับรองปลายหมดอายุ
LEAF["ใบรับรองปลาย<br/>(ใบรับรองลูกข่าย・ใบรับรองเซิร์ฟเวอร์)"] --> INT["ใบรับรอง CA กลาง<br/>ที่วาง: ที่เก็บผู้ออกใบรับรองระดับกลาง (CA)"]
INT --> ROOT["ใบรับรอง CA ราก<br/>ที่วาง: ผู้ออกใบรับรองรากที่เชื่อถือ (Root)"]
INT -.->|"หาไม่ได้<br/>(ไม่มีการเสนอ・AIA・ที่เก็บใดเลย)"| E1["สร้างโซ่ไม่ได้<br/>(สาเหตุประจำ 1)"]
ROOT -.->|"ยังไม่ถูกแจก"| E2["ข้อผิดพลาด «ไม่ถูกเชื่อถือ»<br/>(สาเหตุประจำ 2)"]
LEAF -.->|"หมดอายุ"| E3["ข้อผิดพลาดช่วงที่ใช้ได้<br/>(สาเหตุประจำ 3)"]
ภาพ 4: โซ่ขาดเมื่อหา CA กลางหรือรากไม่ได้ หรือเพราะช่วงที่ใช้ได้ ให้ระบุชั้นด้วย certutil.
:: สร้างและตรวจโซ่ของไฟล์ใบรับรอง (พร้อมดึง 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 ในองค์กรหรือใบรับรองที่ลงนามเองสำหรับตรวจ ต้องแจกใบรับรองรากนั้นไปแต่ละพีซี อย่าใส่ทีละเครื่องด้วยมือ แต่ขึ้นกลไกการแจก
- สภาพแวดล้อม Active Directory (GPO): นำเข้าใบรับรองไป «ผู้ออกใบรับรองรากที่เชื่อถือ» ใน
Computer Configuration\Policies\Windows Settings\Security Settings\Public Key Policiesของ Group Policy แล้วจะถูกแจกไปพีซีเป้าหมาย8 - สภาพแวดล้อมที่จัดการด้วย Intune: แจกใบรับรอง CA ราก/กลางด้วยโปรไฟล์ «ใบรับรองที่เชื่อถือ» บน Windows เลือกที่เก็บปลายทางได้ (ราก/กลางของคอมพิวเตอร์ กลางของผู้ใช้)9
ดังที่กล่าวใน 2.1 ถ้าใส่รากของที่เก็บคอมพิวเตอร์ ทุกผู้ใช้เชื่อถือ1 เพราะฉะนั้นยิ่งต้องมองความเสี่ยงกลับ การดำเนินงานที่ใส่ใบรับรองที่ลงนามเองลง «ผู้ออกใบรับรองรากที่เชื่อถือ» คือการปลูกจุดตั้งต้นความเชื่อถือใหม่บนพีซีนั้น หากคีย์ลับนั้นรั่ว จะเป็นฐานให้ถูกออกใบรับรองที่ปลอมเป็นไซต์หรือซอฟต์แวร์ใดก็ได้ หากจะทำให้เป็นการดำเนินงานถาวร ทางถูกคือตั้ง CA ในองค์กรที่ปกป้องคีย์ลับอย่างเหมาะสม หรือดึงไปใบรับรองของ CA สาธารณะ และรากที่ลงนามเองโดยหลักคือ «จำกัดสภาพแวดล้อมตรวจ・ตัดเส้นหมดอายุ»
7. มุมนักพัฒนา — ใช้ที่เก็บจาก .NET ให้ถูก
7.1. ค้นด้วย thumbprint ผ่าน X509Store
จาก .NET เปิดที่เก็บด้วย X509Store แล้วดึงใบรับรองด้วย Find1011
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={thumbprint}, " +
$"ตำแหน่ง={store.Location}\\{store.Name}");
var cert = found[0];
if (!cert.HasPrivateKey)
throw new InvalidOperationException(
$"มีใบรับรองแต่ไม่ได้ผูกคีย์ลับ (เช่น การนำเข้า" +
$"จาก .cer): thumbprint={thumbprint}, ตำแหน่ง={store.Location}\\{store.Name}");
return cert;
}
การตัดสินหมวด 3 เชื่อมตรงนี้ โค้ดที่ทำงานเป็นบริการใช้ StoreLocation.LocalMachine แอปโต้ตอบใช้ StoreLocation.CurrentUser อีกอย่าง ระวังอาร์กิวเมนต์ที่สาม validOnly ของ Find 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. ปัญหา thumbprint ที่ฮาร์ดโค้ดตายตอนสลับ
การค้นด้วย thumbprint ชัวร์ แต่ถ้าฝัง thumbprint ในโค้ด ทุกครั้งที่ต่ออายุใบรับรองต้องบิลด์และปล่อย การรับมือในการออกแบบมีสามขั้น
- อย่างน้อย: ดึง thumbprint ออกไปไฟล์โครง (appsettings ฯลฯ) ให้สลับได้โดยไม่ปล่อย จุดโครงให้อยู่ในบัญชีของ 5.3
- ก้าวขึ้น: ค้นด้วยชื่อ subject หรือผู้ออก แล้วรวม
validOnly: trueเลือก «ในที่ที่ใช้ได้ตอนนี้ด้วยชื่อนั่น อันที่NotAfterไกลสุด» ในช่วงคู่ขนานเก่าใหม่จะสลับไปใบรับรองใหม่อัตโนมัติ แต่มีความเสี่ยงจับใบรับรองชื่อเดียวกันที่ไม่ได้ตั้งใจ จึงจับคู่การยืนยันผู้ออกกับการเขียนบันทึก นอกจากนี้ การสลับอัตโนมัตินี้ตั้งได้ เฉพาะเมื่อปลายทางไม่ต้องลงทะเบียนใบรับรองล่วงหน้า สำหรับ API ที่ต้องลงทะเบียนล่วงหน้า (5.2) อาจสลับไปใบรับรองที่นำเข้าอย่างเดียวยังไม่ลงทะเบียนแล้วการสื่อสารหยุด จึงจำกัดไว้ที่วิธีดึงโครงออกซึ่งสลับหลังยืนยันการลงทะเบียนเสร็จ - รัดด้วยงาน: ไม่ว่าวิธีใด เหลือในบันทึกตอนเริ่มว่า «เลือกใบรับรองใด (thumbprint・วันหมดอายุ)» ทั้งสอบสวนความล้มเหลวและการเทียบบัญชี บรรทัดนี้ได้ผล
8. ความสัมพันธ์กับใบรับรองลงนามโค้ด — ที่เก็บ «ผู้เผยแพร่ที่เชื่อถือ»
ที่ผ่านมาเป็นใบรับรองเพื่อการสื่อสาร (TLS) แต่ที่เก็บใบรับรองยังมีอีกโลกหนึ่งอยู่ร่วม ── การลงนามโค้ด ที่เก็บ «ผู้เผยแพร่ที่เชื่อถือ (TrustedPublisher)» ที่ปรากฏในตาราง 2.2 คือจุดต่อ และเป็นที่ลงทะเบียนใบรับรองผู้เผยแพร่ซอฟต์แวร์ที่ลงนามแล้วเป็นที่เชื่อถือ มีทั้งตำแหน่งผู้ใช้และคอมพิวเตอร์10 และถูกใช้ในงานอย่างแจกผู้เผยแพร่ของแอปที่แจกในองค์กรไป TrustedPublisher ของแต่ละพีซีด้วย GPO
ในฐานะ «ฝั่งแจก» แอป หากต้องรับมือการลงนามโค้ดหรือคำเตือน SmartScreen («พีซีถูกปกป้องโดย Windows») สรุปในบทความแยก «เหตุผลที่ Windows แสดง «พีซีถูกปกป้องโดย Windows»» ความรู้ในบทความนี้ (สองสายของที่เก็บ การแจกราก) ใช้เป็นความรู้ข้อสมมติได้ตามเดิม
9. สรุป
- ที่เก็บใบรับรองมีสองสาย คือผู้ใช้ (CurrentUser) กับคอมพิวเตอร์ (LocalMachine) certmgr.msc / certlm.msc / ไดรฟ์
Cert:คือสามหน้าต่างที่มองสิ่งเดียวกัน ขั้นแรกของการสอบสวนคือจับให้ตรงว่า «กำลังพูดถึงที่เก็บใด» - ที่ใส่ตัดสินจาก «โปรแกรมทำงานในฐานะใคร» การรันไร้คน (บริการ・IIS・งาน) โดยหลักคือที่เก็บคอมพิวเตอร์ แอปโต้ตอบคือที่เก็บผู้ใช้
- «ตอนพัฒนางาน แต่ตอนงานจริงหาไม่เจอ» สาเหตุคือที่เก็บผู้ใช้ของนักพัฒนากับที่เก็บผู้ใช้ของบัญชีบริการคนละชุด แก้ด้วยการจับให้ตรงที่เก็บคอมพิวเตอร์ +
StoreLocation.LocalMachine - การวางในที่เก็บคอมพิวเตอร์กับการให้สิทธิ์อ่านใน «จัดการคีย์ลับ» เป็นชุดเดียวกัน อย่าลืมให้ใหม่ตอนต่ออายุ
- การนำเข้า pfx ตามค่าเริ่มต้นส่งออกไม่ได้
-Exportableเฉพาะเมื่อจำเป็นจริง รวมการเก็บ pfx ต้นฉบับกับรหัสผ่านในการออกแบบ - วันหมดอายุกันด้วยการทำรายการเป็นระยะของ
Get-ChildItem Cert: ... -ExpiringInDaysและบัญชีใบรับรอง การสลับเดินตามลำดับ «เพิ่ม→สลับ→ยืนยัน→ลบ» ระวังการอัปเดต thumbprint ในโครงที่ตกหล่น - การแยกโซ่ใช้
certutil -urlfetch -verifyแจกรากของ CA ในองค์กรด้วย GPO/Intune และการดำเนินงานที่ใส่ที่ลงนามเองลงรากจำกัดสภาพแวดล้อมตรวจและมีเส้นหมดอายุ - ในโค้ดดึง thumbprint ออกไปโครง และเหลือใบรับรองที่เลือกในบันทึก แค่นั้นการรับมือความล้มเหลวจากใบรับรองเปลี่ยนหน้า
บทความที่เกี่ยวข้อง
- เหตุผลที่ Windows แสดง «พีซีถูกปกป้องโดย Windows»
- การเก็บข้อมูลลับของแอป Windows — เลี่ยงการตั้งข้อความธรรมดาด้วย DPAPI
- การจัดการข้อมูลรับรองอย่างปลอดภัยใน PowerShell — ไล่รหัสผ่านข้อความธรรมดาออกจากสคริปต์
- ห้ามล้อม HttpClient ด้วย using — งานปฏิบัติการสื่อสาร HTTP ของแอปธุรกิจ C#
- เมื่อแตะบัตรประกัน My Number เกิดอะไร — อ่านการเชื่อมการยืนยันสิทธิ์ออนไลน์กับเครื่องคิดค่ารักษาจากซอร์สของ ORCA
ด้านให้คำปรึกษาที่เกี่ยวข้อง
KomuraSoft LLC รับพัฒนาแอปธุรกิจที่ฝังการเชื่อม Web API ที่มีใบรับรองลูกข่าย (API ตระกูลธนาคาร การยืนยันสิทธิ์ออนไลน์ ฯลฯ) การสอบสวนความล้มเหลวแบบ «หาใบรับรองไม่พบ» «ต่ออายุแล้วเชื่อมไม่ได้» และการจัดขั้นตอนสลับใบรับรอง คำปรึกษาขั้นยังไม่รู้ว่าควรมองที่เก็บตรงไหนก็ได้
ลิงก์อ้างอิง
-
Microsoft Learn, Local Machine and Current User Certificate Stores. ว่าด้วยที่เก็บใบรับรองของคอมพิวเตอร์ที่เป็นท้องถิ่นต่อพีซี ร่วมกันทุกผู้ใช้ และอยู่ใต้ HKEY_LOCAL_MACHINE ที่เก็บใบรับรองของผู้ใช้ที่เป็นต่อบัญชีผู้ใช้และอยู่ใต้ HKEY_CURRENT_USER และที่เก็บผู้ใช้ที่สืบทอดเนื้อที่เก็บคอมพิวเตอร์ยกเว้นที่เก็บ «ส่วนบุคคล» (ใบรับรองที่เพิ่มใน «ผู้ออกใบรับรองรากที่เชื่อถือ» ของคอมพิวเตอร์ปรากฏในที่เก็บชื่อเดียวกันของแต่ละผู้ใช้) ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System Store Locations. ว่าด้วยตำแหน่งรีจิสทรีของ CERT_SYSTEM_STORE_CURRENT_USER / CERT_SYSTEM_STORE_LOCAL_MACHINE (Software\Microsoft\SystemCertificates ของ HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE ตามลำดับ) ที่เก็บตรรกะที่นิยามไว้คือ MY, Root, Trust, CA ที่เก็บสำหรับบริการอยู่ที่คีย์รีจิสทรีต่อชื่อบริการ (Software\Microsoft\Cryptography\Services\ServiceName\SystemCertificates) และมีที่เก็บสำหรับแจกด้วย Group Policy แยก ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, How to: View certificates with the MMC snap-in. ว่าด้วย certlm.msc ที่เป็นเครื่องมือจัดการใบรับรองของอุปกรณ์ท้องถิ่น (คอมพิวเตอร์ท้องถิ่น) และ certmgr.msc ที่จัดการใบรับรองของผู้ใช้ปัจจุบัน เป้าหมายของสแนปอินใบรับรองมีสามชนิดคือ «บัญชีคอมพิวเตอร์» «บัญชีผู้ใช้» «บัญชีบริการ» และผู้ใช้ที่ไม่ใช่ผู้ดูแลจัดการได้แค่ใบรับรองของบัญชีผู้ใช้ตนเอง ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, about_Certificate_Provider. ว่าด้วยไดรฟ์ Cert: ของ PowerShell ที่เป็นเนมสเปซชั้นที่มีสองตำแหน่งที่เก็บคือ CurrentUser และ LocalMachine การแจกที่เก็บและใบรับรองด้วย Get-ChildItem พารามิเตอร์ -ExpiringInDays ที่คืนใบรับรองที่หมดอายุภายในจำนวนวันที่ระบุ (0 คือหมดอายุแล้ว) พารามิเตอร์พลวัตอย่าง -CodeSigningCert คุณสมบัติ NotAfter ที่เก็บวันหมดอายุ และใบรับรองที่ถูกระบุด้วย thumbprint ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, How to Modify Private Key Permissions to Support Management Server or Streaming Server. ว่าด้วยขั้นตอนเปิด «จัดการคีย์ลับ» (Manage Private Keys) ในสแนปอินใบรับรองที่เป้าหมายเป็นที่เก็บใบรับรองของคอมพิวเตอร์ท้องถิ่น แล้วเพิ่มสิทธิ์เข้าถึง «อ่าน» ให้บัญชีที่รันบริการ (เช่น Network Service) ในแท็บ «ความปลอดภัย» ↩ ↩2 ↩3
-
Microsoft Learn, Import-PfxCertificate. ว่าด้วย Import-PfxCertificate ที่นำใบรับรองและคีย์ลับจากไฟล์ PFX เข้าที่เก็บที่ระบุ หากไม่ระบุสวิตช์ -Exportable คีย์ลับที่ถูกนำเข้าจะส่งออกไม่ได้ และไวยากรณ์กับตัวอย่างการใช้ของพารามิเตอร์ -CertStoreLocation, -Password, -FilePath ↩ ↩2 ↩3
-
Microsoft Learn, certutil. ว่าด้วย certutil -verify ที่ตรวจใบรับรอง CRL และโซ่ใบรับรอง และถ้าไม่ระบุไฟล์ใบรับรอง CA จะสร้างโซ่เต็มแล้วตรวจ ตัวเลือก -urlfetch ที่ใช้ได้ และ certutil -store ที่ดัมพ์ที่เก็บใบรับรอง โดยตัวเลือก -user เข้าถึงที่เก็บผู้ใช้แทนที่เก็บคอมพิวเตอร์ ↩ ↩2
-
Microsoft Learn, Distribute Certificates to Client Computers by Using Group Policy. ว่าด้วยขั้นตอนนำเข้าใบรับรองไป «ผู้ออกใบรับรองรากที่เชื่อถือ» ใต้
Computer Configuration\Policies\Windows Settings\Security Settings\Public Key Policiesของ Group Policy แล้วแจกไปคอมพิวเตอร์ลูกข่ายในโดเมน และสิทธิ์ที่จำเป็น (เทียบ Domain Admins / Enterprise Admins) ↩ -
Microsoft Learn, Create trusted certificate profiles in Microsoft Intune. ว่าด้วยโปรไฟล์ «ใบรับรองที่เชื่อถือ» ของ Intune ที่เป็นกลไกแจกใบรับรอง CA รากหรือกลางไปอุปกรณ์ที่จัดการ ถูกใช้เป็นข้อสมมติของโปรไฟล์ใบรับรอง SCEP/PKCS เพื่อสร้างความเชื่อถือต่อ CA ราก และบน Windows เลือกที่เก็บปลายทางได้เป็น «ที่เก็บใบรับรองคอมพิวเตอร์ - ราก» «ที่เก็บใบรับรองคอมพิวเตอร์ - กลาง» «ที่เก็บใบรับรองผู้ใช้ - กลาง» ↩
-
Microsoft Learn, X509Store Class. ว่าด้วย X509Store ที่สร้างโดยระบุ StoreName และ StoreLocation (CurrentUser / LocalMachine) เปิดที่เก็บด้วยเมธอด Open และ OpenFlags (ReadOnly, OpenExistingOnly ฯลฯ) ดึงคอลเลกชันใบรับรองด้วยคุณสมบัติ Certificates ชื่อที่เก็บมาตรฐานรวม My, Root, CA, TrustedPublisher ฯลฯ และที่เก็บ TrustedPublisher มีทั้ง CurrentUser และ LocalMachine ↩ ↩2
-
Microsoft Learn, X509Certificate2Collection.Find(X509FindType, Object, Boolean) Method. ว่าด้วยเมธอด Find ที่ค้นใบรับรองด้วย X509FindType (FindByThumbprint ฯลฯ) และค่าค้น และเมื่อระบุอาร์กิวเมนต์ที่สาม validOnly เป็น true จะคืนเฉพาะใบรับรองที่ใช้ได้ที่ผ่านการตรวจ ↩ ↩2
-
Microsoft Learn, HttpClientHandler.ClientCertificates Property. ว่าด้วยคุณสมบัติ ClientCertificates ที่เป็น X509CertificateCollection ซึ่งถูกเสนอให้เซิร์ฟเวอร์ในการรับรองลูกข่ายฐานใบรับรอง และใน .NET Core หากใบรับรองมีแอตทริบิวต์ Key Usage ต้องมี «Digital Signature» ↩ ↩2
บทความที่เกี่ยวข้อง
บทความล่าสุดที่มีแท็กเดียวกัน เพื่อเจาะลึกหัวข้อใกล้เคียง
ไฟร์วอลล์ Windows กับแอปธุรกิจ — ลงทะเบียนกฎขาเข้าจากตัวติดตั้ง
สาเหตุประจำของ «บนเครื่องพัฒนาทำงาน แต่ที่ลูกค้าสื่อสารไม่ได้» คือไฟร์วอลล์ Windows บทความนี้อธิบายบล็อกขาเข้าตามค่าเริ่มต้นกับโปรไฟล์ เห...
นโยบายการตรวจสอบความปลอดภัยของ Windows และการสอบสวนบันทึกเหตุการณ์ในทางปฏิบัติ — เป็นทีมไอทีที่อ่าน 4625 ได้
คู่มือปฏิบัติเพื่อตอบคำขอ «ช่วยดูบันทึกการลงชื่อเข้าใช้ที่ล้มเหลว» ครอบคลุมความสัมพันธ์ของนโยบายการตรวจสอบพื้นฐานกับขั้นสูง หมวดย่อยที่คว...
คู่มือปฏิบัติ Windows LAPS — เลิกใช้รหัสผ่านผู้ดูแลท้องถิ่นร่วมกันทุกพีซี
รหัสผ่านผู้ดูแลท้องถิ่นร่วมกันทุกพีซีเป็นแหล่งเพาะการโจมตี Pass-the-Hash ที่การบุกรุกหนึ่งเครื่องลามไปทั้งกอง บทความนี้อธิบายการหมุนเวียน...
OneDrive "ไฟล์ตามความต้องการ" กับแอปธุรกิจ — ข้อสมมติที่เพลสโฮลเดอร์ทำลาย และวิธีรับมือ
CSV บนเดสก์ท็อปเปิดไม่ได้ หรือการนำเข้าล้มเหลวด้วย "ไม่พบไฟล์" — สาเหตุอาจเป็น Known Folder Move และไฟล์ตามความต้องการของ OneDrive บทความ...
Volume Shadow Copy (VSS): กลไกและงานปฏิบัติ — ทำไมซอฟต์แวร์สำรองจึงคัดลอกไฟล์ที่กำลังใช้อยู่ได้
ไฟล์ที่กำลังใช้อยู่ปกติคัดลอกไม่ได้เพราะละเมิดการแชร์ แล้วซอฟต์แวร์สำรองทำได้อย่างไร บทความนี้อธิบายบทบาทตัวร้องขอ ไรเตอร์ และโปรไวเดอร์ข...
หัวข้อที่เกี่ยวข้อง
หน้าเหล่านี้วางหัวข้อไว้ในบริบทที่กว้างขึ้นของบริการและการตัดสินใจ
หัวข้อเทคนิคของ Windows
ประตูสู่การพัฒนา Windows การตรวจสอบบั๊ก และการใช้ประโยชน์จากสินทรัพย์เดิม
บริการที่เกี่ยวข้องกับหัวข้อนี้
บทความนี้เกี่ยวข้องโดยตรงกับบริการต่อไปนี้
พัฒนาแอป Windows
แอปธุรกิจ การเชื่อมต่ออุปกรณ์ และเครื่องมือสื่อสาร ตั้งแต่ความต้องการจนถึงการพัฒนา
คำถามที่พบบ่อย
คำถามที่มักพบในการปรึกษาเกี่ยวกับหัวข้อของบทความนี้
- certmgr.msc กับ certlm.msc ต่างกันอย่างไร?
- ที่เก็บเป้าหมายต่างกัน certmgr.msc เปิดที่เก็บใบรับรองของผู้ใช้ที่ลงชื่อเข้าใช้ (ผู้ใช้ปัจจุบัน, CurrentUser) ส่วน certlm.msc เปิดที่เก็บใบรับรองของคอมพิวเตอร์ (คอมพิวเตอร์ท้องถิ่น, LocalMachine) ที่เก็บคอมพิวเตอร์ร่วมกันทุกผู้ใช้และบริการบนพีซี และการจัดการต้องมีสิทธิ์ผู้ดูแล ผู้ใช้ที่ไม่ใช่ผู้ดูแลจัดการได้แค่ที่เก็บผู้ใช้ของตนเอง ทั้งคู่ภายในแบ่งเป็นที่เก็บตรรกะอย่าง «ส่วนบุคคล» «ผู้ออกใบรับรองรากที่เชื่อถือ» และจาก PowerShell เห็นโครงเดียวกันเป็น Cert:\CurrentUser กับ Cert:\LocalMachine
- ใบรับรองลูกข่ายควรใส่ที่เก็บผู้ใช้หรือคอมพิวเตอร์?
- ตัดสินจากโปรแกรมที่ใช้ใบรับรองนั้น «ทำงานในฐานะใคร» หากเป็นแอปเดสก์ท็อปที่ผู้ใช้โต้ตอบเป็นผู้เริ่ม ฐานคือที่เก็บผู้ใช้ของคนที่ใช้ (Cert:\CurrentUser\My) หากเป็นโปรแกรมที่รันไร้คนด้วยบริการ Windows, พูลแอป IIS หรือ Task Scheduler ให้ใส่ที่เก็บคอมพิวเตอร์ (Cert:\LocalMachine\My) แล้วให้สิทธิ์อ่านคีย์ลับแก่บัญชีที่รัน ที่เก็บผู้ใช้คนละชุดต่อบัญชี ดังนั้นใบรับรองที่นักพัฒนาใส่ในที่เก็บผู้ใช้ของตนเอง บริการที่ทำงานด้วยบัญชีอื่นมองไม่เห็น นี่คือสาเหตุแบบฉบับของอุบัติเหตุ «ตอนพัฒนางาน แต่ตอนงานจริงหาไม่เจอ»
- เมื่อบริการ Windows หาหรือใช้ใบรับรองไม่ได้ ควรตรวจอะไร?
- ตรวจสองขั้น ประการแรก «กำลังมองที่เก็บใด» ถ้าโค้ดเปิด StoreLocation.CurrentUser นั่นคือที่เก็บผู้ใช้ของบัญชีที่รันบริการ ซึ่งคนละชุดกับที่เก็บของตนเองที่ผู้ดูแลกำลังดูด้วย certmgr.msc ให้ย้ายใบรับรองไปที่เก็บคอมพิวเตอร์ และจับโค้ดให้ตรง StoreLocation.LocalMachine ประการที่สอง «อ่านคีย์ลับได้หรือไม่» การเห็นในรายการใบรับรองกับการใช้คีย์ลับคนละเรื่อง และคีย์ลับของที่เก็บคอมพิวเตอร์โดยปกติเข้าถึงได้แค่ผู้ดูแลกับ SYSTEM ตามค่าเริ่มต้น จาก certlm.msc เปิด «จัดการคีย์ลับ» จากใบรับรองเป้าหมาย แล้วให้ «อ่าน» แก่บัญชีที่รันบริการ (NETWORK SERVICE ฯลฯ)
- จะหาวันหมดอายุของใบรับรองล่วงหน้าด้วย PowerShell อย่างไร?
- ทำรายการด้วย Get-ChildItem ต่อไดรฟ์ Cert: ตัวอย่างเช่น Get-ChildItem Cert:\LocalMachine\My | Sort-Object NotAfter | Format-Table Thumbprint, Subject, NotAfter จะแสดงที่เก็บส่วนบุคคลของที่เก็บคอมพิวเตอร์เรียงตามวันหมดอายุ นอกจากนี้พารามิเตอร์ -ExpiringInDays สกัดได้เฉพาะ «ใบรับรองที่หมดอายุภายในจำนวนวันที่ระบุ» และถ้าระบุ 0 จะได้ใบรับรองที่หมดอายุแล้ว หากดำเนินงานนี้เป็นรายเดือนกับทุกเซิร์ฟเวอร์ แล้วเทียบผลกับบัญชีใบรับรอง อุบัติเหตุแบบ «หมดอายุแล้วเช้านี้เชื่อมไม่ได้» กันได้เกือบหมด