Auto-entering Password In Sn.exe
Asked Answered
D

7

11

I need to create post build event to perform the following:

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

When the Visual Studio executes the 1st statement it requires a password and waits until the user specifies it and fails.

Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42 Copyright (c) Microsoft Corporation. All rights reserved.

Enter the password for the PKCS#12 key file: Failed to parse the PKCS#12 blob in mykey.pfx -- The handle is invalid.

I tried to specify the password using sn command line arguments, but I could not see a way to do it.

Please help.

Regards, Hilmi.

Davin answered 29/6, 2010 at 5:36 Comment(1)
Is there any reason why you can't use Visual Studio's own key-signing feature? (Check the Properties for the project.)Fortnight
S
8

Unfortunately, no approach mentioned here worked for me. I have to register couple pfxs in a docker container.

So I re-developed the sn.exe -i <infile> <container> command in C# using the RSACryptoServiceProvider. The source and the app are on GitHub in the SnInstallPfx project.

The SnInstallPfx app accepts a PFX key and its password. It computes the key container name (VS_KEY_*) automatically (borrowed from MSBuild source code) and installs it to the strong name CSP.

Usage:

SnInstallPfx.exe <pfx_infile> <pfx_password>
Sheik answered 9/11, 2019 at 16:11 Comment(3)
Thank you for this! Most helpful.Certitude
Great, thx for feedback! I had updated cmdln args for the app. You can optionally pass the container nameSheik
You're literally a life savior.Zach
C
3

if like me, you are not using TFS or MSBUILD to build, then there are at least 2 other ways:

a) run sn.exe from a script, and write the password to stdin

see here for a C# example:

note: with the .NET4 version of sn.exe it seems to be impossible to execute it as external process (at least on Windows XP), and write the password to stdin (I tried with python + with C#, and sn.exe seems to just exit without waiting for password input).

b) use sn.exe to re-sign the password, using a pfx that has already been installed.

If you have already installed the pfx file, then you might know the container name (usually Visual Studio uses a name like VS_KEY_ABAB1234ABAB1234)

If, like me, you do not know or remember the container name, then just re-install the pfx file:

sn -i myPfxFile VS_KEY_ABAB1234ABAB1234

sn.exe will prompt you for a password, as it installs the certificate in the pfx file.

You can then make sn.exe re-sign your assembly, without any prompt for password:

sn -Rca myAssembly.dll myVSkey

The above can be used in a build script, as no interaction is required :-)

NB remember to check that the signing actually works:

sn -v myAssembly.dll
Cleaner answered 1/6, 2012 at 14:45 Comment(0)
Q
2

I had this problem come up today, with a C++ DLL that I use in a ClickOnce C# app.

I set up delay signing on the DLL, then used a post-build event to run SN like so:

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

Gotta love the old-school batch methods. ECHO <your-password-here> prints your password, the pipe | pipes that output along to SN.exe, which takes the password.

You probably won't need delay signing and the -R switch like I did, but you get the idea.

EDIT: THIS PROBABLY NO LONGER WORKS - my answer was from 2013 and I was using VS2010 back then.

Quadrant answered 2/2, 2013 at 0:42 Comment(6)
One other very important thing to know, that I only now just remembered: If you're putting this command in a batch file, and your password contains a % symbol, you need to use two of them. If your password is super%cool%guy, you need to use super%%cool%%guy in the batch file only.Quadrant
The idea is great, but doesn't work on a batch file. sn.exe does not accept console input redirection and complains about it like this : Console input may not be redirected for password entry. Any ideas how to achieve this in a batch file?Acetum
I didn't have an issue with it. Can you post your batch file contents? (I did this a few years back and maybe the latest SN.exe doesn't allow it)Quadrant
Here is my gist. Tried to set the password in a variable and pipe it into the sn command, but no luck :(Acetum
It may be changed in the later version of sn maybe - I tried with a pipe without batch file directly from the console it did not work either.Sarcenet
You're probably right. It worked on a much older SN.exe and they probably thought piping input was a security risk.Quadrant
G
2

I've been researching this for almost two days now. I tried older versions of sn.exe (going back as far as 2.0!), but I couldn't get the echo PASSWORD | trick to work.

In the end, I did this:

[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() sends keys to the current activated window. Since we're starting sn.exe using Start-Process with the -NoNewWindow modifier, our window is already focussed.
  • ~ is a special character, representing the ENTER button. {ENTER} is also possible, but that's for the Enter button near the numpad on your keyboard. Probably doesn't make any difference, but I just wanted to be sure.

Source I used: https://technet.microsoft.com/en-us/library/ff731008.aspx

I didn't need Visual Basic's AppActivate() in the end because of -NoNewWindow.

Update: Works even better with -Wait argument!

Gillen answered 22/12, 2017 at 14:20 Comment(1)
Same error than the cmd standard pipe, the input can't be redirectedCorrasion
B
1

I needed this to be automatized and found this question. Following this answer (many thanks @Thomas Rijsewijk) I've written my version of it:

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

Using the -Wait switch of the Start-process command did not solve the problem because the PS script was waiting the sn.exe process to terminate before forwarding the password to it.

Ballonet answered 30/11, 2018 at 12:9 Comment(0)
E
1

After a long investigation i can run sn.ex for various automated environments such as Azure devops. My code is here:

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}");
Euthenics answered 1/7, 2019 at 20:15 Comment(0)
E
0

Using WinAPI we can do it so:

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
Euthenics answered 19/6, 2020 at 14:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.