ObeTools 1.7.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package ObeTools --version 1.7.0
                    
NuGet\Install-Package ObeTools -Version 1.7.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ObeTools" Version="1.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ObeTools" Version="1.7.0" />
                    
Directory.Packages.props
<PackageReference Include="ObeTools" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ObeTools --version 1.7.0
                    
#r "nuget: ObeTools, 1.7.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ObeTools@1.7.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ObeTools&version=1.7.0
                    
Install as a Cake Addin
#tool nuget:?package=ObeTools&version=1.7.0
                    
Install as a Cake Tool

ObeTools

C# .NET library (netstandard2.1 and net8.0) to help any developer in their project. It collects what I have needed in every project over 10+ years of work.

It has many static classes:

  • 1 ⇒ Logger: simple thread-safe daily text-file logger (no lost lines across threads or processes)
  • 2 ⇒ SecretProtector: authenticated encryption (AES-256-GCM) for tokens and links, with purpose binding and key rotation
  • 3 ⇒ EncryptHasher: SHA/HMAC hashing, PBKDF2 password hashing, constant-time compare, random numeric codes, safe base64 (and the legacy Encrypt/Decrypt obfuscation)
  • 4 ⇒ ExcelHelper: create/read Excel files (.xlsx) with a modern design, multiple sheets and typed cells
  • 5 ⇒ TLVEncoding: tag-length-value encoding (UTF-8 text and binary), as used by ZATCA QR codes
  • 6 ⇒ VATHelper: calculate VAT
  • 7 ⇒ FileHelper: detect a file's type from its content (base64 or bytes)
  • 8 ⇒ NumberHelper: amounts to words in English and Arabic, and rounding
  • 9 ⇒ Base1024Encoding: encode numbers and strings with a 1,024-character alphabet (compact in characters)

Logger

Logger.LogInfo("Orders", @"D:\Logs", "Order created", order, nameof(Create));
string text = Logger.LogException("Orders", @"D:\Logs", ex, "Checkout failed");

// Need to know whether the entry reached the disk?
bool written = Logger.TryLogInfo("Orders", logDir, "Order created", order, nameof(Create));

Each entry goes to "{logName} yyyy-MM-dd - Info.txt" (or - Exception.txt) in this exact shape, which log scanners may rely on:

<blank line>
StringTime: HH:mm:ss:ff |  Caption: … |  Message: … |  Method: … |  FileName: … |  LineNumber: …

Settings (all static, all optional):

Setting Default Meaning
WriteTimeoutMs 2000 How long one entry waits for another writer or a reader holding the file
UseCrossProcessMutex false on Windows, true elsewhere Serialise processes sharing a file with a named mutex
IncludeStackTrace false Append \| StackTrace: … (full exception text) after LineNumber
IncludeDatabaseDetail true Include PostgreSQL Detail (may contain row values / customer data)
RetentionDays null Delete this log's daily files older than N days
WriteFailed event Raised with (path, exception) when an entry could not be written

SecretProtector

byte[] key = SecretProtector.ParseKey(Environment.GetEnvironmentVariable("APP_PROTECTOR_KEY")); // 32 bytes, base64
string token = SecretProtector.Protect("order:42", key, purpose: "waybill-link");               // "v2.…", URL-safe

if (SecretProtector.TryUnprotect(token, key, "waybill-link", out var value)) { /* trusted */ }

// Key rotation: newest first
SecretProtector.TryUnprotect(token, new[] { newKey, oldKey }, "waybill-link", out value);

A token only opens with the same key and purpose, and any change to it makes TryUnprotect return false. Create a key once with Convert.ToBase64String(SecretProtector.GenerateKey()) and keep it in your secret store.

Security notes

  • EncryptHasher.Encrypt / Decrypt / ToURLToken / FromURLToken are obfuscation, not encryption. The key is a constant in this public repository and package, so anyone can decrypt and forge these tokens, and there is no integrity check. They are kept (output unchanged) so existing links keep working, and are marked [Obsolete]. Use SecretProtector for anything secret or anything a client must not be able to forge.
  • HashSha512 / HashSha256 are fine for high-entropy values (API keys). For human passwords use HashPassword / VerifyPassword (PBKDF2-SHA256, 600,000 iterations, random salt).
  • Compare hashes and HMACs with EncryptHasher.FixedTimeEquals, not ==.
  • StripTags (formerly RemoveJavaScript) removes tags; it is not an XSS defence. HTML-encode output instead.
  • Use GenerateNumericCode for OTPs; it is cryptographically random.

Numbers and VAT

NumberHelper.NumberToWords(1234.56, NumberWordType.English, "ريال", "SAR", "هللة", "Halala");
// "One Thousand Two Hundred Thirty Four SAR and Fifty Six Halala"
NumberHelper.NumberToWords(1234.56, NumberWordType.Arabic, "ريال", "SAR", "هللة", "Halala");
// "ألف ومئتين وأربعة وثلاثون ريال وستة وخمسون هللة"

decimal vat = NumberHelper.RoundMoney(1.90m * 0.15m);                    // 0.29 (half away from zero)
var (net, vatAmount, gross) = VATHelper.SplitVat(115m, 15, amountIncludesVat: true); // 100.00, 15.00, 115.00

NumberHelper.Round(double, digits) keeps its historical behaviour (binary double, banker's rounding: Round(0.285, 2) = 0.28). Use RoundMoney/SplitVat (decimal, half away from zero) for new money code.

Excel

ExcelHelper.WriteExcel(stream, "Invoices", headers, rows /* List<object[]>: numbers and dates keep their type */,
    new ExcelWriteOptions { RightToLeft = true, Properties = new ExcelDocumentProperties { Company = "My Co" } });
List<string[]> cells = ExcelHelper.GetCells(stream, skipFirstRow: true);

Licence: ExcelHelper uses EPPlus, which is free for non-commercial use only (EPPlus 5+). The host application should declare its licence before calling it, e.g. ExcelPackage.LicenseContext = LicenseContext.Commercial;. ObeTools never overwrites a licence you set; if none is set it falls back to NonCommercial (turn that off with ExcelHelper.UseNonCommercialLicenseWhenUnset = false).

Build

dotnet test ObeTools.sln -c Release
dotnet pack ObeTools.csproj -c Release -o artifacts

See CHANGELOG.md for behaviour changes between versions.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on ObeTools:

Package Downloads
ObeTools.AspNetCore

Help any .Net developer

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.7.1 111 10/10/2026
1.7.0 101 10/9/2026
1.6.7 276 6/25/2026
1.6.6 181 3/26/2026
1.6.5 161 3/24/2026
1.6.4 179 3/24/2026 1.6.4 is deprecated because it has critical bugs.
1.6.3 368 12/15/2025
1.6.2 366 11/16/2025
1.6.1 484 4/13/2025
1.6.0 350 1/16/2025
1.5.61 402 10/1/2024
1.5.8 396 4/13/2025
1.5.7 324 1/15/2025 1.5.7 is deprecated because it has critical bugs.
1.5.6 339 10/1/2024 1.5.6 is deprecated because it has critical bugs.
1.5.5 330 8/28/2024
1.5.4 352 8/28/2024 1.5.4 is deprecated because it has critical bugs.
1.5.3 316 8/4/2024
1.5.2 485 8/29/2023
1.5.1 282 8/19/2023
1.5.0 312 7/19/2023
Loading failed