我正在尝试创建一个脚本,以从任何给定的智能卡(在SC读卡器上)中删除除最新证书之外的所有证书。我希望将其分发给终端用户,因此它应该是自给自足的。我的第一个问题是读取智能卡上的证书。我不想影响智能卡上没有的任何证书,因此我寻找了直接从卡片中读取的解决方案,我找到了这个宝石:如何枚举智能卡上的所有证书(PowerShell)。虽然它看起来有些过时,但它似乎可以做到我所需要的。总的来说,它确实有效,但当我到达以下行时,PowerShell ISE会崩溃:
我已经在以下脚本中尝试了这两种方法,结果是一样的。第二种方法在脚本告诉我默认用户密钥容器是什么时,会给我返回 "�" 字符,所以我觉得它不正确。
我还尝试了 Vesper 建议的 x86 版 PowerShell。应用程序没有崩溃,并且确实返回了包含我的智能卡证书的有效存储。现在的问题是我无法将其发送给用户,因为期望他们能够导航到 x86 版 PowerShell,然后使用它运行脚本,就像期望我的狗给我做华夫饼一样... 我想这可能会发生,但更有可能出现问题,我最终还是要自己搞定。 编辑2:好吧,所以我想我会强制让脚本在 x86 模式下运行。我会发布一个含有更新代码的答案并接受它。如果 @Vesper 发布关于 64/32 位事情的答案(希望能提供更多信息),我会接受他的答案,这样他就可以得到功劳,因为他的评论让我找到了解决方案。
$store = new-object System.Security.Cryptography.X509Certificates.X509Store($hwStore)
我可以创建一个通用的商店,通过从该行中排除($hwStore)
来默认使用'My'商店,而不会出现问题,但是指定该商店会导致我的PowerShell ISE崩溃。
以下是该站点提供的函数,我遇到问题的那一行位于底部附近。
function Get-SCUserStore {
[string]$providerName ="Microsoft Base Smart Card Crypto Provider"
# import CrytoAPI from advapi32.dll
$signature = @"
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetProvParam(
IntPtr hProv,
uint dwParam,
byte[] pbProvData,
ref uint pdwProvDataLen,
uint dwFlags);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDestroyKey(
IntPtr hKey);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string pszContainer,
string pszProvider,
uint dwProvType,
long dwFlags);
[DllImport("advapi32.dll", CharSet=CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetUserKey(
IntPtr hProv,
uint dwKeySpec,
ref IntPtr phUserKey);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetKeyParam(
IntPtr hKey,
uint dwParam,
byte[] pbData,
ref uint pdwDataLen,
uint dwFlags);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptReleaseContext(
IntPtr hProv,
uint dwFlags);
"@
$CryptoAPI = Add-Type -member $signature -name advapiUtils -Namespace CryptoAPI -passthru
# set some constants for CryptoAPI
$AT_KEYEXCHANGE = 1
$AT_SIGNATURE = 2
$PROV_RSA_FULL = 1
$KP_CERTIFICATE = 26
$PP_ENUMCONTAINERS = 2
$PP_CONTAINER = 6
$PP_USER_CERTSTORE = 42
$CRYPT_FIRST = 1
$CRYPT_NEXT = 2
$CRYPT_VERIFYCONTEXT = 0xF0000000
[System.IntPtr]$hProvParent=0
$contextRet = $CryptoAPI::CryptAcquireContext([ref]$hprovParent,$null,$providerName,$PROV_RSA_FULL,$CRYPT_VERIFYCONTEXT)
[Uint32]$pdwProvDataLen = 0
[byte[]]$pbProvData = $null
$GetProvParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_CONTAINER,$pbProvData,[ref]$pdwProvDataLen,0)
if($pdwProvDataLen -gt 0)
{
$ProvData = new-Object byte[] $pdwProvDataLen
$GetKeyParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_CONTAINER,$ProvData,[ref]$pdwProvDataLen,0)
}
$enc = new-object System.Text.UTF8Encoding($null)
$keyContainer = $enc.GetString($ProvData)
write-host " The Default User Key Container:" $keyContainer
[Uint32]$pdwProvDataLen = 0
[byte[]]$pbProvData = $null
$GetProvParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_USER_CERTSTORE,$pbProvData,[ref]$pdwProvDataLen,0)
if($pdwProvDataLen -gt 0)
{
$ProvData = new-Object byte[] $pdwProvDataLen
$GetKeyParamRet = $CryptoAPI::CryptGetProvParam($hprovParent,$PP_USER_CERTSTORE,$ProvData,[ref]$pdwProvDataLen,0)
[uint32]$provdataInt = [System.BitConverter]::ToUInt32($provdata,0)
[System.IntPtr]$hwStore = $provdataInt
}
$store = new-object System.Security.Cryptography.X509Certificates.X509Store($hwStore)
# release smart card
$ReleaseContextRet = $CryptoAPI::CryptReleaseContext($hprovParent,0)
return $store
}
我没有使用P/Invoke的经验(我想我说对了),所以我不确定如何排除由此导入的命令问题。
编辑:certutil -scinfo -silent
列出的提供程序有:
Microsoft Base Smart Card Crypto Provider
Microsoft Smart Card Key Storage Provider
我已经在以下脚本中尝试了这两种方法,结果是一样的。第二种方法在脚本告诉我默认用户密钥容器是什么时,会给我返回 "�" 字符,所以我觉得它不正确。
我还尝试了 Vesper 建议的 x86 版 PowerShell。应用程序没有崩溃,并且确实返回了包含我的智能卡证书的有效存储。现在的问题是我无法将其发送给用户,因为期望他们能够导航到 x86 版 PowerShell,然后使用它运行脚本,就像期望我的狗给我做华夫饼一样... 我想这可能会发生,但更有可能出现问题,我最终还是要自己搞定。 编辑2:好吧,所以我想我会强制让脚本在 x86 模式下运行。我会发布一个含有更新代码的答案并接受它。如果 @Vesper 发布关于 64/32 位事情的答案(希望能提供更多信息),我会接受他的答案,这样他就可以得到功劳,因为他的评论让我找到了解决方案。