Автоматический ввод пароля в Sn.exe

Мне нужно создать событие после сборки, чтобы выполнить следующее:

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

Когда Visual Studio выполняет 1-й оператор, он требует пароль и ждет, пока пользователь его не укажет, и произойдет сбой.

Утилита для использования строгих имен Microsoft (R) .NET Framework версии 2.0.50727.42. Copyright (c) Microsoft Corporation. Все права защищены.

Введите пароль для файла ключей PKCS#12: Не удалось проанализировать BLOB-объект PKCS#12 в mykey.pfx - дескриптор недействителен.

Я пытался указать пароль, используя аргументы командной строки sn, но не смог найти способ сделать это.

Пожалуйста помоги.

С уважением, Хилми.

4 ответа

К сожалению, ни один из упомянутых здесь подходов не помог мне. Мне нужно зарегистрировать пару pfx в контейнере докера.

Поэтому я заново разработал sn.exe -i <infile> <container>на C# с помощью RSACryptoServiceProvider. Источник и приложение находятся на GitHub в проекте SnInstallPfx.

Приложение SnInstallPfx принимает ключ PFX и его пароль. Он автоматически вычисляет имя контейнера ключей (VS_KEY_*) (заимствовано из исходного кода MSBuild) и устанавливает его в CSP строгого имени.

Применение:

SnInstallPfx.exe <pfx_infile> <pfx_password>

Если, как и я, вы не используете TFS или MSBUILD для сборки, то есть как минимум 2 других способа:

а) запустите sn.exe из скрипта и напишите пароль к stdin

смотрите здесь для примера C#:

примечание: в версии sn.exe для.NET4 кажется невозможным выполнить его как внешний процесс (по крайней мере, в Windows XP) и записать пароль в stdin (я пробовал с python + с C#, и кажется, что sn.exe просто выйти без ожидания ввода пароля).

б) используйте 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

Вышеприведенное можно использовать в скрипте сборки, так как никакого взаимодействия не требуется:-)

NB не забудьте проверить, что подпись действительно работает:

sn -v myAssembly.dll

У меня была эта проблема сегодня, с C++ DLL, которую я использую в приложении ClickOnce C#.

Я установил задержку подписи в 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.

Я исследую это почти два дня. Я пробовал старые версии 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 рядом с цифровой клавиатурой на клавиатуре. Наверное, не имеет никакого значения, но я просто хотел быть уверен.

Источник, который я использовал: https://technet.microsoft.com/en-us/library/ff731008.aspx

Я не нуждался в Visual Basic AppActivate() в конце концов из-за -NoNewWindow,

Обновление: работает еще лучше с -Wait аргумент!

После долгого исследования я могу запускать sn.ex для различных автоматизированных сред, таких как Azure DevOps. Мой код здесь:

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}");

Мне нужно было это автоматизировать и нашел этот вопрос. После этого ответа (большое спасибо @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 перед отправкой пароля.

Используя 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
Другие вопросы по тегам