KARPACH

WEB DEVELOPER BLOG

How to sign assembly with as strong name in Visual Studio Team Services?

I have a self-generated password-protected key.pfx. That key.pfx file is used to sign some assemblies in my solution. I tried to build that solution in Visual Studio Team Services and it failed right away with the following error:

Error MSB3325: Cannot import the following key file: key.pfx. The key file may be password protected. To correct this, try to import the certificate again or manually install the certificate to the Strong Name CSP with the following key container name: VS\_KEY\_9000008CC1777

Quick googling shows that I need to run:

sn -i key.pfx VS_KEY_9000008CC1777

However this command will prompt a password, so this would not work for a build server. I tried to use an automated solution for the above command using the following PowerShell script:

param($PfxFilePath, $Password)

$absolutePfxFilePath = Resolve-Path -Path $PfxFilePath
Write-Output "Importing store certificate '$absolutePfxFilePath'..."

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($absolutePfxFilePath, $Password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet")
$store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "MY", CurrentUser
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::"ReadWrite")
$store.Add($cert)
$store.Close()

However, this didn’t work for Visual Studio Teams Services hosted build agent. I suspect that hosted build server has some kind of environment protection and the above certificate installation fails silently. Then I decided to extract key.snk file from key.pfx and use that file to sign assemblies. This approach works, but it is not secure, since I would need to store a private unprotected key file in source control. So, I came up with the idea to dynamically extract key.snk using the following PowerShell script:

Param(
	[Parameter(Mandatory=$True,Position=1)]
	[string] $PfxFilePath,
	[string] $PfxPassword
)

# The path to the snk file we're creating
[string] $snkFilePath = [IO.Path]::GetFileNameWithoutExtension($PfxFilePath) + ".snk";

# Read in the bytes of the pfx file
[byte[]] $pfxBytes = Get-Content $PfxFilePath -Encoding Byte;

# Get a cert object from the pfx bytes with the private key marked as exportable
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(
	$pfxBytes,
	$PfxPassword,
	[Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable);

# Export a CSP blob from the cert (which is the same format as an SNK file)
[byte[]] $snkBytes = ([Security.Cryptography.RSACryptoServiceProvider]$cert.PrivateKey).ExportCspBlob($true);

# Write the CSP blob/SNK bytes to the snk file
[IO.File]::WriteAllBytes([IO.Path]::Combine([IO.Path]::GetDirectoryName($PfxFilePath), $snkFilePath), $snkBytes);

Then I added two variables to the build definition:

CertPath = $(Build.SourcesDirectory)\key.pfx
CertPass = password (clicked on a lock to secure value of variable)

After that I added a PowerShell task with the following arguments:

Sign assembly PowerShell task

This time the build passed and assemblies were signed successfully.

Posted on July 24, 2017 by

Comments

Posted on 8/17/2017 10:45:54 PM by Brett Hitnon

The two powershell scripts above look identical, which one ended up working for you and which didn’t - are the two scripts (which are the same) the good script or the one that didn’t work for you?
Thank you for noticing. I corrected the first script, so scripts don’t look the same. I ended up using the second powershell script.

Posted on 8/25/2017 10:27:48 AM by Brett Hinton

Viktar,

Thank for the info. One more follow up question, how did you get the signing process to use the .snk file instead of the .pfx file?

Did you just have to “point” the solution/project to sign using the .snk file instead of the .pfx file so it would pick it up at build time?

Is that what you were referring to when you said “Then I decided to extract key.snk file from key.pfx and use that file to sign assemblies” in your blog post?

Yes, I pointed my solution/project file to key.snk file for Release configuration, which is only used by CI.

Posted on 11/27/2017 06:44:36 AM by Dmitry

Hello Viktar :) “After that I added powershell task with following arguments” you use Inline script? if yes, what you put to the field “Inline script”? (500 symbols max). could you provide please screenshot of this two tasks?
I am sorry for such delayed reply. I updated the article with screenshot. I didn’t inline PowerShell script, I committed it to the Git repository.

Posted on 1/10/2018 02:42:00 AM by suresh

i ran same script in my machine, but not working.

Posted on 2/2/2019 04:54:10 AM by Jimmy Wales

Can I have the full script to sign the assembly with snk file? your script here is generating the snk file only. Not sure how to sign the .dll file further?

Posted on 8/20/2019 03:16:50 PM by Michael Sondergaard

Regarding Brett Hinton question. you answered “I pointed my solution/project file to key.snk file for Release configuration” does this imply that your project reference the snk file in project and is stored in your source control system or do you changed the solution/project file on the build agent to use the snk file.

Posted on 6/23/2021 10:15:13 AM by René Schindhelm

Hi Vitkar, does this actually sign (as in the author is verified by a third party) assemblies or just provide a strong-name for them? ty!