33

We recently purchased an EV Code Signing Certificate from DigiCert to sign our MSI to get around the Windows SmartScreen warning message. The problem is that the Certificate was delivered to a USB Token that does not allow the exporting of the private key. Our build environment is on a hosted VM so there is no way we can plug the USB Token into the host VM.

Does anyone have a solution for using an EV Code Signing Certificate on a hosted VM? Do all Certificate Vendors deliver this type of certificate to a hardware token? How do you code sign an MSI in a virtual environment using this type of Cert?

Jim Carter
  • 331
  • 1
  • 4
  • 7

5 Answers5

7

The crypto provider being shipped with the SafeNet client is accessing the USB-Token using the SmardCardAPI (winscard.dll). Because SmartCards are also used for authentication/login purposes, the RDP stack will always redirect any access to the RDP client computer.

https://learn.microsoft.com/en-us/windows/security/identity-protection/smart-cards/smart-card-and-remote-desktop-services

For scenarios like code signing this behavior can be quite cumbersome. We are using a dedicated virtual machine for signing purposes and each developer having access to this machine should be able to perform the signing process. With COVID-19 and all developers working at home the idea to use the local USB port is not feasible. Our USB-Token is attached to the dedicated machine. Each time you connect via RDP to this machine the dongle is no longer accessible, because the SmartCardAPI will redirect the access.

There is a solution to this problem however: The SmartCard stack uses the API-Calls

ProcessIdToSessionId

WinStationGetCurrentSessionCapabilities

to determine if the current process is running in a RDP session. By injecting a DLL into the signtool and using the Detours framework you can hook these API calls and report a local session, so the SmardCardAPI will access the dongle connected to the remote virtual machine.

https://github.com/microsoft/Detours

The detoured functions are quite simple (code reduced to important stuff, so you can get the idea)

DWORD WINAPI ProcessIdToSessionIdLocal(DWORD dwProcessId, DWORD *pSessionId)
{
  OutputDebugString("Detoured ProcessIdToSessionId\r\n");
  if (pSessionId)
     pSessionId = 0;
  return TRUE;
}

BOOL WINAPI WinStationGetCurrentSessionCapabilitiesLocal(DWORD flags, DWORD *pOutBuffer)
{
   BOOL bResult;
   
   OutputDebugString("Detoured WinStationGetCurrentSessionCapabilities\r\n");
   bResult = TrueGetCurStationCapabilities(flags,pOutBuffer);
   if (bResult)
      *pOutBuffer = 0;
   return bResult;
}

BOOL WINAPI DllMain (haDLL, dwReason, lpReserved)
    HANDLE haDLL;
    DWORD dwReason;
    LPVOID lpReserved;
{
    LONG error;
    TCHAR cBuffer[160];
    
    wsprintf(cBuffer,"DllMain Entry %08x\r\n",dwReason);
    OutputDebugString(cBuffer);
    if (DetourIsHelperProcess()) {
        return TRUE;
    }

    if (dwReason == DLL_PROCESS_ATTACH) {
        OutputDebugString("Starting Detour API Calls \r\n");
        hStaDLL = LoadLibrary("WINSTA.DLL");
        TrueGetCurStationCapabilities = GetProcAddress(hStaDLL,"WinStationGetCurrentSessionCapabilities");
        
        DetourRestoreAfterWith();
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach((PVOID*)&TrueProcessIdToSessionId, ProcessIdToSessionIdLocal);
        DetourAttach((PVOID*)&TrueGetCurStationCapabilities, WinStationGetCurrentSessionCapabilitiesLocal);
        error = DetourTransactionCommit();

        if (error == NO_ERROR) {
        OutputDebugString("Successfully Detoured API Calls \r\n");

        }
        else {
            return FALSE;
        }
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach((PVOID*)&TrueProcessIdToSessionId, ProcessIdToSessionIdLocal);
        DetourDetach((PVOID*)&TrueGetCurStationCapabilities, WinStationGetCurrentSessionCapabilitiesLocal);
        error = DetourTransactionCommit();
        FreeLibrary(hStaDLL);
    }
    return TRUE;
}

Injecting a DLL into the signtool is also easy: just add a new entry to the IMPORTs section that will load the DLL with the detoured functions. This can be done using a tool named "Lord PE" (for 32bit executeables).

Alatun
  • 81
  • 2
  • 3
  • Thank you so much for the explanation and the code. It worked great in my application. I think the line "pSessionId = 0;" should be "*pSessionId = 0;" – dsmtoday Jun 02 '22 at 04:53
3

For anyone else who's had this problem, we used VNC Server to connect to the VM that had the token on it. It didn't work over RDP only VNC.

Apparently, VNC is like having console access to the box (as far as the EV Dongle is concerned).

CryptoJones
  • 734
  • 3
  • 11
  • 33
2

I had absolutely no problem with ours. We switched from a PFX (file) backed standard code signing certificate to a USB token backed EV code signing certificate in less than 15 minutes.

I simply set the USB token device to connect to the client OS in VMWare (see icons at bottom right of client OS window for devices), installed the necessary device drivers, set the options to require a password to unlock only once per session, then were good to go.

As to HOW to sign, you sign just like you would with any other certificate. If no other applicable code signing certificate is in the OS's certificate store, then you don't even have to specify where the certificate is.

I worried we would have troubles, but didn't. So, I don't think you will, and don't know why you would have troubles.

dyasta
  • 2,056
  • 17
  • 23
  • 1
    After isntallign certificate on the VM, I can usccessfully sign an assembly using cmd, but everytime the build agent (in our case Team City) tries to execute either a command line or call a batch file with a command line, it comes back with "SignTool Error: No certificates were found that met all the given criteria". The build agent is an administrator on that machine. have You encountered this ? If not maybe You could elaborate on Your setup ? – nlv Apr 11 '16 at 13:23
  • @NeilVarnas add `/debug`to your signtool cmds and work out on what basis signtool is eliminating all your certs in the TeamCity case. You might be able to adjust your signtool invocation to give hints to signtool as to which cert to pick. – azhrei Apr 12 '16 at 07:24
  • @NeilVarnas No, I never encountered anything like that. Sounds like something specific to your build agent. All my signing is through signtool.exe. – dyasta Apr 15 '16 at 06:29
  • 1
    (adding comment since update rejected) UPDATE: I later switched to Remote Desktop sessions for development and the rules there are this - The USB Token must be plugged into the client computer (the one you are on, not the VM you are remote logging into). It will not work if the USB Token is plugged in to the Host Machine instead of Client with Remote Desktop, it immediately disconnects the USB Token (by design I think). However, you can use TeamViewer or VNC and keep the Token on the Host. I like having it on the client, as I can Remote into different VMs and it will connect to each as I do. – dyasta Aug 16 '16 at 15:55
  • You need to install the usb token software/drivers inside the vm. i only had them on the machine where the token was plugged in and that did not work. installing them inside the vm did resolve this. – Thomas Woelfer May 11 '20 at 14:58
  • Which of the plenty VMware products was it? "In VMware" is a rather unspecific statement, given there are bare-metal hypervisors they sell as well as hosted products like Fusion, Workstation or Player. – 0xC0000022L Jan 23 '22 at 13:30
0

EV code signing certificates are required to use special hardware to store the private key. That's part of what makes them more expensive and more secure than standard certificates. My suggestion would be to sign the executable on the host machine using signtool.exe as a post-build step.

Normal code signing certificates do not have the hardware requirement, but they're not as "SmartScreen Filter Friendly" as the EV certificates.

Shannon Cook
  • 737
  • 1
  • 7
  • 16
-1

Some virtualization software including VMware allow USB devices to be redirected.

Yuhong Bao
  • 3,891
  • 1
  • 19
  • 20
  • 1
    ... and make sure you connect to your VM with vnc, not Remote Desktop – azhrei Aug 12 '15 at 05:20
  • @azhrei Do You know why the dongle appears to be disconnected when using Remote Desktop ? Once I use console window in vmware it appears to be okay (the certificate software sees the dongle ) – nlv Apr 11 '16 at 13:20
  • @NeilVarnas I don't know why - I hazard a guess that the dongle driver explicitly requires (and checks) you to be on the console for security purposes – azhrei Apr 12 '16 at 07:27