Search code examples
c#unit-testingx509certificatefilestream

How can you mock signature information returned from an exe file?


I've put in some validation to check an external exe file. It verifies the file by getting the FileSignatureInfo like this:

public static bool IsSigned(string filePath)
{
    using var fileStream = File.OpenRead(filePath);
    var signatureInfo = FileSignatureInfo.GetFromFileStream(fileStream);
    return signatureInfo.State == SignatureState.SignedAndTrusted;
}

However, now I want to test this. I figured System.IO.Abstractions would be the way to go. This doesn't seem to do quite what I want though. To try and get this to work I updated the method as follows:

public static bool IsSigned(IFileSystem fileSystem, string filePath)
{
    using var fileStream = fileSystem.File.OpenRead(filePath) as FileStream;
    var signatureInfo = FileSignatureInfo.GetFromFileStream(fileStream);
    return signatureInfo.State == SignatureState.SignedAndTrusted;
}

I then started to create a test like this:

[Test]
public void IsSignedReturnsFalseIfSignatureStateIsNotSignedAndTrusted()
{
    // Arrange
    var mockFileSystem = new MockFileSystem();
    var mockFileData = new MockFileData("Test content");
    var directory = @"C:\testDirectory";
    var fileName = "Test.exe";
    var filePath = Path.Combine(directory, fileName);

    mockFileSystem.AddDirectory(directory);
    mockFileSystem.AddFile(filePath, mockFileData);

    // Act
    var result = MyHelper.IsSigned(mockFileSystem, filePath);

    // Assert
    Assert.IsFalse(result);        
}

I know at this stage the test is flawed as I'm testing a mocked file with no certificate. I just wanted to make sure it was actually flowing through the validation as expected. However, what I found was that it failed when trying to get the signature info because fileStream was null. Turns out the as FileStream isn't working, as the Stream isn't null.

So my first question is, how can I convert the stream returned from File.OpenRead() to a FileStream? If that's a bit clunky / not recommended, then what's the best way to unit test validation around certificate signatures? My thought was to somehow mock FileSignatureInfo.GetFromFileStream(), but not sure if that's more trouble than it's worth.


Solution

  • Looks like this isn't something that you can mock, so I've worked around it. I've removed the need for the fileSystem and gone back to the original but I simply inject the signing requirements via a record I created called SigningRequirement:

    public static bool IsSigned(string filePath, SigningRequirement requirements)
    {
        using var fileStream = File.OpenRead(filePath);
        var signatureInfo = FileSignatureInfo.GetFromFileStream(fileStream);
    
        if(signatureInfo.State == requirements.SignatureState && signatureInfo.SigningCertifcate is not null)
        {
            // more checks against the certificate like thumbprint etc.
        }
    }