8

I am trying to build a click-once application using the Continuous integration and deployment feature in VSTS (Visual studio team services Online)We are trying to build this using the Hosted agent Visual studio 2015 We had difficulties signing the strong name key file with an error of

MSB3326: Cannot import the following key file: xxxx.snk. The key file may be password protected. To correct this, try to import the certificate again or import the certificate manually into the current user's personal certificate store. And after that

MSB3321: Importing key file "xxxx.pfx" was canceled.

I have tried to both select from store and from file changed the location and made sure to commit but with no success. Any ideas how i can overcome this errors or what am doing wrong.

Clerification on answer selected

Just wanted to make a clarification if anyone else has the same issue, in addition to the answer i had to place my certificate in my source control code and commit it. Then to select its location add a global variable on the VSTS Build

enter image description here

$cert.Import("$(CertPath)", $password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet") Where $(CertPath) would be something like $(Build.SourcesDirectory)\SharedSolutionFiles\CertificateName.pfx

Harry
  • 3,333
  • 3
  • 18
  • 28
  • 1
    (almost) same exact question as http://stackoverflow.com/questions/11155858/msbuild-cannot-sign-a-clickonce-manifest-using-a-temporary-key-errors-msb3326-a but but this issue cannot be solved by the answers on that question as OP is in the cloud. Be forewarned, folks who might dupehammer –  Apr 21 '17 at 16:09

4 Answers4

13

You can create a PowerShell script and add a PowerShell Script step in your build definition to import the certificate file before the VSBuild step.

Build failed without PowerShell Import Certificate Step: enter image description here

Build passed with PowerShell Import Certificate Step: enter image description here

The PowerShell Script I used:

$pfxpath = 'pathtoees.pfx'
$password = 'password'

Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $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()
Eddie Chen - MSFT
  • 29,708
  • 2
  • 46
  • 60
  • Thanks will give this a try today, this seems indeed promising! – Harry Apr 26 '17 at 09:27
  • Thank you so much for this, worked like a charm without me having to make an on-premise agent! Brilliant! – Harry Apr 26 '17 at 11:33
  • 1
    Isn't the user that runs the build a shared account? It seems like a bad idea to change the build environment on a hosted agent like this... not to mention, aren't you giving everyone else access to sign things with your certificate? – Michael Edenfield May 30 '18 at 16:48
  • 1
    I am running this as a part of Azure DevOps build pipeline as a power shell script task. The task completes without error but my build fails saying Cannot import the following key file: Image1.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_60E41B07B11F3C7E – Ziggler May 28 '19 at 18:28
1

The better way is that you can setup a on premise build agent and import the certificate to certificate store, then change build agent service account to the same account.

starian chen-MSFT
  • 33,174
  • 2
  • 29
  • 53
  • The thing is we do not want to limit this to a single PC or have to install an agent to all the PC's the idea was because the build needs some files from a design team for the design team to place those files in when they need a new build and just run that regardless of machine. But will give it a try regardless see how easy it is thanks for the advice – Harry Apr 24 '17 at 08:02
  • @AvalothOath You may consider key file without password, otherwise on premise build agent is needed. – starian chen-MSFT Apr 24 '17 at 09:01
  • Hmm Thanks for this pice of information. Will give this a go and if this seems the only solution will mark this as correct – Harry Apr 24 '17 at 09:20
0

Instead of using either an on premise build or loading the certificates on to the certificate stores on the build agent (which could be considered insecure) it is possible to overwrite the build task FileSign and construct one that uses a certificate file and password.

I have outlined the steps here: https://stackoverflow.com/a/55313239/2068626

Peter
  • 745
  • 6
  • 17
0

After failing to use methods in other answers, I found another way to use PowerShell script to import pfx certificate. my script was written for GitHub Actions but you can easily change the syntax to VSTS or Azure pipeline (using 'task: PowerShell@2' for azure pipeline for example). You may also like to update file path from github to your devops path. Also password can be replaced with a secured variable.

- name: Import certificate from the command-line
  shell: pwsh
  run: |
    $Secure_String_Pwd = ConvertTo-SecureString "<password>" -AsPlainText -Force
    Import-PfxCertificate -FilePath '${{github.workspace}}\<path>\project1_TemporaryKey.pfx' -CertStoreLocation Cert:\CurrentUser\My -Password $Secure_String_Pwd

Then build your Visual Studio ClickOnce project. The error should be gone.

After the build, you may like to remove the certificate from the machine. Here is a PowerShell example provided by Microsoft:

https://learn.microsoft.com/en-us/answers/questions/360772/powershell-commands-to-delete-personal-certificate.html

 $users = "user1","user2","user3","user4","user5"
 Get-ChildItem Cert:\CurrentUser\My | ForEach-Object {
     $ifkeep = $false
     foreach($user in $users){
         if($_.Subject -match $user){
             $ifkeep = $true
             break
         }
     }
     if($ifkeep -eq $false){
         Remove-Item $_
     }
 }
flywhc
  • 41
  • 7