在 Java 中產生 SHA-256 雜湊值的方式,是將 UTF-8 位元組傳入以標準演算法名稱 "SHA-256" 設定的 java.security.MessageDigest,接著把產生的 32 位元組摘要格式化為 64 個小寫十六進位字元,或是標準填補的 Base64。相同的輸入位元組一定會產生相同的摘要,但無法從摘要反推回原始訊息。SHA-256 屬於 NIST FIPS 180-4 所規範的 SHA-2 家族,廣泛用於檔案完整性檢查、建置驗證,以及數位簽章的預映像。Java 的平台實作會將底層的區塊排程與壓縮委派給瀏覽器透過 Web Cryptography 暴露的同一個 FIPS 180-4 程式,因此只要兩端處理相同的 UTF-8 位元組,MessageDigest 產生的雜湊值會與瀏覽器產生器回傳的摘要逐位元組相符。針對一次性驗證工作——比對 Java 輸出與已發布的校驗和、除錯不相符的情形,或是產生摘要讓非程式設計人員驗證——瀏覽器的 SHA-256 Hash Generator 會回傳精確的 64 字元 Hex 值,你可以直接貼到訊息或建置清單中。

SHA-256 摘要的樣貌
SHA-256 一律輸出恰好 256 個位元,也就是 32 位元組,與輸入長度無關。Java 透過 MessageDigest.digest() 將這 32 個位元組以 byte 陣列的形式回傳。為了讓摘要易於閱讀,同樣的 32 位元組會以兩種標準編碼呈現:
| 表示方式 | 長度 | 用途 |
|---|---|---|
| 小寫 Hex | 64 個字元 | 每個位元組轉成兩個十六進位數字(0–9、a–f);已發布校驗和與下載頁面的正式格式。 |
| 填補 Base64 | 44 個字元 | RFC 4648 字元集並加上尾端 = 填補;當摘要需嵌入 JSON、JWT 或其他純文字協定時相當實用。 |
| 原始位元組 | 32 位元組 | Java 直接回傳的內容;HMAC、RSA 簽章,以及任何會消化摘要的密碼學作業都會用到。 |
改變 Hex 形式的字母大小寫(例如改用大寫 A–F 而非小寫 a–f),只會影響呈現方式。底層的 32 位元組完全相同,任何在比對前先將輸入轉為小寫的驗證程式,仍然會看到相符結果。
該標準同時定義了空輸入的摘要。空位元組字串的雜湊值為 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,這是最常被引用的 SHA-256 測試樣本,也是任何實作是否正確接入演算法的第一道可靠檢查。
在 Java 中使用 MessageDigest 產生 SHA-256
Java 透過 java.security.MessageDigest 提供符合 FIPS 的 SHA-256 實作。標準模式是以名稱請求演算法、將想要雜湊的確切位元組餵入,然後將得到的位元組陣列編碼為 Hex 或 Base64 以便顯示或傳輸。
完整程序包含四個簡短步驟。第一,透過 MessageDigest.getInstance("SHA-256") 取得 MessageDigest 實例;字串常值必須完全一致,而此呼叫只有在不符合規範的 JVM 上才會拋出 NoSuchAlgorithmException。第二,將輸入轉成位元組,務必傳入 StandardCharsets.UTF_8,因為無參數的 getBytes() 會回退到平台預設字元集,一旦出現非 ASCII 字元,摘要就會被悄悄改變。第三,呼叫 digest(bytes) 以接收 32 位元組的陣列,或是在想要分批餵入資料時先呼叫 update(bytes) 再呼叫 digest(),這是處理大於可用堆積之檔案的標準做法。第四,以十六進位編碼該位元組陣列,可以手寫使用兩字元的 String.format("%02x", b) 迴圈,或委派給 Java 17 之後的 HexFormat.of()。
針對檔案,典型模式是把 FileInputStream(或 NIO 的 SeekableByteChannel)包進 DigestInputStream,如此便能串流位元組通過摘要,而不必將整個檔案載入記憶體。一旦檔案接近任何瀏覽器方案的容量上限,這種串流行為就格外重要,因為 SHA-256 Hash Generator 是將完整位元組緩衝區一次傳給 Web Cryptography 摘要介面,而不是以串流方式處理。
在瀏覽器中計算相同的摘要
瀏覽器的 SHA-256 Hash Generator 在以下情境很實用:你需要快速、無相依性的檢查;不會寫 Java 的同事需要驗證已發布的校驗和;或是單純想要對你程式碼剛產生的數值取得第二意見。該工具會在你當前的分頁中執行完全相同的 FIPS 180-4 程式,而且絕不會上傳你選取的位元組。使用步驟如下:
- 在瀏覽器分頁中開啟 SHA256 Hash Generator;實際雜湊作業不需要安裝、帳號或任何網路呼叫。
- 選擇文字模式或檔案模式。在文字模式中,貼上或輸入你想要重現 Java 摘要的確切字串,請記住空格、換行,以及結尾換行都算位元組。在檔案模式中,選取你想要交叉驗證的本地端檔案;工具會直接從磁碟讀取檔案位元組,而不會重新編碼。
- 觸發摘要計算。工具會將完全相同的 UTF-8 位元組(文字模式)或完全相同的檔案位元組(檔案模式)傳給 Web Cryptography 的 SHA-256 實作,並以兩種形式顯示產生的 32 位元組:64 個小寫 Hex 字元,以及標準填補的 Base64。
- 若對方系統預期的是十六進位校驗和,就複製 Hex 表示;若預期的是 Base64,就複製 Base64。將該值貼到 Java 測試、建置清單或訊息中,比對時務必比對完整字串而非前綴。
- 儲存任何摘要時,務必一併記錄演算法名稱,如此一來,64 字元的 SHA-256 值日後才不會與其他格式、未記載的校驗和,或被截斷的摘要混淆。
由於 Java MessageDigest 路徑與瀏覽器工具最終呼叫的都是同一套 FIPS 180-4 程式,對任何給定輸入產生的摘要是逐位元組相同的。Java 實作通常使用 JDK 提供者的原生程式碼,瀏覽器則使用 Web Crypto,但兩者都實作了 NIST FIPS 180-4 中所述的相同填補、區塊切分,以及 64 字排程。
在不產生不相符的情況下比對兩種輸出
摘要不相符幾乎都是因為輸入位元組不同,而不是實作有誤。下表列出最常見的原因與各自的檢查方式。針對密碼型式的輸入,請使用 密碼雜湊工作流程,因為下方要點假設的是完整性檢查的使用情境。
| 不相符原因 | 如何偵測 | 如何修正 |
|---|---|---|
| 平台預設字元集 | 非 ASCII 文字在不同機器上會產生不同位元組。 | 一律明確傳入 StandardCharsets.UTF_8。 |
| 結尾換行 | 命令列工具會自動附加 "\n"。 | 移除最後一個位元組,或改用檔案而非貼上文字進行雜湊。 |
| 換行風格 | Windows 的 CRLF 會產生 0d 0a;Unix 的 LF 則只產生 0a。 | 在雜湊前先正規化,或直接以原樣對檔案雜湊以符合發布者。 |
| Unicode 正規化 | 帶腔調字母或表情符號具有 NFC 與 NFD 兩種形式,會編碼成不同的 UTF-8 位元組序列。 | 使用 Normalizer.normalize(input, Form.NFC) 明確正規化,或在源頭選定單一正規形式。 |
| 文字與檔案位元組 | 貼上文字會經過解碼再重新編碼;檔案模式則會略過這一步。 | 驗證下載校驗和時,直接對檔案本身雜湊。 |
即使位元組相符,參考摘要本身也必須是可信的。若攻擊者能同時置換檔案與並列顯示的校驗和,重算 SHA-256 只會確認攻擊者那一對組合是相符的。請透過 HTTPS 從擁有者處取得參考雜湊、透過已簽署的釋出構件,或透過其他適合該風險等級的已認證管道取得;摘要僅提供完整性,並非來源保證。
不該使用 SHA-256 的情境
SHA-256 是完整性基元,而不是身分驗證基元。有三項常見工作很容易與完整性檢查混淆,若貿然使用原始 SHA-256,將會在不出錯的情況下悄悄失敗:
- 密碼儲存。SHA-256 的速度對此用途而言實在太快了。它的速度對校驗和工作流程很有價值,卻恰恰是密碼驗證最不該具備的特性——攻擊者可以在消費級 GPU 上每秒嘗試數十億次猜測。帳號系統應為每位使用者儲存唯一的 salt,並將密碼通過專為此設計的記憶體困難函式,例如 Argon2id、scrypt 或 bcrypt。如需具體工作流程,請參考「輕鬆生成密碼雜湊」一文。
- 雙方之間的訊息認證。原始 SHA-256 無法證明摘要是由誰產生。當兩個系統共享祕密,並且需要驗證訊息確實來自彼此時,應使用 HMAC-SHA-256,它會在雜湊內部把祕密與訊息結合起來。
- 基於單一公開金鑰的可驗證來源。當驗證者需要確認訊息是由特定一方簽署時,應使用 RSA-PSS 或 ECDSA 等數位簽章(作用在 SHA-256 摘要之上),而不是單獨使用摘要。
如果你的目標只是確認兩個檔案相符,或是重現廠商發布的校驗和,那麼原始 SHA-256 才是正確選擇,SHA-256 Hash Generator 也會產生你所需要的精確數值。
Limits and Operational Notes
Three operational limits come up repeatedly when generating SHA-256 hashes in Java and verifying them elsewhere:
- Input cap for browser tools. The SHA-256 Hash Generator accepts up to 100 MB of file bytes because the underlying Web Cryptography digest interface receives the full byte buffer rather than streaming it. Larger files should be hashed with a trusted streaming tool on the local machine, such as sha256sum on Unix or certutil on Windows, or with Java's DigestInputStream.
- Empty input is valid. Hashing the empty byte string returns the standard digest beginning e3b0c442. The generator accepts empty text on purpose; if you instead see a non-empty digest, the input likely contains invisible characters such as a final newline or a zero-width space.
- Authentication tests are separate. The NIST Cryptographic Algorithm Validation Program for Secure Hashing tests implementations against published answer sets, but the validation status of a particular library does not authenticate the bytes you hashed, only the algorithm's correctness.
For Java specifically, two further points save hours of debugging. First, the algorithm string passed to MessageDigest.getInstance is case-insensitive in modern JDKs but the canonical form is "SHA-256", and variants such as "SHA-256/224" or "SHA3-256" produce different digests. Second, when comparing digests across languages, hash the empty string on each side first; if both return the e3b0c442 fixture, the encoding, algorithm, and output format are all wired correctly and any remaining mismatch is in the input bytes.
If you're weighing options, How to Calculate a SHA-512 Hash on a Linux System covers this in detail.