vcsjones / AzureSignTool

SignTool Library and Azure Key Vault Support
MIT License
275 stars 88 forks source link

AzureSignTool does not contain the same functionality as SignTool.exe #169

Open ncook-hxgn opened 2 years ago

ncook-hxgn commented 2 years ago

I'm trying to replace SignTool.exe with AzureSignTool so that I can keep my signing certificate and associated secrets more secure when used in Azure DevOps Pipelines.

I have discovered that AzureSignTool does not have the same functionality as SignTool. Some of our products ship with JavaScript and VBS files, and we'd like to continue signing these, but AzureSignTool throws an error when we try to sign them.

This has been reported by others previously:

It would be good if these limitations were at least documented, which would prevent wasted development effort. Better would be to implement these features?

Edited to add:

As an alternative, software publishers and enterprises can use the Microsoft Windows Certificate Server—a feature of some versions of Windows Server®—to issue certificates for use within a managed network or test environment.

  • For managed network scenarios, an internal CA is useful for signing internal line-of-business (LOB) applications, scripts, or macros.

From the whitepaper found here, emphasis mine: Code-Signing Best Practices

engycz commented 1 year ago

After a day of investigating the issue, debugging and reverse engineering system DLLs I found the cause of the problem. The call chain starts at function mssign32.SignerSignEx3 and among other things, the ConvertTextToUnicode function in wshext.dll is called in the call chain.

int __fastcall ConvertTextToUnicode(int a1, int a2, BSTR *a3)
{
  int v3; // esi
  int v4; // eax
  OLECHAR *v5; // ecx
  OLECHAR *v7; // ecx
  int v8; // [esp-18h] [ebp-3Ch] BYREF
  _DWORD v9[5]; // [esp-14h] [ebp-38h] BYREF
  int v10; // [esp+0h] [ebp-24h] BYREF
  int v11; // [esp+Ch] [ebp-18h]
  int v12; // [esp+10h] [ebp-14h]
  int v13; // [esp+14h] [ebp-10h]
  LPVOID ppv; // [esp+18h] [ebp-Ch] BYREF
  DWORD dwErrCode; // [esp+1Ch] [ebp-8h]
  BSTR bstrString; // [esp+20h] [ebp-4h] BYREF

  v3 = 0;
  v12 = a2;
  v11 = a1;
  dwErrCode = 0;
  v13 = 0;
  ppv = 0;
  bstrString = 0;
  if ( CoInitialize(0) >= 0 )
  {
    v13 = 1;
...

The problem is in the call to the CoInitialize function. According to documentation https://learn.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize Initializes the COM library on the current thread and identifies the concurrency model as single-thread apartment (STA). However, the signature function is called from Task, which initializes the thread as an MTA apartment.

The return value from the CoInitialize function call is then 0x80010106 (RPC_E_CHANGED_MODE) and the ConvertTextToUnicode fails. obrazek

The issue can be fixed by calling mssign32.SignerSignEx3 from STA thread. I tested it with this my dirt fix https://github.com/engycz/AzureSignTool/commit/4de63af21544fca1b1950338566984dab267011c and now I'm able to sign VBS and js script

WScript.Echo "XX"

'' SIG '' Begin signature block
'' SIG '' MIIt6wYJKoZIhvcNAQcCoIIt3DCCLdgCAQExDzANBglg
'' SIG '' hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor
'' SIG '' BgEEAYI3AgEeMCQCAQEEEE7wKRaZJ7VNj+Ws4Q8X66sC
...

Is there anyone (@vcsjones) with better knowledge of C# to finish the fix?

This comment is also relevant to #124 and #137

ncook-hxgn commented 1 year ago

@engycz amazing stuff, thank you for looking into this

air2 commented 3 months ago

Are there any plans to implement this fix?