【问题标题】:Auto-entering Password In Sn.exe在 Sn.exe 中自动输入密码
【发布时间】:2010-06-29 05:36:37
【问题描述】:

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

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) 微软公司。 保留所有权利。

输入 PKCS#12 密钥的密码 文件:无法解析 PKCS#12 mykey.pfx 中的 blob -- 句柄是 无效。

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

请帮忙。

问候, 希尔米。

【问题讨论】:

  • 有什么理由不能使用 Visual Studio 自己的密钥签名功能吗? (检查项目的属性。)

标签: c# signing sn.exe


【解决方案1】:

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

所以我重新开发了 C# 中的sn.exe -i <infile> <container> 命令使用RSACryptoServiceProvider。源代码和应用程​​序在 GitHub 上 SnInstallPfx 项目。

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

用法:

SnInstallPfx.exe <pfx_infile> <pfx_password>

【讨论】:

  • 谢谢你!最有帮助。
  • 太好了,感谢反馈!我已经更新了应用程序的 cmdln args。您可以选择传递容器名称
  • 你真的是一个救世主。
【解决方案2】:

如果你和我一样,不使用TFS或MSBUILD构建,那么至少还有另外2种方式:

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

有关 C# 示例,请参阅 here

注意:使用 .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

【讨论】:

    【解决方案3】:

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

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

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

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

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

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

    【讨论】:

    • 另一件非常重要的事情,我现在才想起:如果您将此命令放在批处理文件中,并且您的密码包含 % 符号,则需要使用两个其中。如果你的密码是super%cool%guy,你需要在批处理文件中使用super%%cool%%guyonly
    • 这个想法很棒,但不适用于批处理文件。 sn.exe 不接受控制台输入重定向并像这样抱怨它:Console input may not be redirected for password entry。任何想法如何在批处理文件中实现这一点?
    • 我对它没有意见。你能发布你的批处理文件内容吗? (几年前我就这样做了,也许最新的 SN.exe 不允许这样做)
    • 这是我的gist。试图在变量中设置密码并将其输入sn 命令,但没有运气:(
    • 你可能是对的。它适用于更旧的 SN.exe,他们可能认为管道输入存在安全风险。
    【解决方案4】:

    我已经研究了将近两天了。我尝试了旧版本的 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() 将密钥发送到当前激活的窗口。由于我们使用带有-NoNewWindow 修饰符的Start-Process 开始sn.exe,因此我们的窗口已经聚焦。
    • ~ 是一个特殊字符,代表 ENTER 按钮。 {ENTER} 也是可能的,但这是用于键盘上小键盘附近的 Enter 按钮。可能没有任何区别,但我只是想确定一下。

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

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

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

    【讨论】:

    • 与cmd标准管道相同的错误,输入无法重定向
    【解决方案5】:

    我需要将其自动化并找到了这个问题。按照this 的回答(非常感谢@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}")
    

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

    【讨论】:

      【解决方案6】:

      经过长时间的调查,我可以为各种自动化环境(例如 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}");
      

      【讨论】:

        【解决方案7】:

        使用 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
        

        【讨论】:

          猜你喜欢
          • 2013-04-09
          • 2012-07-04
          • 1970-01-01
          • 2021-05-12
          • 1970-01-01
          • 2021-08-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多