using System.Security.Cryptography;
using Microsoft.AspNetCore.WebUtilities;
public static void Main()
var rng = new RNGCryptoServiceProvider();
foreach ((var decimalToStrFuncName, var decimalToStrFunc) in new (string, Func<decimal, string>)[] {
("G", x => x.ToString("G")),
("Bits", DecimalToBase64),
("b64.b64", DecimalToIntegralAndFractional)
foreach (var alg in new HashAlgorithm[] { new SHA1CryptoServiceProvider(), new HMACSHA1(key), new HMACSHA256(key) })
var priceId = "SA1:NZ:7BF00137-D4B9-47EB-9865-B6DBDB86CB11";
var linePrice = 1_000_000.123456m;
var priceIdAndLinePrice = $"{priceId}~{decimalToStrFunc(linePrice)}~";
var hash = Hash(alg, priceIdAndLinePrice);
var priceIdWithHash = priceIdAndLinePrice + WebEncoders.Base64UrlEncode(hash);
Console.WriteLine("{0} => {1},{2} is {3}, length: {4}",
priceIdAndLinePrice, alg.GetType().Name, decimalToStrFuncName, priceIdWithHash, priceIdWithHash.Length);
public static string DecimalToBase64(decimal value)
var bitsInt32 = new int[4];
var bitsInt8 = new byte[4 * 2];
decimal.GetBits(value, bitsInt32);
for (int i = 0; i < 4; i++) {
var bytesInt16 = BitConverter.GetBytes((short)(bitsInt32[i] >> 16));
if (BitConverter.IsLittleEndian)
Array.Reverse(bytesInt16);
Buffer.BlockCopy(bytesInt16, 0, bitsInt8, i * 2, 2);
return WebEncoders.Base64UrlEncode(bitsInt8);
public static string DecimalToIntegralAndFractional(decimal value)
decimal fraction = value % 1.0m;
decimal integral = value - fraction;
decimal fractionShifted = fraction;
while (Math.Abs(fractionShifted % 1.0m) > 0.0m) {
Console.WriteLine("{0} || {1}", integral, fractionShifted);
var integralBytes = Bytes(integral);
var fractionBytes = Bytes(fractionShifted);
Console.WriteLine("i: {0}, f {1}", integralBytes.Length, fractionBytes.Length);
var totalBytes = new byte[8];
Buffer.BlockCopy(integralBytes, 0, totalBytes, 0, 4);
Buffer.BlockCopy(fractionBytes, 0, totalBytes, 4, 4);
static string B64(byte[] bytes)
if (BitConverter.IsLittleEndian)
return WebEncoders.Base64UrlEncode(bytes);
static byte[] Bytes(decimal part)
if (part > int.MaxValue) {
return BitConverter.GetBytes((long)part);
} else if (part > short.MaxValue) {
return BitConverter.GetBytes((int)part);
return BitConverter.GetBytes((short)part);
public static byte[] Hash(HashAlgorithm alg, string message)
var utf8bytes = new UTF8Encoding().GetBytes(message);
return alg.ComputeHash(utf8bytes);