Created
March 2, 2021 04:42
-
-
Save jborean93/9758823e0546abf561b07d380bc60c53 to your computer and use it in GitHub Desktop.
Opens an X509 store for an NT Service account
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright: (c) 2021, Jordan Borean (@jborean93) <jborean93@gmail.com> | |
# MIT License (see LICENSE or https://opensource.org/licenses/MIT) | |
Function Get-ServiceCertStore { | |
<# | |
.SYNOPSIS | |
Open an X509 store to a service account. | |
.DESCRIPTION | |
Opens an X509 store to the NT SERVICE account specified. The X509 store can be used to then add/remove/enumerate | |
certificate to and from that store. | |
.PARAMETER ServiceName | |
The name of the service to open the store for. This should be the service name and not the display name. | |
.PARAMETER Name | |
The store name to open, e.g. 'My', 'Root'. Can be a value of [Security.Cryptography.X509Certificates.StoreName] or | |
just any string for the store name. If the store does not exist this will create the new store unless OpenFlags | |
has 'OpenExistingOnly' set. | |
.PARAMETER OpenFlags | |
Customise the open behaviour for the store. Can be set to | |
IncludeArchive: Include archived certificates | |
MaxAllowed: Opens with the highest access allowed for the current user | |
OpenExistingOnly: Opens existing store names, will fail if the store does not exist | |
ReadOnly: Open with read only access. | |
ReadWrite: Open with read write access. | |
.EXAMPLE Opens the NTDS\My store | |
$store = Get-ServiceCertStore NTDS | |
$store.Certificates | |
.EXAMPLE Opens the root store with read only access | |
$store = Get-Service WinRM | Get-ServiceCertStore -Name Root -OpenFlags ReadOnly | |
#> | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] | |
[string] | |
$ServiceName, | |
[Parameter()] | |
[string] | |
$Name = 'My', | |
[Security.Cryptography.X509Certificates.OpenFlags] | |
$OpenFlags = [Security.Cryptography.X509Certificates.OpenFlags]::MaxAllowed | |
) | |
begin { | |
$typeParams = @{ | |
TypeDefinition = @' | |
using Microsoft.Win32.SafeHandles; | |
using System; | |
using System.Runtime.InteropServices; | |
namespace X509 | |
{ | |
public class NativeMethods | |
{ | |
[DllImport("Crypt32.dll")] | |
public static extern bool CertCloseStore( | |
IntPtr hCertStore, | |
uint dwFlags); | |
[DllImport("Crypt32.dll", CharSet=CharSet.Unicode, SetLastError=true)] | |
public static extern SafeX509Store CertOpenStore( | |
IntPtr lpszStoreProvider, | |
uint dwEncodingType, | |
IntPtr hCryptProv, | |
uint dwFlags, | |
string pvPara); | |
} | |
public class SafeX509Store : SafeHandleZeroOrMinusOneIsInvalid | |
{ | |
public SafeX509Store() : base(true) { } | |
protected override bool ReleaseHandle() | |
{ | |
return NativeMethods.CertCloseStore(handle, 0); | |
} | |
} | |
} | |
'@ | |
} | |
Add-Type @typeParams | |
$provider = [IntPtr]::new(10) # CERT_STORE_PROV_SYSTEM_W | |
$flags = (0x00050000 -bor 0x00000004) # CERT_SYSTEM_STORE_SERVICES | CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | |
$flagType = [Security.Cryptography.X509Certificates.OpenFlags] | |
$openMode = [int]$OpenFlags -band 3 | |
$accessFlags = switch ($openMode) { | |
0 { 0x00008000 } # CERT_STORE_READONLY_FLAG | |
2 { 0x00001000 } # CERT_STORE_MAXIMUM_ALLOWED_FLAG | |
default { 0 } | |
} | |
$flags = $flags -bor $accessFlags | |
if ($OpenFlags.HasFlag($flagType::OpenExistingOnly)) { | |
$flags = $flags -bor 0x00004000 # CERT_STORE_OPEN_EXISTING_FLAG | |
} | |
if ($OpenFlags.HasFlag($flagType::IncludeArchived)) { | |
$flags = $flags -bor 0x00000200 # CERT_STORE_ENUM_ARCHIVED_FLAG | |
} | |
} | |
process { | |
$handle = [X509.NativeMethods]::CertOpenStore( | |
$provider, | |
0, # encoding type is only valid for PKCS7 or filename providers, | |
[IntPtr]::Zero, | |
$flags, | |
"$ServiceName\$Name" | |
); $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error() | |
if ($handle.IsInvalid) { | |
$exp = [ComponentModel.Win32Exception]$err | |
Write-Error -Message "Failed to open '$ServiceName\$Name': $($exp.Message)" -Exception $exp | |
return | |
} | |
try { | |
[Security.Cryptography.X509Certificates.X509Store]::new($handle.DangerousGetHandle()) | |
} | |
finally { | |
$handle.Dispose() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment