SHA-224 Java SDK
Our Java SDK provides a high-performance implementation of the SHA-224 cryptographic hash function for Java applications, with support for Android, Jakarta EE, and Spring environments.
JCA Integration
Integrates with Java Cryptography Architecture for seamless use in security-focused applications.
Stream Processing
Efficiently process large files and streams with incremental hashing and optimized memory usage.
High Performance
Highly optimized implementation using JNI with fallback to pure Java when needed.
Android Compatible
Fully compatible with Android applications, from API level 19+ (Android 4.4+).
Thread Safety
Thread-safe implementation for concurrent environments.
REST API Integration
Built-in client for the SHA224.com REST API.
Installation
Add the following dependency to your pom.xml
:
<dependency>
<groupId>com.sha224</groupId>
<artifactId>sha224-java</artifactId>
<version>1.2.3</version>
</dependency>
Add the following to your build.gradle
:
dependencies {
implementation 'com.sha224:sha224-java:1.2.3'
}
For Kotlin DSL (build.gradle.kts
):
dependencies {
implementation("com.sha224:sha224-java:1.2.3")
}
Add the following to your build.sbt
:
libraryDependencies += "com.sha224" % "sha224-java" % "1.2.3"
Download the JAR file from the GitHub releases page and add it to your project's classpath.
SHA-224 Java requires Java 8 (1.8) or higher.
Dependencies
The core SDK has no external dependencies. Optional modules have the following dependencies:
sha224-api-client
: Requirescom.squareup.okhttp3:okhttp
for API client functionalitysha224-spring
: Requires Spring Framework dependencies for Spring integration
Quick Start
Generate SHA-224 hashes with just a few lines of code:
import com.sha224.SHA224;
import com.sha224.SHA224Digest;
public class QuickStart {
public static void main(String[] args) {
// Hash a string
String hash = SHA224.hash("Hello, world!");
System.out.println(hash); // 8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568
// Hash binary data
byte[] binaryData = "Hello, world!".getBytes();
String binaryHash = SHA224.hash(binaryData);
System.out.println(binaryHash);
// Incremental hashing
SHA224Digest digest = new SHA224Digest();
digest.update("Hello, ".getBytes());
digest.update("world!".getBytes());
String incrementalHash = digest.hexDigest();
System.out.println(incrementalHash); // Same as above
// Verify a hash
boolean isValid = SHA224.verify("8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568", "Hello, world!");
System.out.println("Hash is valid: " + isValid); // true
}
}
Using the API Client
import com.sha224.api.SHA224API;
import com.sha224.api.SHA224APIClient;
public class ApiClientExample {
public static void main(String[] args) {
// Initialize the API client
SHA224API api = new SHA224APIClient("YOUR_API_KEY");
// Hash using the API
String apiHash = api.hashText("Hello, world!");
System.out.println("API hash: " + apiHash);
// Verify a hash using the API
boolean isValid = api.verify("8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568", "Hello, world!");
System.out.println("API verification: " + isValid); // true
}
}
API Reference
SHA224.hash(input)
Computes a SHA-224 hash of the input data.
Overloads
static String hash(String input)
- Hash a string using UTF-8 encodingstatic String hash(String input, String encoding)
- Hash a string using specified encodingstatic String hash(byte[] input)
- Hash binary datastatic String hash(InputStream input)
- Hash data from an input streamstatic String hash(File file)
- Hash a filestatic byte[] hashToBytes(byte[] input)
- Hash binary data and return raw bytes
Returns
String
- The SHA-224 hash of the input as a hexadecimal string (or byte[]
for raw output variants)
Example
// Hash a string
String hash = SHA224.hash("Hello, world!");
// Hash a file
File file = new File("document.pdf");
String fileHash = SHA224.hash(file);
// Hash with a specific encoding
String unicodeHash = SHA224.hash("こんにちは", "UTF-16");
SHA224.verify(expectedHash, input)
Verifies if the provided data matches the expected hash.
Overloads
static boolean verify(String expectedHash, String input)
- Verify a string using UTF-8 encodingstatic boolean verify(String expectedHash, String input, String encoding)
- Verify a string using specified encodingstatic boolean verify(String expectedHash, byte[] input)
- Verify binary datastatic boolean verify(String expectedHash, InputStream input)
- Verify data from an input streamstatic boolean verify(String expectedHash, File file)
- Verify a file
Returns
boolean
- True if the hash matches, false otherwise
Example
// Verify a string
String expected = "8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568";
boolean isValid = SHA224.verify(expected, "Hello, world!");
System.out.println(isValid); // true
// Verify a file
File file = new File("document.pdf");
boolean isFileValid = SHA224.verify(expectedFileHash, file);
SHA224Digest
Class for incremental hashing of data chunks.
Methods
void update(byte[] input)
- Update the hash with new datavoid update(byte[] input, int offset, int length)
- Update with a portion of the input arrayvoid update(String input)
- Update with a string (UTF-8 encoded)void update(String input, String encoding)
- Update with a string using specified encodingvoid update(InputStream input)
- Update with data from an input streambyte[] digest()
- Get the final hash as raw bytesString hexDigest()
- Get the final hash as a hex stringString base64Digest()
- Get the final hash as a base64 stringvoid reset()
- Reset the digest for reuseSHA224Digest copy()
- Create a copy of the current digest state
Example
// Create a digest instance
SHA224Digest digest = new SHA224Digest();
// Update with chunks of data
digest.update("Hello, ");
digest.update("world!");
// Get the final hash
String hash = digest.hexDigest();
System.out.println(hash);
// Create a copy midway
SHA224Digest copy = digest.copy();
digest.update(" More data");
System.out.println(digest.hexDigest()); // Different from hash
System.out.println(copy.hexDigest()); // Same as hash
// Reset and reuse
digest.reset();
digest.update("New data");
String newHash = digest.hexDigest();
SHA224Util
Utility methods for working with SHA-224 hashes.
Methods
static boolean compareHashes(String hash1, String hash2)
- Constant-time hash comparisonstatic String bytesToHex(byte[] bytes)
- Convert raw digest bytes to a hex stringstatic String bytesToBase64(byte[] bytes)
- Convert raw digest bytes to a base64 stringstatic byte[] hexToBytes(String hex)
- Convert a hex string to raw bytes
Example
// Secure hash comparison (constant-time)
boolean hashesMatch = SHA224Util.compareHashes(hash1, hash2);
// Convert between formats
byte[] rawBytes = SHA224.hashToBytes("Hello, world!".getBytes());
String hexString = SHA224Util.bytesToHex(rawBytes);
String base64String = SHA224Util.bytesToBase64(rawBytes);
byte[] backToBytes = SHA224Util.hexToBytes(hexString);
SHA224Provider
JCA security provider for SHA-224 integration.
Usage
import java.security.MessageDigest;
import java.security.Security;
import com.sha224.jca.SHA224Provider;
// Register the provider
Security.addProvider(new SHA224Provider());
// Use SHA-224 through the JCA
MessageDigest md = MessageDigest.getInstance("SHA-224", "SHA224Provider");
md.update("Hello, world!".getBytes());
byte[] digest = md.digest();
// Convert to hex
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
hexString.append(String.format("%02x", b));
}
System.out.println(hexString.toString());
SHA224API
Interface for the SHA224.com REST API client.
Methods
String hashText(String text)
- Hash text using the APIString hashText(String text, String encoding)
- Hash text with specified encodingString hashFile(File file)
- Hash a file using the APIString hashFile(File file, ProgressListener listener)
- Hash with progress trackingboolean verify(String expectedHash, String text)
- Verify a hash using the APIboolean verify(String expectedHash, File file)
- Verify a file hashBatchResult batchHash(List<BatchItem> items)
- Batch hash multiple itemsBatchVerifyResult batchVerify(List<BatchVerifyItem> items)
- Batch verify multiple items
Example
import com.sha224.api.SHA224API;
import com.sha224.api.SHA224APIClient;
import com.sha224.api.BatchItem;
import com.sha224.api.BatchResult;
import com.sha224.api.ProgressListener;
// Create the API client
SHA224API api = new SHA224APIClient("YOUR_API_KEY");
// Hash text
String hash = api.hashText("Hello, world!");
// Hash a file with progress tracking
File largeFile = new File("large-file.bin");
String fileHash = api.hashFile(largeFile, new ProgressListener() {
@Override
public void onProgress(long bytesProcessed, long totalBytes) {
double progress = (double) bytesProcessed / totalBytes * 100;
System.out.printf("Progress: %.2f%%\n", progress);
}
});
// Batch hash multiple items
List<BatchItem> items = new ArrayList<>();
items.add(new BatchItem("item1", "First item"));
items.add(new BatchItem("item2", "Second item"));
items.add(new BatchItem("item3", "Third item"));
BatchResult result = api.batchHash(items);
for (BatchItem item : result.getItems()) {
System.out.println(item.getId() + ": " + item.getHash());
}
Examples
Basic Usage Examples
Hashing Different Data Types
import com.sha224.SHA224;
import java.nio.charset.StandardCharsets;
public class BasicExamples {
public static void main(String[] args) throws Exception {
// Hash a string (default UTF-8 encoding)
String textHash = SHA224.hash("Hello, world!");
System.out.println("String hash: " + textHash);
// Hash with different string encoding
String utf16Hash = SHA224.hash("Hello, world!", "UTF-16");
System.out.println("UTF-16 string hash: " + utf16Hash);
// Hash bytes
byte[] bytes = "Hello, world!".getBytes(StandardCharsets.UTF_8);
String bytesHash = SHA224.hash(bytes);
System.out.println("Bytes hash: " + bytesHash);
// Hash an integer value (converted to bytes)
int value = 12345;
byte[] intBytes = new byte[4];
intBytes[0] = (byte) (value >> 24);
intBytes[1] = (byte) (value >> 16);
intBytes[2] = (byte) (value >> 8);
intBytes[3] = (byte) value;
String intHash = SHA224.hash(intBytes);
System.out.println("Integer hash: " + intHash);
// Get raw bytes output
byte[] rawHash = SHA224.hashToBytes(bytes);
System.out.println("Raw hash length: " + rawHash.length); // 28 bytes
}
}
Incremental Hashing
import com.sha224.SHA224Digest;
import java.nio.charset.StandardCharsets;
public class IncrementalExample {
public static void main(String[] args) {
// Create a digest instance
SHA224Digest digest = new SHA224Digest();
// Update with different chunks of data
digest.update("Part 1: ");
digest.update("Part 2: ".getBytes(StandardCharsets.UTF_8));
// Using offset and length
byte[] buffer = "Part 3: ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();
digest.update(buffer, 0, 8); // Only use "Part 3: "
// Get the final hash
String hash = digest.hexDigest();
System.out.println("Incremental hash: " + hash);
// Create a copy at a specific point
SHA224Digest copy = digest.copy();
// Continue updating the original
digest.update(" Additional data");
System.out.println("Original digest: " + digest.hexDigest());
System.out.println("Copied digest: " + copy.hexDigest());
// Reset and reuse
digest.reset();
digest.update("Fresh start");
System.out.println("After reset: " + digest.hexDigest());
}
}
Hash Verification
import com.sha224.SHA224;
import com.sha224.SHA224Util;
public class VerificationExample {
public static void main(String[] args) {
// Create and verify a hash
String data = "Important data to verify";
String originalHash = SHA224.hash(data);
System.out.println("Original hash: " + originalHash);
// Verify the data hasn't been modified
boolean isValid = SHA224.verify(originalHash, data);
System.out.println("Valid data: " + isValid); // true
// Verify modified data
String modifiedData = "Important data to verify!";
boolean isModifiedValid = SHA224.verify(originalHash, modifiedData);
System.out.println("Valid modified data: " + isModifiedValid); // false
// Secure comparison of hashes (constant-time)
String hash1 = SHA224.hash("data1");
String hash2 = SHA224.hash("data2");
String hash3 = SHA224.hash("data1");
System.out.println("hash1 == hash2: " + SHA224Util.compareHashes(hash1, hash2)); // false
System.out.println("hash1 == hash3: " + SHA224Util.compareHashes(hash1, hash3)); // true
// WARNING: Don't use regular string comparison for security-sensitive hash comparisons
// It's vulnerable to timing attacks
System.out.println("Insecure: hash1.equals(hash3): " + hash1.equals(hash3)); // true but insecure
}
}
File Hashing Examples
Hashing Files
import com.sha224.SHA224;
import com.sha224.SHA224Digest;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class FileHashingExample {
public static void main(String[] args) throws Exception {
// Simple file hashing
File smallFile = new File("small-file.txt");
String smallFileHash = SHA224.hash(smallFile);
System.out.println("Small file hash: " + smallFileHash);
// Hash a large file with manual chunking
File largeFile = new File("large-file.bin");
String largeFileHash = hashLargeFile(largeFile);
System.out.println("Large file hash: " + largeFileHash);
}
public static String hashLargeFile(File file) throws Exception {
SHA224Digest digest = new SHA224Digest();
try (InputStream is = new FileInputStream(file)) {
byte[] buffer = new byte[8192]; // 8KB buffer
long totalBytes = file.length();
long bytesRead = 0;
int read;
while ((read = is.read(buffer)) != -1) {
digest.update(buffer, 0, read);
bytesRead += read;
// Report progress
double progress = (double) bytesRead / totalBytes * 100;
System.out.printf("Progress: %.2f%%\n", progress);
}
}
return digest.hexDigest();
}
}
Directory Hashing
import com.sha224.SHA224;
import com.sha224.SHA224Digest;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DirectoryHashingExample {
public static void main(String[] args) throws Exception {
File directory = new File("./project-directory");
String directoryHash = hashDirectory(directory);
System.out.println("Directory hash: " + directoryHash);
}
public static String hashDirectory(File directory) throws Exception {
if (!directory.isDirectory()) {
throw new IllegalArgumentException("Not a directory: " + directory);
}
// Get all files recursively
List files = new ArrayList<>();
listFilesRecursively(directory, files);
// Sort files by path for consistency
Collections.sort(files, (f1, f2) -> {
return f1.getPath().compareTo(f2.getPath());
});
// Create a master digest
SHA224Digest masterDigest = new SHA224Digest();
// Hash each file and add its path and hash to the master digest
for (File file : files) {
String relativePath = directory.toPath().relativize(file.toPath()).toString();
String fileHash = SHA224.hash(file);
// Add the relative path and hash to the master digest
masterDigest.update(relativePath.getBytes(StandardCharsets.UTF_8));
masterDigest.update(fileHash.getBytes(StandardCharsets.UTF_8));
System.out.println("Hashed: " + relativePath + " - " + fileHash);
}
return masterDigest.hexDigest();
}
private static void listFilesRecursively(File directory, List files) {
File[] entries = directory.listFiles();
if (entries != null) {
for (File entry : entries) {
if (entry.isFile()) {
files.add(entry);
} else if (entry.isDirectory()) {
listFilesRecursively(entry, files);
}
}
}
}
}
File Integrity Verification
import com.sha224.SHA224;
import java.io.File;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FileIntegrityExample {
public static void main(String[] args) throws Exception {
// Create a file
File file = new File("important-document.txt");
try (FileWriter writer = new FileWriter(file)) {
writer.write("This is important data that should not be modified.");
}
// Create and save the hash
String originalHash = SHA224.hash(file);
File hashFile = new File("important-document.txt.sha224");
try (FileWriter writer = new FileWriter(hashFile)) {
writer.write(originalHash);
}
System.out.println("File created with hash: " + originalHash);
// Later, verify the file hasn't been modified
boolean isValid = verifyFileIntegrity(file, hashFile);
System.out.println("File integrity valid: " + isValid);
// Modify the file and check again
try (FileWriter writer = new FileWriter(file, true)) {
writer.write(" THIS IS A MODIFICATION!");
}
boolean isStillValid = verifyFileIntegrity(file, hashFile);
System.out.println("Modified file integrity valid: " + isStillValid); // Should be false
}
public static boolean verifyFileIntegrity(File file, File hashFile) throws Exception {
if (!file.exists() || !hashFile.exists()) {
return false;
}
String expectedHash = new String(Files.readAllBytes(hashFile.toPath())).trim();
return SHA224.verify(expectedHash, file);
}
}
Stream Processing Examples
Input Stream Hashing
import com.sha224.SHA224;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class StreamHashingExample {
public static void main(String[] args) throws Exception {
// Hash a ByteArrayInputStream
byte[] data = "Stream data for hashing".getBytes();
try (InputStream is = new ByteArrayInputStream(data)) {
String hash = SHA224.hash(is);
System.out.println("ByteArrayInputStream hash: " + hash);
}
// Hash a FileInputStream
try (InputStream fis = new FileInputStream("data.txt")) {
String hash = SHA224.hash(fis);
System.out.println("FileInputStream hash: " + hash);
}
// Reset and rehash the same stream
// Note: Not all streams support reset()
try (InputStream is = new ByteArrayInputStream(data)) {
SHA224Digest digest = new SHA224Digest();
// Read half the stream
byte[] buffer = new byte[data.length / 2];
is.read(buffer);
digest.update(buffer);
// Reset the stream
is.reset();
// Now hash the entire stream
digest.update(is);
String hash = digest.hexDigest();
System.out.println("Reset stream hash: " + hash);
}
}
}
Stream Processing with Pipes
import com.sha224.SHA224;
import com.sha224.SHA224Digest;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class PipedStreamExample {
public static void main(String[] args) throws Exception {
// Create piped streams
try (PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream = new PipedInputStream(outputStream, 8192)) {
// Create a thread pool
ExecutorService executor = Executors.newFixedThreadPool(2);
// Submit producer task
Future> producerFuture = executor.submit(() -> {
try {
// Simulate data production
for (int i = 0; i < 100; i++) {
String chunk = "Data chunk " + i + "\n";
outputStream.write(chunk.getBytes(StandardCharsets.UTF_8));
Thread.sleep(10); // Simulate processing time
}
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
});
// Submit consumer/hasher task
Future consumerFuture = executor.submit(() -> {
try {
// Hash the data as it becomes available
SHA224Digest digest = new SHA224Digest();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
System.out.println("Processed " + bytesRead + " bytes");
}
return digest.hexDigest();
} catch (Exception e) {
e.printStackTrace();
return null;
}
});
// Wait for completion and get the hash
producerFuture.get();
String hash = consumerFuture.get();
System.out.println("Final hash: " + hash);
executor.shutdown();
}
}
}
NIO Channel Hashing
import com.sha224.SHA224Digest;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class NIOChannelExample {
public static void main(String[] args) throws Exception {
File largeFile = new File("large-file.bin");
String hash = hashWithNIO(largeFile);
System.out.println("NIO hash: " + hash);
}
public static String hashWithNIO(File file) throws Exception {
SHA224Digest digest = new SHA224Digest();
try (RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(65536); // 64KB buffer
long size = channel.size();
long position = 0;
while (position < size) {
buffer.clear();
int bytesRead = channel.read(buffer);
if (bytesRead <= 0) {
break;
}
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
digest.update(data);
position += bytesRead;
double progress = (double) position / size * 100;
System.out.printf("Progress: %.2f%%\n", progress);
}
}
return digest.hexDigest();
}
}
Advanced Examples
JCA Integration
import com.sha224.jca.SHA224Provider;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
public class JCAExample {
public static void main(String[] args) throws Exception {
// Register the SHA-224 provider
Security.addProvider(new SHA224Provider());
// Get SHA-224 MessageDigest instance
MessageDigest md = getMessageDigest();
// Update with data
md.update("Hello, world!".getBytes());
// Get the digest
byte[] digest = md.digest();
// Convert to hex
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
hexString.append(String.format("%02x", b));
}
System.out.println("JCA SHA-224 hash: " + hexString.toString());
}
private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException, NoSuchProviderException {
try {
// Try to get SHA-224 from the JCA provider
return MessageDigest.getInstance("SHA-224", "SHA224Provider");
} catch (NoSuchProviderException e) {
// Fallback to built-in providers
try {
return MessageDigest.getInstance("SHA-224");
} catch (NoSuchAlgorithmException e2) {
// Fallback to our SHA-224 provider explicitly
Security.addProvider(new SHA224Provider());
return MessageDigest.getInstance("SHA-224", "SHA224Provider");
}
}
}
}
Multi-threaded Hashing
import com.sha224.SHA224;
import com.sha224.SHA224Digest;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MultiThreadedExample {
public static void main(String[] args) throws Exception {
// List of files to hash
List files = new ArrayList<>();
files.add(new File("file1.txt"));
files.add(new File("file2.bin"));
files.add(new File("file3.pdf"));
files.add(new File("file4.jpg"));
// Hash all files in parallel
List hashes = hashFilesInParallel(files);
// Print results
for (int i = 0; i < files.size(); i++) {
System.out.println(files.get(i).getName() + ": " + hashes.get(i));
}
}
public static List hashFilesInParallel(List files) throws Exception {
// Create a thread pool
int processors = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(processors);
// Create tasks
List> tasks = new ArrayList<>();
for (File file : files) {
tasks.add(() -> {
System.out.println(Thread.currentThread().getName() +
" hashing " + file.getName());
return SHA224.hash(file);
});
}
// Execute tasks and wait for results
List> futures = executor.invokeAll(tasks);
// Collect results
List results = new ArrayList<>();
for (Future future : futures) {
results.add(future.get());
}
// Shutdown the executor
executor.shutdown();
return results;
}
}
Content-Addressable Storage
import com.sha224.SHA224;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class ContentAddressableStorageExample {
private final Path storageDir;
private final Map hashIndex = new HashMap<>();
public ContentAddressableStorageExample(String storagePath) throws IOException {
this.storageDir = Paths.get(storagePath);
Files.createDirectories(storageDir);
// Load existing files into the index
Files.walk(storageDir, 1)
.filter(Files::isRegularFile)
.forEach(path -> {
File file = path.toFile();
String hash = file.getName();
hashIndex.put(hash, file);
});
System.out.println("Loaded " + hashIndex.size() + " files from storage");
}
public String store(File sourceFile) throws Exception {
// Hash the file
String hash = SHA224.hash(sourceFile);
// Check if already stored
if (hashIndex.containsKey(hash)) {
System.out.println("File already exists in store: " + hash);
return hash;
}
// Copy to storage with hash as filename
File destFile = storageDir.resolve(hash).toFile();
copyFile(sourceFile, destFile);
// Add to index
hashIndex.put(hash, destFile);
System.out.println("Stored file with hash: " + hash);
return hash;
}
public File retrieve(String hash) {
File file = hashIndex.get(hash);
if (file != null && file.exists()) {
return file;
}
return null;
}
public boolean verify(String hash, File file) throws Exception {
return SHA224.verify(hash, file);
}
private void copyFile(File source, File dest) throws IOException {
try (FileInputStream in = new FileInputStream(source);
FileOutputStream out = new FileOutputStream(dest)) {
byte[] buffer = new byte[8192];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
public static void main(String[] args) throws Exception {
// Create storage
ContentAddressableStorageExample storage =
new ContentAddressableStorageExample("./content_store");
// Store some files
File file1 = new File("document1.pdf");
File file2 = new File("document2.pdf");
File file3 = new File("document3.pdf");
String hash1 = storage.store(file1);
String hash2 = storage.store(file2);
String hash3 = storage.store(file3);
// Retrieve files
File retrieved1 = storage.retrieve(hash1);
System.out.println("Retrieved: " + retrieved1.getAbsolutePath());
// Verify
boolean isValid = storage.verify(hash1, retrieved1);
System.out.println("File verification: " + isValid);
}
}
Android Support
The SHA-224 Java SDK is fully compatible with Android applications. This section provides guidance for Android-specific integrations.
Installation for Android
Add the dependency to your app's build.gradle
:
dependencies {
implementation 'com.sha224:sha224-java:1.2.3'
}
Android Usage Example
import android.os.AsyncTask;
import android.util.Log;
import com.sha224.SHA224;
import com.sha224.SHA224Digest;
import java.io.File;
import java.io.FileInputStream;
public class AndroidExample {
private static final String TAG = "SHA224Example";
// Hash text on the main thread (for small inputs)
public String hashText(String text) {
return SHA224.hash(text);
}
// Hash a file in the background
public void hashFileAsync(final File file, final HashCallback callback) {
new FileHashTask(callback).execute(file);
}
// AsyncTask for file hashing
private static class FileHashTask extends AsyncTask {
private final HashCallback callback;
FileHashTask(HashCallback callback) {
this.callback = callback;
}
@Override
protected String doInBackground(File... files) {
File file = files[0];
try {
if (file.length() < 1024 * 1024) { // Small file (< 1MB)
return SHA224.hash(file);
} else {
// Large file with progress updates
SHA224Digest digest = new SHA224Digest();
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[8192];
long totalBytes = file.length();
long bytesRead = 0;
int read;
while ((read = fis.read(buffer)) != -1) {
digest.update(buffer, 0, read);
bytesRead += read;
int progress = (int) ((bytesRead * 100) / totalBytes);
publishProgress(progress);
}
fis.close();
return digest.hexDigest();
}
} catch (Exception e) {
Log.e(TAG, "Error hashing file", e);
return null;
}
}
@Override
protected void onProgressUpdate(Integer... values) {
if (callback != null) {
callback.onProgress(values[0]);
}
}
@Override
protected void onPostExecute(String result) {
if (callback != null) {
if (result != null) {
callback.onComplete(result);
} else {
callback.onError(new Exception("Hash calculation failed"));
}
}
}
}
// Callback interface
public interface HashCallback {
void onProgress(int percentComplete);
void onComplete(String hash);
void onError(Exception e);
}
}
Android Activity Example
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.sha224.SHA224;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class HashActivity extends Activity {
private static final int REQUEST_PICK_FILE = 1;
private Button selectButton;
private TextView resultText;
private ProgressBar progressBar;
private AndroidExample hashExample;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hash);
selectButton = findViewById(R.id.select_button);
resultText = findViewById(R.id.result_text);
progressBar = findViewById(R.id.progress_bar);
hashExample = new AndroidExample();
selectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
startActivityForResult(intent, REQUEST_PICK_FILE);
}
});
// Example of hashing a string
String textHash = hashExample.hashText("Test message");
resultText.setText("String hash: " + textHash);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_PICK_FILE && resultCode == RESULT_OK) {
if (data != null) {
Uri uri = data.getData();
try {
// Copy the file to cache
File tempFile = File.createTempFile("hash_", ".tmp", getCacheDir());
copyUriToFile(uri, tempFile);
// Show progress
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
resultText.setText("Calculating hash...");
// Hash the file
hashExample.hashFileAsync(tempFile, new AndroidExample.HashCallback() {
@Override
public void onProgress(int percentComplete) {
progressBar.setProgress(percentComplete);
}
@Override
public void onComplete(String hash) {
progressBar.setVisibility(View.GONE);
resultText.setText("File hash: " + hash);
}
@Override
public void onError(Exception e) {
progressBar.setVisibility(View.GONE);
resultText.setText("Error: " + e.getMessage());
}
});
} catch (Exception e) {
resultText.setText("Error: " + e.getMessage());
}
}
}
}
private void copyUriToFile(Uri uri, File destFile) throws Exception {
InputStream is = getContentResolver().openInputStream(uri);
FileOutputStream os = new FileOutputStream(destFile);
byte[] buffer = new byte[8192];
int read;
try {
while ((read = is.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
} finally {
is.close();
os.close();
}
}
}
Android Performance Considerations
When using the SHA-224 SDK in Android applications, keep the following in mind:
- Threading: Always perform hash operations on a background thread (using AsyncTask, Handler, ExecutorService, or Kotlin coroutines) to avoid blocking the UI
- Memory Usage: Use incremental hashing with appropriate buffer sizes to minimize memory usage for large files
- Battery Impact: Hash computations can be CPU-intensive, impacting battery life. Consider adding a user option to defer intensive operations until the device is charging
- API Client: When using the API client, implement proper network handling including checking for connectivity, handling timeouts, and implementing retry logic
Frequently Asked Questions
SHA-224 is a member of the SHA-2 family and is essentially SHA-256 truncated to 224 bits with a different initialization vector. It offers a good balance between security and output size, making it suitable for applications where space is a concern but strong security is still required.
Compared to other hash functions:
- SHA-1 (160 bits): SHA-224 is more secure and resistant to collision attacks
- SHA-256 (256 bits): SHA-224 produces a smaller digest (224 bits) with similar security properties
- MD5 (128 bits): SHA-224 is significantly more secure, as MD5 is cryptographically broken
For a detailed comparison, see our SHA-224 vs SHA-256 comparison.
Yes, the SHA-224 Java SDK is designed with thread safety in mind:
- The static methods in the
SHA224
class are thread-safe and can be called from multiple threads concurrently. - Each
SHA224Digest
instance maintains its own state and should not be shared between threads without proper synchronization. If you need to use a digest in multiple threads, either create a separate instance for each thread or use proper synchronization mechanisms. - The
SHA224API
client is thread-safe for making concurrent requests.
For multi-threaded applications requiring high-throughput hashing, consider creating a pool of SHA224Digest
instances or using the provided utilities for parallel processing.
The SHA-224 Java SDK includes a Java Cryptography Architecture (JCA) provider that integrates with Java's security framework. This allows you to use SHA-224 through standard JCA interfaces like MessageDigest
.
To use the JCA integration:
- Register the provider:
Security.addProvider(new SHA224Provider());
- Get a MessageDigest instance:
MessageDigest md = MessageDigest.getInstance("SHA-224", "SHA224Provider");
- Use standard MessageDigest methods:
md.update(data); byte[] digest = md.digest();
The JCA integration is particularly useful when working with existing code that expects JCA interfaces or when using other JCA-based security features.
For efficient hashing of large files, use the incremental approach with appropriate buffer sizes:
public static String hashLargeFile(File file) throws Exception {
SHA224Digest digest = new SHA224Digest();
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[65536]; // 64KB buffer
int read;
while ((read = fis.read(buffer)) != -1) {
digest.update(buffer, 0, read);
}
}
return digest.hexDigest();
}
For even better performance with very large files:
- Use NIO channels and direct ByteBuffers for more efficient I/O
- Consider memory-mapped files for extremely large files
- Use parallel processing for multiple files
- For truly massive files, consider using the SHA224.com API which can process files server-side
Yes, the SHA-224 Java SDK is fully compatible with Android applications. It supports Android API level 19 (Android 4.4 KitKat) and higher.
For optimal performance on Android:
- Always perform hash calculations on background threads
- Use incremental hashing for large files
- Implement progress tracking for better user experience
- Consider using the API client for server-side processing of large files
The SDK's pure Java implementation ensures compatibility across all Android devices, while the API client offers an alternative for more resource-intensive operations.