最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Calculating SHA256 hash different results under Windows and Linux - Stack Overflow

programmeradmin6浏览0评论

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(".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?

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/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?

Share Improve this question asked Mar 21 at 9:53 MarthijnMarthijn 3,4342 gold badges33 silver badges49 bronze badges 4
  • 8 Check the line endings of MyTests.Resources.jquery-3.7.1.min.js on your Windows machine. (My guess is that it's got CR-LF line endings, potentially due to autocrlf=true in your git repo, whereas on Linux it'll have LF endings. That's just a guess.) – Jon Skeet Commented Mar 21 at 10:13
  • 1 Have you checked the content of the embedded file? – shingo Commented Mar 21 at 10:15
  • 4 Very simple first test - print the file length in each case. I suspect you'll see a difference, which would mean that it's got nothing to do with sha256, and everything to do with the actual content being different. – Jon Skeet Commented Mar 21 at 10:15
  • Thanks @jon-skeet, it was the line endings indeed. Today I learned about the autocrlf setting :) – Marthijn Commented Mar 22 at 8:52
Add a comment  | 

1 Answer 1

Reset to default 4

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!

发布评论

评论列表(0)

  1. 暂无评论