в C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
В каталоге есть перечисление ключевых контейнеров. Соглашение об именах <uniqueGUID>_<staticGUID>
и я предполагаю <staticGUID>
быть идентификатором машины. В конечном итоге я хочу иметь возможность связать контейнер ключей с соответствующим сертификатом, чтобы я мог настроить таргетинг на определенные файлы ключей для ACL. Для этого мне нужно знать, как <uniqueGUID>
выводится и как он относится к сертификатам.
Ресурсы Microsoft, которые я проверил до сих пор, не дали ответа, но они отлично подходят для справки:
Общие сведения о контейнерах ключей RSA на уровне компьютера и пользователя (справочник по IIS)
Как: изменить разрешения безопасности для каталога MachineKeys
Чтобы решить вашу проблему определения сертификата, который соответствует какому файлу ключа, с целью изменения списков ACL файловой системы для файлов закрытого ключа, используйте следующее:
PS C:\Users\Ryan> $Cert = Get-Item Cert:\LocalMachine\My\2F6CB7D56BAA752BCCC0829DD829C0E2662FA1C6
PS C:\Users\Ryan> $Cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
fad662b360941f26a1193357aab3c12d_03f917b5-cb8b-45bd-b884-41c139a66ff7
Соглашение об именах файлов - x_y, где x - это случайный GUID для уникальной идентификации ключа, а y - это GUID компьютера, найденный в HKLM\SOFTWARE\Microsoft\Cryptography
.
Некоторые из этих уникальных идентификаторов хорошо известны, например некоторые из этих идентификаторов IIS:
6de9cb26d2b98c01ec4e9e8b34824aa2_GUID iisConfigurationKey
d6d986f09a1ee04e24c949879fdb506c_GUID NetFrameworkConfigurationKey
76944fb33636aeddb9590521c2e8815a_GUID iisWasKey
Но другие генерируются случайным образом.
Обратите внимание, что эта информация относится только к сертификатам / ключам «Локального компьютера» или «Машины». Сертификаты пользователей хранятся в соответствующих пользовательских местах файловой системы и реестра.
Райан Райс предоставил только частичное решение, потому что оно не будет работать с ключами CNG. Следующий код получит имя контейнера (а значит, и имя файла) для ключей CNG:
$signature = @"
[DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CertGetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
IntPtr pvData,
ref uint pcbData
);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPT_KEY_PROV_INFO {
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszProvName;
public uint dwProvType;
public uint dwFlags;
public uint cProvParam;
public IntPtr rgProvParam;
public uint dwKeySpec;
}
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenStorageProvider(
ref IntPtr phProvider,
[MarshalAs(UnmanagedType.LPWStr)]
string pszProviderName,
uint dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenKey(
IntPtr hProvider,
ref IntPtr phKey,
[MarshalAs(UnmanagedType.LPWStr)]
string pszKeyName,
uint dwLegacyKeySpec,
uint dwFlags
);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptGetProperty(
IntPtr hObject,
[MarshalAs(UnmanagedType.LPWStr)]
string pszProperty,
byte[] pbOutput,
int cbOutput,
ref int pcbResult,
int dwFlags
);
[DllImport("ncrypt.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int NCryptFreeObject(
IntPtr hObject
);
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Tools
$CERT_KEY_PROV_INFO_PROP_ID = 0x2 # from Wincrypt.h header file
$cert = dir cert:\currentuser\my\C541C66F490413302C845A440AFA24E98A231C3C
$pcbData = 0
[void][PKI.Tools]::CertGetCertificateContextProperty($cert.Handle,$CERT_KEY_PROV_INFO_PROP_ID,[IntPtr]::Zero,[ref]$pcbData)
$pvData = [Runtime.InteropServices.Marshal]::AllocHGlobal($pcbData)
[PKI.Tools]::CertGetCertificateContextProperty($cert.Handle,$CERT_KEY_PROV_INFO_PROP_ID,$pvData,[ref]$pcbData)
$keyProv = [Runtime.InteropServices.Marshal]::PtrToStructure($pvData,[type][PKI.Tools+CRYPT_KEY_PROV_INFO])
[Runtime.InteropServices.Marshal]::FreeHGlobal($pvData)
$phProvider = [IntPtr]::Zero
[void][PKI.Tools]::NCryptOpenStorageProvider([ref]$phProvider,$keyProv.pwszProvName,0)
$phKey = [IntPtr]::Zero
[void][PKI.Tools]::NCryptOpenKey($phProvider,[ref]$phKey,$keyProv.pwszContainerName,0,0)
$pcbResult = 0
[void][PKI.Tools]::NCryptGetProperty($phKey,"Unique Name",$null,0,[ref]$pcbResult,0)
$pbOutput = New-Object byte[] -ArgumentList $pcbResult
[void][PKI.Tools]::NCryptGetProperty($phKey,"Unique Name",$pbOutput,$pbOutput.length,[ref]$pcbResult,0)
[Text.Encoding]::Unicode.GetString($pbOutput)
[void][PKI.Tools]::NCryptFreeObject($phProvider)
[void][PKI.Tools]::NCryptFreeObject($phKey)
Я использовал код CryptoGuy, значительно расширил его и превратил в функцию. Однако он все еще нуждается в улучшении. Спасибо, CryptoGuy!
function Get-KeyContainer {
[CmdletBinding()]
Param([Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [string]$Thumbprint,
[Parameter(Position=1, Mandatory=$false, ValueFromPipeline=$false)] [switch]$MachineStore)
$MemberDefinition=@"
[DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CertGetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
IntPtr pvData,
ref uint pcbData);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPT_KEY_PROV_INFO {
[MarshalAs(UnmanagedType.LPWStr)] public string pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)] public string pwszProvName;
public uint dwProvType;
public uint dwFlags;
public uint cProvParam;
public IntPtr rgProvParam;
public uint dwKeySpec;}
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenStorageProvider(
ref IntPtr phProvider,
[MarshalAs(UnmanagedType.LPWStr)] string pszProviderName,
uint dwFlags);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptOpenKey(
IntPtr hProvider,
ref IntPtr phKey,
[MarshalAs(UnmanagedType.LPWStr)] string pszKeyName,
uint dwLegacyKeySpec,
uint dwFlags);
[DllImport("ncrypt.dll", SetLastError = true)]
public static extern int NCryptGetProperty(
IntPtr hObject,
[MarshalAs(UnmanagedType.LPWStr)] string pszProperty,
byte[] pbOutput,
int cbOutput,
ref int pcbResult,
int dwFlags);
[DllImport("ncrypt.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int NCryptFreeObject(IntPtr hObject);
"@
Add-Type -MemberDefinition $MemberDefinition -Namespace PKI -Name Tools
$CERT_KEY_PROV_INFO_PROP_ID = 0x2 # from Wincrypt.h header file
# from Ncrypt.h header file
if ($MachineStore.IsPresent) { $NCRYPT_MACHINE_KEY_FLAG = 0x20 }
else { $NCRYPT_MACHINE_KEY_FLAG = 0 }
$cert=Get-Item -Path ("Cert:\LocalMachine\My\"+$Thumbprint)
$pcbData = 0
$result=[PKI.Tools]::CertGetCertificateContextProperty($cert.Handle,$CERT_KEY_PROV_INFO_PROP_ID,[IntPtr]::Zero,[ref]$pcbData)
if ($result -ne $true) {
switch ($result) {
-2146885628 { Write-Error "ERROR: CRYPT_E_NOT_FOUND 0x80092004 (-2146885628)`r`nThe certificate does not have the specified property." }
-2005270525 { Write-Error "ERROR: ERROR_MORE_DATA 0x887A0003 (-2005270525)`r`nIf the buffer specified by the pvData parameter is not large enough to hold the returned data, the function sets the ERROR_MORE_DATA code and stores the required buffer size, in bytes, in the variable pointed to by pcbData." }
}
exit
}
$pvData = [Runtime.InteropServices.Marshal]::AllocHGlobal($pcbData)
$result=[PKI.Tools]::CertGetCertificateContextProperty($cert.Handle,$CERT_KEY_PROV_INFO_PROP_ID,$pvData,[ref]$pcbData)
if ($result -ne $true) {
switch ($result) {
-2146885628 { Write-Error "ERROR: CRYPT_E_NOT_FOUND 0x80092004 (-2146885628)`r`nThe certificate does not have the specified property." }
-2005270525 { Write-Error "ERROR: ERROR_MORE_DATA 0x887A0003 (-2005270525)`r`nIf the buffer specified by the pvData parameter is not large enough to hold the returned data, the function sets the ERROR_MORE_DATA code and stores the required buffer size, in bytes, in the variable pointed to by pcbData." }
}
exit
}
$keyProv = [Runtime.InteropServices.Marshal]::PtrToStructure($pvData,[type][PKI.Tools+CRYPT_KEY_PROV_INFO])
[Runtime.InteropServices.Marshal]::FreeHGlobal($pvData)
$phProvider = [IntPtr]::Zero
$result=[PKI.Tools]::NCryptOpenStorageProvider([ref]$phProvider,$keyProv.pwszProvName,0)
if ($result -ne 0) {
switch ($result) {
-2146893815 { Write-Error "ERROR: NTE_BAD_FLAGS 0x80090009 (-2146893815)`r`nInvalid flags specified" }
-2146893785 { Write-Error "ERROR: NTE_INVALID_PARAMETER 0x80090027 (-2146893785)`r`nThe parameter is incorrect" }
-2146893810 { Write-Error "ERROR: NTE_NO_MEMORY 0x8009000E (-2146893810)`r`nInsufficient memory available for the operation" }
}
exit
}
$phKey = [IntPtr]::Zero
$result=[PKI.Tools]::NCryptOpenKey($phProvider,[ref]$phKey,$keyProv.pwszContainerName,0,$NCRYPT_MACHINE_KEY_FLAG)
if ($result -ne 0) {
switch ($result) {
-2146893815 { Write-Error "ERROR: NTE_BAD_FLAGS 0x80090009 (-2146893815)`r`nThe dwFlags parameter contains a value that is not valid." }
-2146893802 { Write-Error "ERROR: NTE_BAD_KEYSET 0x80090016 (-2146893802)`r`nThe specified key was not found. Try using the -MachineKey flag to look in the Machine's store instead of the User's store." }
-2146893786 { Write-Error "ERROR: NTE_INVALID_HANDLE 0x80090026 (-2146893786)`r`nThe hProvider parameter is not valid." }
-2146893785 { Write-Error "ERROR: NTE_INVALID_PARAMETER 0x80090027 (-2146893785)`r`nThe parameter is incorrect" }
-2146893810 { Write-Error "ERROR: NTE_NO_MEMORY 0x8009000E (-2146893810)`r`nInsufficient memory available for the operation" }
}
exit
}
$pcbResult = 0
$result=[PKI.Tools]::NCryptGetProperty($phKey,"Unique Name",$null,0,[ref]$pcbResult,0)
if ($result -ne 0) {
switch ($result) {
-2146893815 { Write-Error "ERROR: NTE_BAD_FLAGS 0x80090009 (-2146893815)`r`nThe dwFlags parameter contains a value that is not valid." }
-2146893786 { Write-Error "ERROR: NTE_INVALID_HANDLE 0x80090026 (-2146893786)`r`nThe hProvider parameter is not valid." }
-2146893785 { Write-Error "ERROR: NTE_INVALID_PARAMETER 0x80090027 (-2146893785)`r`nThe parameter is incorrect" }
-2146893810 { Write-Error "ERROR: NTE_NO_MEMORY 0x8009000E (-2146893810)`r`nInsufficient memory available for the operation" }
-2146893783 { Write-Error "ERROR: NTE_NOT_SUPPORTED 0x80090029 (-2146893783)`r`nThe specified property is not supported for the object." }
}
exit
}
$pbOutput = New-Object byte[] -ArgumentList $pcbResult
$result=[PKI.Tools]::NCryptGetProperty($phKey,"Unique Name",$pbOutput,$pbOutput.length,[ref]$pcbResult,0)
if ($result -ne 0) {
switch ($result) {
-2146893815 { Write-Error "ERROR: NTE_BAD_FLAGS 0x80090009 (-2146893815)`r`nThe dwFlags parameter contains a value that is not valid." }
-2146893786 { Write-Error "ERROR: NTE_INVALID_HANDLE 0x80090026 (-2146893786)`r`nThe hProvider parameter is not valid." }
-2146893785 { Write-Error "ERROR: NTE_INVALID_PARAMETER 0x80090027 (-2146893785)`r`nThe parameter is incorrect" }
-2146893810 { Write-Error "ERROR: NTE_NO_MEMORY 0x8009000E (-2146893810)`r`nInsufficient memory available for the operation" }
-2146893783 { Write-Error "ERROR: NTE_NOT_SUPPORTED 0x80090029 (-2146893783)`r`nThe specified property is not supported for the object." }
}
exit
}
[Text.Encoding]::Unicode.GetString($pbOutput)
$result=[PKI.Tools]::NCryptFreeObject($phProvider)
if ($result -ne 0) { Write-Error "ERROR: NTE_INVALID_HANDLE 0x80090026 (-2146893786)`r`nThe hProvider parameter is not valid."; exit }
[void][PKI.Tools]::NCryptFreeObject($phKey)
if ($result -ne 0) { Write-Error "ERROR: NTE_INVALID_HANDLE 0x80090026 (-2146893786)`r`nThe hProvider parameter is not valid."; exit }
}
Get-KeyContainer -Thumbprint "xxxxxxxxx" -MachineStore