10

我需要创建构建后事件来执行以下操作:

sn -i MyKey.pfx MyKeyContainerName
tlbimp $(ConfigurationName)\MyCom.tlb /out:$(ConfigurationName)\NETMyCom.dll /keycontainer:MyKeyContainerName
sn -d MyKeyContainerName

当 Visual Studio 执行第一条语句时,它需要密码并等待用户指定密码并失败。

Microsoft (R) .NET Framework 强名称实用程序版本 2.0.50727.42 版权所有 (c) Microsoft Corporation。版权所有。

Enter the password for the PKCS#12 key file: Failed to parse the PKCS#12 blob in mykey.pfx -- 句柄无效。

我尝试使用 sn 命令行参数指定密码,但我看不到这样做的方法。

请帮忙。

问候,希尔米。

4

7 回答 7

6

不幸的是,这里没有提到对我有用的方法。我必须在 docker 容器中注册几个 pfxs。

因此,我使用RSACryptoServiceProvidersn.exe -i <infile> <container>在 C# 中重新开发了该 命令。源代码和应用程​​序位于SnInstallPfx项目的 GitHub 上。

SnInstallPfx应用程序接受 PFX 密钥及其密码。它自动计算密钥容器名称 (VS_KEY_*)(借用自 MSBuild 源代码)并将其安装到强名称 CSP。

用法:

SnInstallPfx.exe <pfx_infile> <pfx_password>
于 2019-11-09T16:11:13.000 回答
3

如果像我一样,你没有使用 TFS 或 MSBUILD 来构建,那么至少还有 2 种其他方式:

a) 从脚本运行 sn.exe,并将密码写入标准输入

有关 C# 示例,请参见此处:

注意:使用 .NET4 版本的 sn.exe 似乎不可能将其作为外部进程执行(至少在 Windows XP 上),并将密码写入标准输入(我尝试使用 python + 和 C#,并且 sn.exe 似乎直接退出而不等待密码输入)。

b) 使用 sn.exe 重新签署密码,使用已安装的 pfx。

如果您已经安装了 pfx 文件,那么您可能知道容器名称(通常 Visual Studio 使用类似 VS_KEY_ABAB1234ABAB1234 的名称)

如果像我一样,您不知道或不记得容器名称,那么只需重新安装 pfx 文件:

sn -i myPfxFile VS_KEY_ABAB1234ABAB1234

sn.exe 将提示您输入密码,因为它将证书安装在 pfx 文件中。

然后,您可以让 sn.exe 重新签署您的程序集,而无需任何密码提示:

sn -Rca myAssembly.dll myVSkey

以上可以在构建脚本中使用,因为不需要交互:-)

注意要检查签名是否真的有效:

sn -v myAssembly.dll
于 2012-06-01T14:45:03.427 回答
2

我今天遇到了这个问题,我在 ClickOnce C# 应用程序中使用了一个 C++ DLL。

我在 DLL 上设置了延迟签名,然后使用构建后事件来运行 SN,如下所示:

ECHO <your-password-here> | sn.exe -R $(OutDir)$(TargetFileName) $(MSBuildProjectDirectory)<path-to-pfx-file>

一定要喜欢老式的批处理方法。ECHO <your-password-here>打印你的密码,|管道输出到 SN.exe,它接受密码。

您可能不需要像我那样延迟签名和 -R 开关,但您明白了。

编辑:这可能不再有效 - 我的回答来自 2013 年,当时我使用的是 VS2010。

于 2013-02-02T00:42:30.497 回答
2

我已经研究了将近两天了。我尝试了较旧版本的 sn.exe(可以追溯到 2.0!),但我无法使用该echo PASSWORD |技巧。

最后,我这样做了:

[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")

Start-Process "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\sn.exe " -ArgumentList "-i 
`"Certificate.pfx`" VS_KEY_XXXXXXX" -NoNewWindow -Wait
[System.Windows.Forms.SendKeys]::SendWait("PASSWORDHERE~")
  • SendWait()将键发送到当前激活的窗口。由于我们开始sn.exe使用修饰符,我们Start-Process-NoNewWindow窗口已经聚焦。
  • ~是一个特殊字符,代表 ENTER 按钮。{ENTER}也是可能的,但这是用于键盘上小键盘附近的 Enter 按钮。可能没有任何区别,但我只是想确定一下。

我使用的来源:https ://technet.microsoft.com/en-us/library/ff731008.aspx

我最终不需要 Visual Basic AppActivate(),因为-NoNewWindow.

更新:使用参数效果更好-Wait

于 2017-12-22T14:20:45.827 回答
0

经过长时间的调查,我可以为 Azure devops 等各种自动化环境运行 sn.ex。我的代码在这里:

Start-Process cmd.exe 
Sleep 3
$WshShell = New-Object -ComObject WScript.Shell
Sleep 3
$WshShell.sendkeys(".\sn.exe -i  $PfxCertificatePath  VS_KEY_10D1C85C6387479B{Enter}");
Sleep 3;
$WshShell.sendkeys("**password**{Enter}");
Sleep 3;
$WshShell.sendkeys("{Enter}");
于 2019-07-01T20:15:10.993 回答
0

使用 WinAPI,我们可以这样做:

Param(
    [string]  $Password ,
    [string] $CertFilePath 
)

function ExecuteCommand([string]$message)
{
    try{
    foreach ($char in [char[]]$message) {
    [void][WPIA.ConsoleUtils]::PostMessage($handle, [WPIA.ConsoleUtils]::WM_CHAR, [int]$char, 0) 
    }
    [void][WPIA.ConsoleUtils]::PostMessage($handle, [WPIA.ConsoleUtils]::WM_CHAR, 13, 0)
    Sleep 3
    }catch{
    }
}

Add-Type -Name ConsoleUtils -Namespace WPIA -MemberDefinition @'
[DllImport("user32.dll")]
public static extern int PostMessage(int hWnd, uint Msg, int wParam, int lParam);
public const int WM_CHAR = 0x0102;
'@

$Win32API = Add-Type -Name Funcs -Namespace Win32 -PassThru -MemberDefinition @'
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, IntPtr lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className,  string  windowTitle);

    [DllImport("kernel32.dll")]
  public static extern uint GetLastError();
'@

[System.Reflection.Assembly]::Load("Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[System.Reflection.Assembly]::Load("Microsoft.Build.Utilities.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
$publish = New-Object Microsoft.Build.Tasks.ResolveKeySource
$publish.KeyFile=$CertFilePath
try
{
        $publish.Execute()
}
catch{
        $VsKey= [regex]::match($error[0].Exception,'VS_KEY_[A-F0-9]+').Value
        $VsKey =$VsKey -replace "`n|`r"
}
$quotedCertPath='"'+$CertFilePath+'"'
Write-Host 'VsKey='$VsKey

start cmd
$proc=Get-Process | Where-Object {$_.Name -like "*cmd*"}
$proc.MainWindowTitle
$handle = $proc.MainWindowHandle
'1:'+$handle
if($handle -eq 0){
    $handle=$Win32API::FindWindow([IntPtr]::Zero, 'C:\WINDOWS\system32\cmd.exe')
    '2:'+$handle
}   
if($handle -eq 0){
    $handle=$Win32API::FindWindow('C:\WINDOWS\system32\cmd.exe',       [IntPtr]::Zero)
    '3:'+$handle
}
if($handle -eq 0){
    $proc2 = Start-Process cmd.exe -PassThru
    $proc2
    Get-Process -id $proc2.Id
    Sleep 3;
    $handle = (Get-Process -id $proc2.Id).MainWindowHandle
    $handle
    $proc.MainWindowHandle
    $proc.Refresh()
    Sleep 3;
    $proc.MainWindowHandle
}


Write-Host 'Handle='$handle

ExecuteCommand 'echo Starting > d:\a\1\s\Authenticode\log.txt'  
$SnCommand=[string]::Format(".\sn.exe -i {0} {1}",$quotedCertPath,$VsKey)
ExecuteCommand $SnCommand
ExecuteCommand $Password
于 2020-06-19T14:36:49.507 回答
0

我需要将其自动化并找到这个问题。按照这个答案(非常感谢@Thomas Rijsewijk)我写了我的版本:

# Start the sn.exe process
Start-Process $SnExePath -ArgumentList "-i $PfxCertificatePath $LocalContainerName" -NoNewWindow
# Wait for the process to start
Start-Sleep 2
# This workaround allows to forward the password to the standard input
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
[System.Windows.Forms.SendKeys]::SendWait("$($PfxCertificatePassword){ENTER}")
Start-Sleep 2
# This ENTER is to return from the sn.exe process
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")

使用命令的-Wait开关Start-process并没有解决问题,因为 PS 脚本在将密码转发给它之前正在等待 sn.exe 进程终止。

于 2018-11-30T12:09:34.460 回答