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: Requires com.squareup.okhttp3:okhttp for API client functionality
  • sha224-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 encoding
  • static String hash(String input, String encoding) - Hash a string using specified encoding
  • static String hash(byte[] input) - Hash binary data
  • static String hash(InputStream input) - Hash data from an input stream
  • static String hash(File file) - Hash a file
  • static 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 encoding
  • static boolean verify(String expectedHash, String input, String encoding) - Verify a string using specified encoding
  • static boolean verify(String expectedHash, byte[] input) - Verify binary data
  • static boolean verify(String expectedHash, InputStream input) - Verify data from an input stream
  • static 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 data
  • void update(byte[] input, int offset, int length) - Update with a portion of the input array
  • void update(String input) - Update with a string (UTF-8 encoded)
  • void update(String input, String encoding) - Update with a string using specified encoding
  • void update(InputStream input) - Update with data from an input stream
  • byte[] digest() - Get the final hash as raw bytes
  • String hexDigest() - Get the final hash as a hex string
  • String base64Digest() - Get the final hash as a base64 string
  • void reset() - Reset the digest for reuse
  • SHA224Digest 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 comparison
  • static String bytesToHex(byte[] bytes) - Convert raw digest bytes to a hex string
  • static String bytesToBase64(byte[] bytes) - Convert raw digest bytes to a base64 string
  • static 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 API
  • String hashText(String text, String encoding) - Hash text with specified encoding
  • String hashFile(File file) - Hash a file using the API
  • String hashFile(File file, ProgressListener listener) - Hash with progress tracking
  • boolean verify(String expectedHash, String text) - Verify a hash using the API
  • boolean verify(String expectedHash, File file) - Verify a file hash
  • BatchResult batchHash(List<BatchItem> items) - Batch hash multiple items
  • BatchVerifyResult 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

What makes SHA-224 different from other hash functions?

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.

Is the SHA-224 Java SDK thread-safe?

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.

How does the JCA integration work?

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:

  1. Register the provider: Security.addProvider(new SHA224Provider());
  2. Get a MessageDigest instance: MessageDigest md = MessageDigest.getInstance("SHA-224", "SHA224Provider");
  3. 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.

How can I efficiently hash large files?

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
Does the SDK support Android applications?

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.

Next Steps

API Documentation

Learn about the complete REST API.

View API Docs

Java Examples

Explore more code examples on GitHub.

View Examples

Implementation Guide

Step-by-step implementation guide.

View Guide