A SHA-256 hash from a string in C# is produced by encoding the string as UTF-8 bytes and passing those bytes to the standard SHA256 algorithm from System.Security.Cryptography, which returns a 32-byte digest that you can format as 64 lowercase hexadecimal characters or 44 standard Base64 characters. The C# pattern is short — instantiate SHA256.Create(), call ComputeHash on a byte array from Encoding.UTF8.GetBytes, then convert the bytes to a printable form — but every step is sensitive to the exact bytes that get hashed. A trailing newline from a console write, a Unicode normalization difference, or a switch from UTF-8 to UTF-16 changes the bytes and therefore the digest, so a "wrong" result is almost always a byte-mismatch problem rather than a bug in the cryptography. The class implements the same algorithm specified in NIST FIPS 180-4 and RFC 6234, so any conformant SHA-256 tool will agree on the digest for identical bytes; you can confirm your C# result by recomputing the digest with a browser-based SHA256 Hash Generator that runs the same standard operation on the same UTF-8 text.
C# developers reach for SHA-256 for two common reasons: to fingerprint a string for caching or deduplication, or to verify the integrity of a downloaded artifact by comparing the locally computed digest to one published by the vendor. Both rely on the algorithm behaving the same on every platform, which is exactly what conformance to FIPS 180-4 guarantees. The next sections walk through the code, the output formats, the cross-check workflow, and the pitfalls that quietly change the digest.

How C# Computes a SHA-256 Hash from a String
The .NET runtime ships a FIPS 180-4-conformant SHA-256 implementation in System.Security.Cryptography.SHA256. The class inherits from HashAlgorithm and overrides ComputeHash to perform the SHA-2 padding, 512-bit block splitting, 64-word schedule expansion and eight 32-bit working-variable updates that the standard defines. When you call ComputeHash(byte[]), the runtime hashes every byte in the array in order, including any leading zeros, trailing carriage returns, and the byte order mark that some encoders prepend. The result is always 32 bytes, regardless of input length, which is why the printable hex form is always 64 characters.
The C# pattern treats a .NET string as a sequence of UTF-8 code units before hashing, because the algorithm operates on bytes, not on chars. Encoding.UTF8.GetBytes(string) is the standard bridge; Encoding.Unicode (UTF-16 LE) produces a different byte sequence and therefore a different digest for any non-ASCII string. For pure ASCII text the two encodings happen to match for the ASCII subset, but for accented letters, emoji or CJK characters they diverge and the hash changes. If you want a digest that another language or platform will reproduce, always hash the UTF-8 representation of the string.
C# SHA-256 Code: Hash a UTF-8 String to Hex or Base64
The minimal code path looks like this. Place it inside a method that has access to System.Security.Cryptography, System.Text and System:
using System.Security.Cryptography;
using System.Text;
using System;
byte[] inputBytes = Encoding.UTF8.GetBytes("hello world");
byte[] digestBytes = SHA256.Create().ComputeHash(inputBytes);
string hex = BitConverter.ToString(digestBytes).Replace("-", string.Empty).ToLowerInvariant();
string base64 = Convert.ToBase64String(digestBytes);
For the literal hello world (no trailing newline), hex becomes b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9, which you can convert to standard padded Base64 with the same Convert.ToBase64String call. The two strings describe the same 32 bytes; converting one to the other loses no information, and the lowercase-versus-uppercase hex choice is presentation only.
If you prefer a single-line helper that returns a hex string for logging or display, wrap the pattern in a static method:
public static string Sha256Hex(string input) {
using var sha = SHA256.Create();
byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input));
return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
}
The using declaration ensures the SHA256 instance is disposed and any internal buffer is released when the method returns. Convert.ToBase64String(digestBytes) gives you the standard padded Base64 representation that API fields and JWT components use, while BitConverter.ToString with the hyphen stripped gives you hex. Both formats describe the identical 32-byte digest.
Cross-Check Your C# Result with the Browser Tool
Once you have a 64-character hex digest from C#, the fastest way to confirm you computed the standard digest is to recompute it with a reference implementation on the exact same bytes. The SHA256 Hash Generator runs the standard SHA-256 algorithm in the browser, accepts UTF-8 text or file bytes, and shows both the hex and the Base64 form so you can compare either representation directly.
The cross-check takes five concrete steps:
- Open the SHA256 Hash Generator in a new browser tab.
- Switch to text mode and paste the same string your C# code hashed. For file mode, pick the same file you hashed locally.
- Generate the digest and copy the 64-character hex value shown by the tool.
- Compare the complete hex string character-for-character with the C# output. A match at all 64 positions confirms the algorithm and the input bytes agree.
- If your reference uses Base64, switch the tool to that view and compare the 44-character output instead.
Because the tool uses the same FIPS 180-4 operation as System.Security.Cryptography, any disagreement is a byte-level problem — encoding, hidden newline, normalization, or the wrong algorithm — not a difference in cryptography.
Why a C# SHA-256 Digest May Not Match a Reference
Most "wrong" C# SHA-256 outputs trace back to bytes rather than to the algorithm. The five causes below account for nearly every mismatch in practice:
- Encoding switch. Encoding.Unicode gives UTF-16 LE bytes; ASCII subsets match UTF-8, but anything outside ASCII (é, 中, 🚀) changes byte count and digest.
- Hidden newlines. Console.WriteLine appends \r\n on Windows, and many editors save files with a final newline that your code accidentally includes in the input.
- Hex letter case. Reference checksums sometimes publish uppercase hex. The bytes are identical, so the digest matches; only the letter casing differs.
- Unicode normalization. The same visible string can have multiple valid UTF-8 representations (composed versus decomposed forms). Hashing before normalization gives a different digest.
- Wrong algorithm. Raw SHA-256, HMAC-SHA-256 and a "SHA-256 of a salted string" all produce different digests for the same characters. Make sure both sides use raw SHA-256.
When debugging a mismatch, the cleanest move is to compute the digest of an empty string from both sides and compare; the empty-input digest starts with the eight characters e3b0c442 in both C# and the browser tool, which proves the algorithm and the byte ordering are aligned before you debug any real input.
SHA-256 Output Formats Compared
The same 32 bytes can be printed in several ways. The two formats most C# code meets are shown below.
| Format | Length | Character set | Typical use |
|---|---|---|---|
| Lowercase hex | 64 characters | 0–9, a–f | Published checksums, git commit hashes, vendor download pages |
| Standard padded Base64 | 44 characters | A–Z, a–z, 0–9, +, /, = | API payloads, JWT components, compact binary fields |
| Raw bytes | 32 bytes | Any byte value 0–255 | Internal API between trusted callers, storage in binary formats |
Hex and Base64 are presentation choices; they encode the same 32 bytes and produce the same digest. Changing hex letter case changes only the display, never the underlying bytes. When a system expects one format, paste from the matching view — most mismatches at the integration boundary are simply the wrong format selected rather than a broken implementation.
What SHA-256 Does Not Solve in C#
SHA-256 is a one-way digest, not encryption, so there is no key and no supported reverse operation. It also does not authenticate who produced the input: anyone can hash the same bytes and produce the same digest. For shared-secret authentication, HMAC-SHA-256 combines a secret key with the same SHA-256 primitive; for public-key origin verification, a digital signature scheme such as RSA-PSS or ECDSA is required.
Do not store user passwords by applying SHA-256 directly. The algorithm is intentionally fast, which makes brute-force guessing cheap. Account systems should hash passwords with a unique per-user salt and a memory-hard function such as PBKDF2, bcrypt, scrypt or Argon2, using parameters tuned for current hardware. SHA-256 remains the right tool for integrity checks, fingerprinting and checksum verification, where the goal is to detect accidental or hostile modification of a known input.
For empty input the standard still produces a valid digest — the empty byte string hashes to the well-known value beginning e3b0c442 — so the tool and C# both accept the empty case without special-casing. Spaces, carriage returns, the final newline from a text editor, and the choice between Unicode normalization forms are all bytes, and each one can change the resulting digest.
For a deeper look, see Generate a SHA-512 Hash the Right Way: Full 512 Bits.
For a deeper look, see UTF-8 Converter Online: Encode, Decode, and Verify Bytes.