您实际上并不打算返回Base64-of-Base16-of-hash
; 只是Base64-of-hash
. 我认为关于“这个和应该用十六进制表示”的部分。让你失望:该页面上的说明是为使用toolbox-script-csp
NPM 模块的 JS 开发人员编写的,尽管我同意编写它的人没有考虑非 JS 开发人员。
计算脚本内容的 SHA384 哈希和。该总和应以十六进制表示。
相反,请查看以下参考实现toolbox-script-csp
:
https://github.com/ampproject/amp-toolbox/blob/main/packages/script-csp/lib/calculateHash.js
function calculateHash(src, {algorithm = DEFAULT_ALGORITHM} = {}) {
const algo = algorithm.toLowerCase();
if (!SUPPORTED_ALGORITHMS.has(algo)) {
throw new Error(`Unsupported algorithm for CSP: ${algo}`);
}
if (typeof src === 'string') {
src = Buffer.from(src, 'utf8');
}
const hash = crypto.createHash(algo);
const data = hash.update(src);
const base64 = base64URLFormat(data.digest('base64'));
return `${algo}-${base64}`;
}
function base64URLFormat(base64) {
return base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
}
这可以以直接的方式转换为 C#/.NET,但是....
重要提示:请勿String
用于表示您将要散列和提供的 JS 脚本文件:由于解码和重新编码期间的元信息丢失,字符串的散列通常与原始文件不同,即使脚本的文字内容相同。例如,非规范化的 Unicode 字节在解码时被规范化,缺少前导字节顺序标记(Encoding.UTF8
默认情况下呈现,顺便说一句!),甚至根据您的环境进行换行转换\n
(\r\n
或反之亦然)设置。
...因此,下面的代码用于ReadOnlySpan<Byte>
表示 JS 脚本,而不是System.String
.
.NET Core 和 .NET 5+ 中的等价物是这样的:
public static async Task<String> CalculateHashForGoogleAmpAsync( FileInfo jsFile )
{
// DO NOT read the JS file into a String (and then re-encode it to Byte[] to get the hash)! Doing so will apply a potentially lossy Unicode transformation such that the resultant re-encoded bytes will be different to the source file bytes and so cause the hash to not match the source file.
// Instead just read the file's raw bytes and hash that (it's much simpler too!)
Byte[] bytes = await File.ReadAllBytesAsync( jsFile.FullName ).ConfigureAwait(false);
return CalculateHashForGoogleAmp( js: bytes );
}
public static String CalculateHashForGoogleAmp( ReadOnlySpan<Byte> js )
{
Byte[] hash = SHA384.HashData( js );
String hashBase64 = Convert.ToBase64String( hash );
String hashBase64Url = hashBase64.TrimEnd('=').Replace('+', '-').Replace('/', '_'); // Or use `Microsoft.AspNetCore.WebUtilities.WebEncoders.Base64UrlEncode`
return "sha384-" + hashBase64Url;
}
在 .NET Framework 4.x 和 .NET Standard 2.0 中,ReadAllBytesAsync
andstatic Byte[] HashData()
方法不可用,但通过重新实现这些方法可以直接解决......这超出了这个问题的范围,所以这里是一个非异步版本:
public static String CalculateHashForGoogleAmp( FileInfo jsFile )
{
Byte[] sha384Hash;
using( SHA384 algo = SHA384.Create() )
using( FileStream fs = File.OpenRead( jsFile.FullName ) )
{
sha384Hash = algo.ComputeHash( fs );
}
return FormatHashForGoogleAmp( sha384Hash );
}
private static String FormatHashForGoogleAmp( Byte[] sha384 )
{
String hashBase64 = Convert.ToBase64String( sha384 );
String hashBase64Url = hashBase64.TrimEnd('=').Replace('+', '-').Replace('/', '_'); // Or use `Microsoft.AspNetCore.WebUtilities.WebEncoders.Base64UrlEncode`
return "sha384-" + hashBase64Url;
}