Visual Studio Code Signing Problems

Error Importing Key in Visual Studio 2019

Takeaways

1. Error Importing Key into Visual Studio 2019 can be solved by using a Microsoft proprietary certificate format for code signing.
2. SmartScreen warning can be solved by using an EV code signing certificate.
3. The “Unknown Publisher” problem resulting from the ClickOnce bootstrapper remains unsolved.

I’m preparing for publishing my CISSP test bank, WUSON Practice Field for Wentz QOTD using Visual Studio 2019 Professional. It is signed by a standard code signing certificate issued by Sectigo.

Using private and public keys to sign and verify a strong assembly
Using private and public keys to sign and verify a strong assembly
(Source: https://flylib.com/books/en/4.253.1.138/1/)
I also wrote a post, Code Signing Certificate, about how to buy a code signing certificate from a certificate vendor and use the openssl utility to handle keys and certificates. Another post, Free ZeroSSL for 3 Months, is about how to apply for a free SSL certificate and install it on an IIS server.

Error Importing Key into Visual Studio 2019

The following is my code signing certificate, which is installed and working fine. However, I cannot import it into Visual Studio to sign my code using its GUI.

I kept encountering an error when importing my .pfx file into Visual Studio 2019. After a long journey of Googling and researching, I gave up to solve this common problem. I was wondering why so many people encounter the same problem since years ago. It’s worthy of noting the Visual Studio requires a flat .pfx file, which means CA’s certificates should not be packaged into the same file.

The .pfx file cannot include certificate chaining information. If it does, the following import error will occur: Cannot find the certificate and private key for decryption. To remove the certificate chaining information, you can use Certmgr.msc and disable the option to Include all certificates when exporting the *.pfx file.

Source: Microsoft

Even though I noticed the above reminder, my .pfx file still cannot work at all. Luckily, this post from COMODO saved my life, which provides the following commands to generate the specific format that meets Microsoft’s code signing requirement:

> openssl pkcs12 -in certfile.pfx -out backupcertfile.key
> openssl pkcs12 -export -out certfiletosignwith.pfx -keysig -in backupcertfile.key

Who Knows This? Microsoft!

In the Microsoft world, there are two types of keys: AT_KEYEXCHANGE and AT_SIGNATURE. I have done less with more! It has wasted me 3 workdays to learn this lesson.

-keyex|-keysig

specifies that the private key is to be used for key exchange or just signing. This option is only interpreted by MSIE and similar MS software. Normally “export grade” software will only allow 512 bit RSA keys to be used for encryption purposes but arbitrary length keys for signing. The -keysig option marks the key for signing only. Signing only keys can be used for S/MIME signing, authenticode (ActiveX control signing) and SSL client authentication, however due to a bug only MSIE 5.0 and later support the use of signing only keys for SSL client authentication.

Source: OpenSSL

Strong/Strongly Named Assemblies

After regenerating my .pfx file for code signing only (using the -keysig argument), my .NET assemblies finally got strongly named!

Strong Named Assembly
Strong Named Assembly

The “Delay sign only” Feature

Not every developer gets access to the organizational private key to sign the code. In this situation, the public key portion of the key pair can be attached to the code file. Digital signature generated by the corresponding private key can be added later. Delay-sign also happens in a class library project as well because the output assembly will be shared across projects. To delay-sign an assembly, Microsoft provides the following procedure:

  1. Get the public key portion of the key pair from the organization that will do the eventual signing. Typically this key is in the form of an .snk file, which can be created using the Strong Name tool (Sn.exe) provided by the Windows SDK.
  2. Annotate the source code for the assembly with two custom attributes from System.Reflection:
  3. The compiler inserts the public key into the assembly manifest and reserves space in the PE file for the full strong name signature. The real public key must be stored while the assembly is built so that other assemblies that reference this assembly can obtain the key to store in their own assembly reference.
  4. Because the assembly does not have a valid strong name signature, the verification of that signature must be turned off. You can do this by using the –Vr option with the Strong Name tool.
  5. Later, usually just before shipping, you submit the assembly to your organization’s signing authority for the actual strong name signing using the –R option with the Strong Name tool.

ClickOnce and Code Signing

Three targets shall be signed to publish software through the ClickOnce feature:

  1. Deployment manifest: WUSON Practice Field for Wentz QOTD.application
  2. Application manifest: WUSON Practice Field for Wentz QOTD.exe.manifest
  3. Binary Code files: WUSON Practice Field for Wentz QOTD.exe, setup.exe, and other code

Confusing Naming

The naming of the deployment manifest is confusing, as Microsoft states “the name of a deployment manifest file must end with the .application extension.”

The .application file (deployment manifest) documents the basic profile of the publisher, how the application is installed (online or offline), the version of the .NET framework, and the dependent “application manifest” (with a .manifest file extension). The .manifest file (application manifest) records all the dependent assemblies and resources. Both manifests may or may not be signed by the ClickOnce feature. If so, XML tags, <publisherIdentity> and <Signature>, are appended to the application and deployment manifests.

When a ClickOnce bootstrapper starts, the Windows Defender SmartScreen will pop up and warn the user if the bootstrapper is not signed by an EV code signing certificate. After launched, it downloads the deployment manifest (.application) first from a website if the application is configured to be installed online, and then the application manifest (.manifest). If both manifests are successfully downloaded, the bootstrapper will display the application name, the codebase, and the publisher’s identity. Users can click the install button to proceed.

SmartScreen Security Warning

The Windows Defender SmartScreen feature doesn’t trust newly issued standard code signing certificates and pops up a warning to users. However, there are two ways to avoid the SmartScreen complaints:

  1. Use an EV code signing certificate (more costs)
  2. Accumulate more installations to get credits in the SmartScreen database (more time)

The “Unknown Publisher” Problem

Even though the user clicks the “More info” link and the “Run anyway” button to execute setup.exe, the publisher identity in the deployment or application manifest is not recognized, given the manifest is also digitally signed using the ClickOnce feature of Visual Studio 2019.

My application is signed by a standard code signing certificate, and the manifests have my publisher identity and signature. However, the bootstrapper still shows “Unknown Publisher.” This tough problem remains unsolved until now.

The ClickOnce deployment shows “Unknown Publisher.”
The ClickOnce deployment shows “Unknown Publisher.”

According to Adam Caviness on StackOverflow, the reason this occurs is due to a couple of factors:

  1. ClickOnce displays “Unknown Publisher” when using a SHA2 Authenticode certificate.
  2. On January 1st 2016 Windows deprecated SHA1 for Authenticode signing/code signing. Windows SmartScreen technology thus displays “Unknown Publisher” when using a SHA1 Authenticode certificate.

He added, “this is in effect a catch-22, you need SHA1 for ClickOnce publisher verification and SHA2 for SmartScreen. Nice.”

Custom Installer

Custom ClickOnce Installer
Custom ClickOnce Installer

In the end, I gave up using the default Microsoft bootstrapper and implemented a custom installer because it takes more time for troubleshooting. To avoid the browser interferes with the download process, I added a sha256 signature to the setup.exe.

<Target Name="SignSetupFile" AfterTargets="AfterPublish">
	<PropertyGroup>
		<TimestampServerUrl>http://timestamp.sectigo.com</TimestampServerUrl>
		<ApplicationDescription>WUSON Practice Field for Wentz QOTD</ApplicationDescription>
		<SigningCertificateCriteria>/sha1 "28b23d498c4f539032f23d29ab85b386b6d0443b"</SigningCertificateCriteria>
	</PropertyGroup>
	<ItemGroup>
		<SignableFiles Include="$(ProjectDir)bin\$(ConfigurationName)\app.publish\$(TargetName)$(TargetExt)" />
		<SignableFiles Include="$(ProjectDir)bin\$(ConfigurationName)\app.publish\setup.exe" />
	</ItemGroup>
	<Exec Command="&quot;$(ProjectDir)..\Bin\SignTool&quot; sign $(SigningCertificateCriteria) /d &quot;$(ApplicationDescription)&quot; /tr &quot;$(TimestampServerUrl)&quot; /td sha256 /fd sha256 /as  &quot;%(SignableFiles.Identity)&quot;" />
</Target>

Time Server and Time-stamping

A code signing certificate will expire, but the signed software apparently survives longer. To avoid expired certificate voids the software, time-stamping provides a solution. To add a timestamp, we need a time server when signing the code. There are two popular time-stamping protocols: RFC 3161 and Authenticode.

RFC 3161 time stamping is used by SignTool (using the “/tr” parameter) and other applications (such as jarsigner). Our time stamping server automatically selects the appropriate signature algorithm (RSA/SHA-256, RSA/SHA-384, or RSA/SHA-512) with which to sign each time-stamp, based on the hash algorithm you specify (e.g., via SignTool‘s “/td” parameter).

Authenticode time stamping is used by older versions of SignTool (using the “/t” parameter) and SignCode. Due to this protocol’s design, it is not possible for our time stamping server to automatically select the appropriate signature algorithm. We currently use RSA/SHA-384 by default. However, you may request a different signature algorithm by appending “?td=<hash_algorithm>” to the URL. e.g., http://timestamp.sectigo.com?td=sha256.

Source: Sectigo

Conclusion

  • Error Importing Key: Visual Studio 2019 uses a proprietary certificate format for code signing. Use the openssl -keysig argument can solve this problem.
  • SmartScreen: It’s confirmed that an EV code signing certificate can get immediate credits and avoid complaints from the Windows SmartScreen feature. As WUSON Practice Field for Wentz QOTD is still under development, I would choose to accumulate more installations for trusted publisher credits.
  • Unknown Publisher: The reason why the publisher identity in the deployment manifest, signed using the ClickOnce feature, is not respected or recognized (the unknown publisher problem) by the bootstrapper is still under investigation. I appreciate your feedback and guidance on this problem. I currently implemented a custom installer (the most common way to deploy an application) and gave up the default Microsoft bootstrapper because it took more time for troubleshooting.

References

  1. How to Sign ClickOnce Deployment if ClickOnce Code Signing Function in Visual Studio Doesn’t Work
  2. ClickOnce Reference
  3. Walkthrough: Manually deploy a ClickOnce application
  4. Walkthrough: Create a custom installer for a ClickOnce application
  5. Walkthrough: Manually deploy a ClickOnce application that does not require re-signing and that preserves branding information
  6. MSBuild targets
  7. Trusted Application Deployment overview
  8. How to: Sign application and deployment manifests
  9. ClickOnce and Authenticode
  10. Fix: Cannot import the following key file
  11. Cannot import the following key file fix
  12. Delay-sign an assembly
  13. SignTool
  14. Time Stamp Server & Stamping Protocols for Digital Signatures/Code Signing
  15. Configuring ClickOnce Trusted Publishers
  16. Visual Studio: add After-Publish script
  17. Strong Name sn.exe: Failed to install key pair — Object already exists
  18. Authenticode Code Signing with Microsoft SignTool
  19. Create and use strong-named assemblies
  20. ClickOnce de-signs our executable and says “Unknown Publisher”
  21. Microsoft SmartScreen & Extended Validation (EV) Code Signing Certificates
  22. Security, versioning, and manifest issues in ClickOnce deployments
  23. ClickOnce deployment manifest
  24. ClickOnce application manifest
  25. Eight Evil Things Microsoft Never Showed You in the ClickOnce Demos (and What You Can Do About Some of Them)
  26. Three ways to tell if a .NET Assembly (DLL) has Strong Name
  27. Error: “Cannot import the following key file: mykey.pfx. The key file may be password protected.” in Microsoft Visual Studio 2008 – 2015
  28. .NET Key types: AT_SIGNATURE & AT_KEYEXCHANGE
  29. openssl pkcs12
  30. Sign the assembly with Visual Studio without going crazy
  31. Creating a Code-Signing Certificate using the Key Storage Provider

Leave a Reply