您可以使用PowerShell以一种方式将密码存储在磁盘上,(默认情况下)只能由在创建它的计算机上当前执行用户检索。如果您无法完全在PowerShell中编写脚本,您可以通过在脚本中调用powershell.exe
来完成此操作。
如果您想要从单个计算机和用户使用凭据
创建凭据文件(这将提示您输入凭据信息,在这种情况下,用户名无关紧要但必须提供):
powershell.exe -c "Get-Credential | Export-CliXml cred.xml"
Export-CliXml
是一个特殊的Cmdlet,它将PowerShell对象序列化到磁盘中,并且在一些例外情况下(比如不要尝试使用COM对象并期望它能正常工作),通常可以用于像下面这样将对象读回到另一个会话中。如果想从脚本中读取到变量并在命令中使用,可以按以下方式进行:
for /f %i in ('powershell.exe -c "( Import-CliXml cred.xml ).GetNetworkCredential().Password"') do set PASSWORD=%i
DFC.EXE %PASSWORD% /BOOTTHAWED
我们需要获取网络凭证,这样我们就可以在该命令的一部分中获得可用的密码。如果您发现自己需要在其他命令中使用用户名,也可以通过类似的方式获得:
for /f %i in ('powershell.exe -c "( Import-CliXml cred.xml ).UserName"') do set USERNAME=%i
在用户名的情况下,它没有加密,所以您不需要返回网络凭据以获得可用的用户名。
如果您想要在多台计算机和用户中使用凭据
以上,我们利用Get-Credential
来创建初始凭据以保持简单,但不一定必须使用它来构建凭据对象。
如果您需要从多台计算机或用户解密凭据,那么就会变得有点复杂。您将无法利用Get-Credential
,而是必须自己构建凭据,并仅在文件中存储密码(对于这种情况也可以存储用户名)。
准备秘密
为了准备凭据,首先我们需要创建一个密钥文件。这比能够使用Get-Credential
更为复杂,但您只需要在生成密钥文件或密码文件的第一次执行这些步骤。最好从powershell.exe
执行此步骤:
$keyFile = "C:\path\to\keyfile.key"
$key = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)
$key | Out-File $keyFile
现在我们有了密钥文件,可以使用它将您的密码存储到磁盘上(请记住,这一次我们只是将密码存储在文件中,而不是PSCredential
对象)。再次强调,最好从powershell.exe
执行此操作,就像之前的步骤一样:
$insecurePassFile = "C:\path\to\insecurePassFile.txt"
$securePassFile = "C:\path\to\securePassFile.txt"
$keyFile = "C:\path\to\keyfile.key"
$key = Get-Content $keyFile
$password = Get-Content $insecurePassFile | Select-Object -First 1 | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString -key $key
$password | Out-File $securePassFile
现在,您的加密密码文件已保存到
$securePassFile
。此时,您可以将密码文件和密钥复制到网络上的某个位置。
在批处理脚本中使用凭据
现在我们回到批处理领域。要读取密码,您需要知道密码文件和密钥文件的位置,并拥有访问这两个文件的权限。抱歉,但这将是一个很长的PowerShell命令,需要放在一行上:
for /f %i in ('powershell.exe -c "$key = Get-Content \\server.domain.tld\share\path\to\keyfile.key; [System.Net.NetworkCredential]::new("", ( Get-Content \\server.domain.tld\share\path\to\password.txt | ConvertTo-SecureString -Key $key ) ).Password"') do set PASSWORD=%i
那是一个难以理解的内容,让我们来逐步分解一下:
1. 批处理循环(batch for loop)是必须要使用的“魔法”,用于将变量设置为命令输出的结果。最终会将 PASSWORD 变量设置为 PowerShell 命令的输出结果。我认为编写命令提示符的人都是受虐狂 XD。
2. 这里开始实际的解密过程。首先,我们需要从密钥文件读取 $key。如果没有这个密钥,我们就无法从密码文件中解密出密码。
3. 我们创建了一个新的网络凭据,可以通过创建新的 System.Net.NetworkCredential 对象从中提取密码。第一个参数是用户名(我们这里不需要,填空字符串即可),第二个参数是 SecureString 类型的密码。
4. 对于密码参数,我们从文件中读取密码,并使用密钥文件中的密钥将其解密为正确的 SecureString。
5. 从生成的 NetworkCredential 对象中,我们可以读取 Password 属性,这是一个可用的密码。
6. 作为原始批处理循环的一部分,PASSWORD 被设置为前一个 PowerShell 命令的输出结果。在本例中,它是我们构建的 NetworkCredential 的 Password 属性。
请像任何敏感密码一样对待你的密钥
如果你提供了自己的密钥,请确保将其存储在安全的地方。只有应该访问它的用户和计算机才能读取它。理想情况下,凭据和密钥应该存储在秘密保管库中(例如 Hashicorp Vault、Keepass 等),但文件 ACL 也可以用于控制谁可以访问这些信息。
请注意,当账户密码更改时,如果你依赖默认加密行为,你将需要重新生成 cred.xml。