Embed user-specific data into an authenticode signed installer on download

前端 未结 1 1386
天涯浪人
天涯浪人 2020-12-10 20:08

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

The application t

相关标签:
1条回答
  • 2020-12-10 20:44

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

    Technically, the PKCS#7 signature has a list of attributes that are specifically designated as unauthenticated which could be used, but I know of no easy way to write to these fields without a full PE parser. Luckily, we already have signtool, and adding an additional signature to an already signed file is a non-destructive operation that uses the unauthenticated fields.

    I put together a demo which uses this technique to pass data from an MVC website to a downloadable windows forms executable.

    The procedure is to:

    1. Start with the authenticode signed and timestamped exe produced by standard processes
      (must be able to run without dependencies - ILMerge or similar)
    2. Copy the unstamped exe to a temp file
    3. Create an ephemeral code signing certificate which includes the auxiliary data as an X509 extension
    4. Use signtool to add the auxiliary signature to the temp file
    5. Return the temp file to the client, delete it after the download completes

    On the client side, the app:

    1. Reads the signing certificates from the currently executing exe
    2. Finds the certificate with a known subject name
    3. Finds the extension with a known OID
    4. Alters its behavior based off of the data contained in the extension

    The process has a number of advantages:

    • No monkeying with the PE layout
    • The publicly trusted code signing certificate can stay offline (or even in an HSM), only ephemeral certificates are used on the web server
    • No outbound traffic is generated from the web server (as would otherwise be required if timestamping were performed)
    • Fast (<50ms for a 1MB exe)
    • Can be run from within IIS

    Usage

    Client side retrieval of 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 stamping (Demo Website\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");
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题