【发布时间】:2015-09-02 01:14:12
【问题描述】:
我正在尝试创建一个脚本,以从任何给定的智能卡中删除除最新证书之外的所有证书(当时在 SC 阅读器中)。这是我打算能够分发给最终用户的东西,所以它应该是自给自足的。我的第一个问题是阅读卡上的证书。我不想影响任何不在智能卡上的证书,所以我寻找直接从卡中读取的解决方案,我发现了这个 gem:
How to enumerate all certificates on a smart card (PowerShell)
它很旧,但它看起来应该可以满足我的需要。它似乎确实可以正常工作,但是当我到达线路时 PowerShell ISE 崩溃:
$store = new-object System.Security.Cryptography.X509Certificates.X509Store($hwStore)
我可以通过从该行中排除 ($hwStore) 来创建一个默认为“我的”存储的通用存储,但指定该存储可靠地使我的 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。该应用程序不会崩溃,它会返回一个有效的存储,上面有我的智能卡证书。现在的问题是我无法将其发送给用户,因为期望他们能够导航到 PowerShell 的 x86 版本,然后使用它运行脚本就像期望我的狗给我做华夫饼......我想它可能发生,但很可能会出现问题,而且我最终还是不得不自己做。
Edit2: 好的,所以我想我会强制这部分脚本在 x86 模式下运行。我将用我更新的代码发布答案并接受它。如果@Vesper 发布关于 64/32 位问题的答案(希望有更多信息),我会接受他的答案,以便他得到信任,因为他的评论是导致我找到解决方案的原因。
【问题讨论】:
-
您的智能卡使用什么加密提供商?此外,由于这个 gem 很旧,请尝试运行 Powershell ISE x86 而不是 x64,因为 32 位和 64 位版本之间的 DLL 签名在参数宽度方面是不同的。
标签: powershell x509certificate