Embed user-specific data into signed by wildcard installer at boot

I have a Windows Forms application installed using InnoSetup that my users download from my site. They install this software on multiple PCs.

The application is accessing a web API, which must be able to identify the user. I am creating a web application where the user can login and download the application. I would like to embed the universally unique identifier in the installer so that they cannot login again after installation. I want them to download and run setup.exe and the application will take care of itself.

I am considering a couple of options:

  • Paste the custom UUID into the setup.exe file and code-sign on demand on the web server. Downside: not sure how to do it?
  • Paste custom UUID into installer filename (e.g. setup_08adfb12_2712_4f1e_8630_e202da352657.exe)
    Downside: This is ugly and will fail if the installer is renamed
  • Wrap the installer and settings file containing the UUID in a self extracting zip

How can I embed user data into a signed executable on a web server?

+2


source to share


1 answer


All PE is unsigned. You can insert data into a signed PE by adding it to the signature table. This method is used by Webex and other tools to provide one-click meeting utilities.

Technically, PKCS # 7 signature has a list of attributes that are specifically designated as failing that can be used, but I don't know how easy it is to write these fields without a full PE parser. Fortunately, we already have signtool

, and adding an extra signature to an already signed file is a non-destructive operation that uses unauthenticated fields.

I have put together a demo that uses this method to pass data from an MVC website to a loaded windows executable.

The procedure is as follows:

  • Start with authenticated signature and timestamped exe generated by standard processes
    (should be able to run without dependencies - ILMerge or similar)
  • Copy unused exe to temporary file
  • Create an ephemeral code signing certificate that includes ancillary data as an X509 extension
  • Use signtool

    to add auxiliary signature to temp file
  • Return temp file to client, delete it after download finished

On the client side application:

  • Reads signing certificates from the currently executable exe
  • Finds a certificate with a known name
  • Finds an extension using a known OID
  • Changes its behavior based on the data contained in the extension


The process has several advantages:

  • No monkeys with PE layout
  • The Shared Trust Signing Certificate can remain offline (or even in HSM), only ephemeral certificates are used on the web server.
  • Outbound traffic is not generated from the webserver (as would be needed if the timing was in progress)
  • Fast (<50ms for 1MB exe)
  • Can be run from IIS

Using

Finding Client Side Data ( Demo Application \ MainForm.cs )

try
{
    var thisPath = Assembly.GetExecutingAssembly().Location;
    var stampData = StampReader.ReadStampFromFile(thisPath, StampConstants.StampSubject, StampConstants.StampOid);
    var stampText = Encoding.UTF8.GetString(stampData);

    lbStamped.Text = stampText;
}
catch (StampNotFoundException ex)
{
    MessageBox.Show(this, $"Could not locate stamp\r\n\r\n{ex.Message}", Text);
}

      

Server Side Typography ( Demo Site \ Controllers \ HomeController.cs )

var stampText = $"Server time is currently {DateTime.Now} at time of stamping";
var stampData = Encoding.UTF8.GetBytes(stampText);
var sourceFile = Server.MapPath("~/Content/Demo Application.exe");
var signToolPath = Server.MapPath("~/App_Data/signtool.exe");
var tempFile = Path.GetTempFileName();
bool deleteStreamOpened = false;
try
{
    IOFile.Copy(sourceFile, tempFile, true);
    StampWriter.StampFile(tempFile, signToolPath, StampConstants.StampSubject, StampConstants.StampOid, stampData);

    var deleteOnClose = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, 4096, FileOptions.DeleteOnClose);
    deleteStreamOpened = true;
    return File(deleteOnClose, "application/octet-stream", "Demo Application.exe");
}
finally
{
    if (!deleteStreamOpened)
    {
        try
        {
            IOFile.Delete(tempFile);
        }
        catch
        {
            // no-op, opportunistic cleanup
            Debug.WriteLine("Failed to cleanup file");
        }
    }
}

      

+4


source







All Articles