What is SHA-256 Hashing in Java?

Alexander Obregon
10 min readJun 16, 2024

--

Image Source

Introduction

SHA-256 (Secure Hash Algorithm 256) is a cryptographic hash function that generates a 256-bit (32-byte) hash value. It is widely used in various security applications and protocols, including SSL/TLS and digital signatures. Understanding SHA-256 hashing in Java is important for beginners who want to ensure data integrity and security in their applications. This article will cover the basics of SHA-256, its implementation in Java, and practical use cases.

SHA-256 Hashing Basics

SHA-256 (Secure Hash Algorithm 256) is one of the most widely used cryptographic hash functions today. Developed by the National Security Agency (NSA) and published by the National Institute of Standards and Technology (NIST) in 2001, SHA-256 is part of the SHA-2 family of cryptographic hash functions. The SHA-2 family also includes other hash functions like SHA-224, SHA-384, and SHA-512, each producing a hash of different lengths. Among them, SHA-256 has become particularly popular due to its balance of security and efficiency.

What is a Hash Function?

Before diving into SHA-256 specifically, it’s essential to understand what a hash function is. A hash function takes an input (or ‘message’) and returns a fixed-size string of bytes. The output, typically a hexadecimal number, appears random. However, the same input will always produce the same output. This consistency is crucial for verifying data integrity.

Hash functions have several important properties:

  • Deterministic: The same input always produces the same output.
  • Fast Computation: Hashing the input should be computationally efficient.
  • Pre-image Resistance: Given a hash value, it should be infeasible to find the original input.
  • Small Changes in Input Change the Output Significantly: A small alteration to the input should produce a vastly different hash.
  • Collision Resistance: It should be extremely unlikely for two different inputs to produce the same hash value.

Characteristics of SHA-256

SHA-256 stands out because it produces a 256-bit (32-byte) hash value, often represented as a 64-character hexadecimal number. This large output size makes it highly resistant to brute force attacks, where an attacker tries to reverse-engineer the original input by trying many different inputs.

How SHA-256 Works

SHA-256 processes data in blocks of 512 bits, or 64 bytes. If the input data isn’t a multiple of 512 bits, it is padded. The padding process involves appending a single ‘1’ bit, followed by enough ‘0’ bits to reach a length 64 bits short of a multiple of 512. The final 64 bits are filled with the original message length, in bits.

Once the data is padded, SHA-256 performs a series of bitwise operations and modular additions on the data blocks. These operations involve initializing a set of constants and working with them through a series of rounds to produce a final hash value. The intricate process make sure that even a tiny change in the input drastically changes the output hash.

Importance of SHA-256

SHA-256 is integral to modern security practices for several reasons:

  • Data Integrity: It makes sure that data has not been altered. When you hash a message and later rehash it, the hashes should match if the data is unchanged.
  • Cryptographic Security: It’s used in encryption protocols like SSL/TLS, securing communications over the internet.
  • Digital Signatures: Ensuring authenticity and integrity of digital messages and documents.
  • Blockchain Technology: It underpins the security of blockchain, ensuring that once data is written, it cannot be altered without detection.

Comparing SHA-256 with Other Hash Functions

While there are several hashing algorithms, SHA-256 strikes a balance between security and efficiency. For instance:

  • MD5: An older algorithm producing a 128-bit hash, now considered broken due to vulnerabilities.
  • SHA-1: Produces a 160-bit hash, but also compromised and no longer considered secure.
  • SHA-512: Another member of the SHA-2 family, it produces a 512-bit hash, offering greater security at the cost of computational resources.

In comparison to these, SHA-256 provides strong security while being computationally feasible for most applications, making it a preferred choice in many security implementations.

Implementing SHA-256 Hashing in Java

Java provides strong support for cryptographic operations through its standard libraries, making it relatively straightforward to implement SHA-256 hashing. In this section, we will go through the step-by-step process of implementing SHA-256 hashing in Java, starting from importing the necessary packages to running the code.

Step 1: Import Required Packages

To begin with, you need to import the MessageDigest class from the java.security package. This class provides the functionality of a message digest algorithm, including SHA-256.

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

Step 2: Create a Method to Generate SHA-256 Hash

The core of our implementation will be a method that takes a string input and returns its SHA-256 hash. This method involves initializing a MessageDigest instance for SHA-256, performing the hash computation, and then converting the resulting byte array into a hexadecimal string.

public class SHA256Hashing {

public static String generateSHA256Hash(String input) {
try {
// Create a MessageDigest instance for SHA-256
MessageDigest digest = MessageDigest.getInstance("SHA-256");

// Perform the hash computation
byte[] encodedhash = digest.digest(input.getBytes());

// Convert byte array into a hexadecimal string
StringBuilder hexString = new StringBuilder();
for (byte b : encodedhash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) {
String input = "Hello, SHA-256!";
String hash = generateSHA256Hash(input);
System.out.println("Input: " + input);
System.out.println("SHA-256 Hash: " + hash);
}
}

Explanation of the Code:

  1. MessageDigest Instance: The MessageDigest.getInstance("SHA-256") call returns a MessageDigest object that implements the SHA-256 algorithm. If SHA-256 is not available, a NoSuchAlgorithmException is thrown.
  2. Hash Computation: The digest method computes the hash of the input string's bytes and returns the result as a byte array. The getBytes() method converts the input string into a byte array.
  3. Hexadecimal Conversion: The byte array is converted into a hexadecimal string. Each byte is converted to a two-digit hexadecimal number. The 0xff & b makes sure that the byte is treated as an unsigned value, and Integer.toHexString converts it to a hexadecimal string. If the resulting string has a length of 1, a '0' is appended to ensure two digits.

Step 3: Running the Code

When you run the code, you should see the SHA-256 hash of the input string printed to the console. Here is an example output for the input “Hello, SHA-256!”:

Input: Hello, SHA-256!
SHA-256 Hash: 334d13a2a8fd4e2e312f8e4036e6a9a4c0a8a0df58a81458759aeb5a51f1ff48

Additional Example: Handling Different Character Encodings

By default, the getBytes() method uses the platform's default character encoding. However, it is often a good practice to specify a character encoding explicitly, such as UTF-8, to make sure consistent results across different environments.

import java.nio.charset.StandardCharsets;

public class SHA256Hashing {

public static String generateSHA256Hash(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(input.getBytes(StandardCharsets.UTF_8));

StringBuilder hexString = new StringBuilder();
for (byte b : encodedhash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) {
String input = "Hello, SHA-256!";
String hash = generateSHA256Hash(input);
System.out.println("Input: " + input);
System.out.println("SHA-256 Hash: " + hash);
}
}

Explanation of the Code with UTF-8:

  • Character Encoding: The StandardCharsets.UTF_8 specifies that the string should be converted to bytes using the UTF-8 character encoding. This makes sure that the same bytes are generated regardless of the platform's default encoding.

Best Practices for SHA-256 Hashing in Java

  • Handle Exceptions Properly: Always handle exceptions such as NoSuchAlgorithmException to make sure your application can gracefully deal with any issues related to the availability of the SHA-256 algorithm.
  • Use Secure Random Salts: When hashing sensitive data like passwords, use a secure random salt and hash the combination of the salt and the data to protect against rainbow table attacks.
  • Consistency in Encoding: Specify character encodings explicitly to make sure consistent behavior across different environments and platforms.

Practical Use Cases of SHA-256 Hashing

SHA-256 is a versatile cryptographic hash function used in various scenarios where data integrity and security are crucial. This section will explore some of the most common and practical use cases of SHA-256 hashing, including data integrity verification, password hashing, and digital signatures.

Data Integrity Verification

One of the primary uses of SHA-256 hashing is to verify the integrity of data. When data is transmitted over a network or stored in a file, there is always a risk of it being altered, either accidentally or maliciously. By generating a hash of the original data and comparing it with the hash of the received data, we can make sure that the data has not been tampered with.

Here’s an example of how to verify data integrity using SHA-256 in Java:

public class DataIntegrity {

public static boolean verifyDataIntegrity(String originalData, String receivedData) {
String originalHash = SHA256Hashing.generateSHA256Hash(originalData);
String receivedHash = SHA256Hashing.generateSHA256Hash(receivedData);
return originalHash.equals(receivedHash);
}

public static void main(String[] args) {
String originalData = "Important data";
String receivedData = "Important data"; // Change to test integrity failure

boolean isDataIntact = verifyDataIntegrity(originalData, receivedData);
if (isDataIntact) {
System.out.println("Data integrity verified.");
} else {
System.out.println("Data integrity compromised.");
}
}
}

In this example, the verifyDataIntegrity method generates the SHA-256 hashes of the original and received data and compares them. If the hashes match, the data is intact; otherwise, it has been compromised.

Password Hashing

Storing passwords in plaintext is a significant security risk. Instead, passwords should be hashed using SHA-256 before storage. This way, even if the database is compromised, the actual passwords remain secure. When a user logs in, the password they provide is hashed, and the resulting hash is compared to the stored hash.

Here’s an example of password hashing and verification using SHA-256 in Java:

public class PasswordHashing {

public static String hashPassword(String password) {
return SHA256Hashing.generateSHA256Hash(password);
}

public static boolean verifyPassword(String inputPassword, String storedHash) {
String inputHash = hashPassword(inputPassword);
return inputHash.equals(storedHash);
}

public static void main(String[] args) {
String password = "securePassword";
String storedHash = hashPassword(password);

System.out.println("Stored Hash: " + storedHash);

boolean isPasswordCorrect = verifyPassword("securePassword", storedHash);
if (isPasswordCorrect) {
System.out.println("Password verified.");
} else {
System.out.println("Incorrect password.");
}
}
}

In this example, the hashPassword method generates the SHA-256 hash of the password. The verifyPassword method hashes the input password and compares it with the stored hash to verify if the passwords match.

Digital Signatures

SHA-256 is also used in generating digital signatures, which make sure the authenticity and integrity of a message, software, or digital document. A digital signature is created using a private key and can be verified using the corresponding public key. This process involves hashing the data and then encrypting the hash with the private key.

Here’s an example of creating and verifying digital signatures using SHA-256 in Java:

import java.security.*;

public class DigitalSignatureExample {

public static byte[] createDigitalSignature(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data.getBytes());
return signature.sign();
}

public static boolean verifyDigitalSignature(String data, byte[] signatureBytes, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(data.getBytes());
return signature.verify(signatureBytes);
}

public static void main(String[] args) throws Exception {
// Generate RSA key pair for example purposes
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();

String data = "Important message";
byte[] digitalSignature = createDigitalSignature(data, keyPair.getPrivate());

boolean isVerified = verifyDigitalSignature(data, digitalSignature, keyPair.getPublic());
if (isVerified) {
System.out.println("Digital signature verified.");
} else {
System.out.println("Digital signature verification failed.");
}
}
}

In this example, the createDigitalSignature method generates a digital signature for the given data using a private key, while the verifyDigitalSignature method verifies the signature using the corresponding public key. This makes sure that the data has not been altered and that it originates from a trusted source.

Blockchain Technology

SHA-256 plays a crucial role in blockchain technology, ensuring the security and integrity of blockchain data. In a blockchain, each block contains a cryptographic hash of the previous block, creating a chain of blocks. This makes it computationally infeasible to alter any block without changing all subsequent blocks, ensuring the immutability of the blockchain.

Here’s a simplified example of how SHA-256 is used in a blockchain:

import java.util.ArrayList;
import java.util.List;

public class BlockchainExample {

public static class Block {
public String hash;
public String previousHash;
public String data;
private long timeStamp;

public Block(String data, String previousHash) {
this.data = data;
this.previousHash = previousHash;
this.timeStamp = System.currentTimeMillis();
this.hash = calculateHash();
}

public String calculateHash() {
String input = previousHash + Long.toString(timeStamp) + data;
return SHA256Hashing.generateSHA256Hash(input);
}
}

public static void main(String[] args) {
List<Block> blockchain = new ArrayList<>();

Block genesisBlock = new Block("First block data", "0");
blockchain.add(genesisBlock);

Block secondBlock = new Block("Second block data", genesisBlock.hash);
blockchain.add(secondBlock);

Block thirdBlock = new Block("Third block data", secondBlock.hash);
blockchain.add(thirdBlock);

for (Block block : blockchain) {
System.out.println("Block:");
System.out.println("Data: " + block.data);
System.out.println("Hash: " + block.hash);
System.out.println("Previous Hash: " + block.previousHash);
System.out.println();
}
}
}

In this example, each block in the blockchain contains its data, a hash of the previous block, and a timestamp. The calculateHash method generates the hash for each block, linking it to the previous block and ensuring the integrity of the entire chain.

Certificate Authorities and SSL/TLS

SHA-256 is also extensively used in SSL/TLS certificates issued by certificate authorities (CAs). When a website uses HTTPS, the SSL/TLS certificate ensures secure communication between the client and server. The certificate contains a public key and is signed by a CA using SHA-256, providing authenticity and integrity.

Software Distribution

When distributing software, it’s important to make sure that the software has not been tampered with. Developers often provide a SHA-256 hash of the software package alongside the download link. Users can then generate a hash of the downloaded file and compare it with the provided hash to verify the integrity and authenticity of the software.

Conclusion

SHA-256 hashing is a fundamental tool in modern cryptography, offering strong security and data integrity across various applications. From verifying data integrity and securing passwords to enabling digital signatures and underpinning blockchain technology, SHA-256 makes sure that information remains protected and unaltered. By understanding and implementing SHA-256 hashing in Java, beginners can enhance the security of their applications and build a strong foundation for more advanced cryptographic practices. With its balance of efficiency and security, SHA-256 remains a vital component in safeguarding digital information.

  1. Java Cryptography Architecture (JCA) Reference Guide
  2. MessageDigest Class (Java Platform SE 8)
  3. National Institute of Standards and Technology (NIST) — SHA-256
  4. Java Platform, Standard Edition 8 API Specification

Thank you for reading! If you find this guide helpful, please consider highlighting, clapping, responding or connecting with me on Twitter/X as it’s very appreciated and helps keep content like this free!

--

--

Alexander Obregon

Software Engineer, fervent coder & writer. Devoted to learning & assisting others. Connect on LinkedIn: https://www.linkedin.com/in/alexander-obregon-97849b229/