Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculating SHA256 hash different results under Windows and Linux

I have code that calculates the SHA256 integrity hash of a file from the web. For example, the integrity hash of jquery-3.7.1.min.js is /JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo= (source). I have some units tests to test the code, and that always worked fine. Now, I thinks since .NET 9, the tests fail on my Windows machine. In Github actions (Linux) these test still work fine. This is a simplified version of the code:

[Fact]
public void Test()
{
    // Arrange
    const string Expected = "/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=";
    var fileContents = TestResources.GetFileContents("MyTests.Resources.jquery-3.7.1.min.js"); // read from embedded resource
    var sha256Hasher = SHA256.Create();

    // Act
    var byteResult = sha256Hasher.ComputeHash(Encoding.UTF8.GetBytes(fileContents));

    // Assert
    var result = Convert.ToBase64String(byteResult);
    result.Should().Be(Expected);

/*
Expected result to be the same string, but they differ at index 0:
   ↓ (actual)
  "eqaw4I9IoPldjffqieTL…"
  "/JqT3SQfawRcv/BIHPTh…"
   ↑ (expected).
*/
}

public static string GetFileContents(string filename)
{
    var assembly = typeof(TestResources).GetTypeInfo().Assembly;
    using var resource = assembly.GetManifestResourceStream(filename);
    using var reader = new StreamReader(resource!);
    var result = reader.ReadToEnd();
    return result;
}

When I rewrite the test to fetch the actual CDN url, it works fine:

[Fact]
public async Task TestWithStream()
{
    // Arrange
    const string Expected = "/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=";
    var httpClient = new HttpClient();
    using var stream = await httpClient.GetStreamAsync(new Uri("https://code.jquery.com/jquery-3.7.1.min.js"));
    var sha256Hasher = SHA256.Create();

    // Act
    var byteResult = sha256Hasher.ComputeHash(stream);

    // Assert
    var result = Convert.ToBase64String(byteResult);
    result.Should().Be(Expected); // test passed!
}

This is not the situation I want in a unit test because it now depends on an external resource and internet access. So I think it might has something to do with encoding, how the resource is stored on disk, etc. But everything I try doesn't give the right result. Any ideas?

like image 659
Marthijn Avatar asked Nov 16 '25 06:11

Marthijn


1 Answers

The issue here is with the line endings. I ran the same code but when I used CRLF line endings, I got the Hash eqaw4I9IoPldjffqieTL/h7z0ejA9zc/fyXt+05KMl4=.

As Jon Skeet mentioned, this is likely because you have autocrlf=true on your Git Repo.

You can either disable this (you may need to re-commit a new version of the jQuery.-3.7.1.min.js file, I am not too sure).

An alternate solution is to force fileContents to use LF line endings. The below code will achieve that :

    var fileContents = TestResources.GetFileContents("MyTests.Resources.jquery-3.7.1.min.js").Replace("\r\n", "\n").Replace("\r", "\n");

This will ensure that fileContents is normalized to use LF Line endings.

I hope this helps, I am new to posting on StackOverflow so just trying to get the hang of it at the moment!

like image 50
nosniktaj Avatar answered Nov 18 '25 20:11

nosniktaj