GroupDocs.Signature for Java supports verifying digital signatures embedded in raster images using steganography (LSB - Least Significant Bit technique). These signatures are invisibly embedded in image pixels and can be verified using the password that was used during signing. See eSign image with Digital signature to learn how to add such a signature.
The verification settings are kept in the ImageDigitalVerifyOptions class (package com.groupdocs.signature.options.verify), and the result is a VerificationResult object.
Key Features
Password-Based Verification: Verify signatures using the password from signing
Detection Thresholds: Configurable detection threshold for verification (0-100%)
Full Data Extraction: Optional full data extraction mode for maximum accuracy
Format Support: Works with PNG and JPG image formats
Probability Detection: Get probability scores when using full extraction mode
How It Works
Digital signature verification in images:
Extraction: The embedded signature data is extracted from the least significant bits of image pixels
Password Verification: The extracted data is verified using the provided password
Threshold Check: The detection threshold determines if enough signature data was found
Result: Returns verification status; for a valid signature, the getSucceeded() list of the result holds a DigitalSignature object whose isValid() returns true
The embedded data does not contain a certificate, so the returned DigitalSignature carries no certificate details.
Requirements
Password Requirements
Must Match: Password must match the one used during signing
Case Sensitive: Yes, passwords are case-sensitive
Minimum Length: 4 characters (same as signing)
Supported Formats
PNG - Supported
JPG/JPEG - Supported
BMP - Not supported for digital steganography signatures
Basic Usage
Verifying a Signed Image
// Load the signed image
Signaturesignature=newSignature("signed_image.png");try{// Create verify options with the same password used for signing
ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions();verifyOptions.setPassword("MySecurePassword123");// Verify the signature
VerificationResultverifyResult=signature.verify(verifyOptions);if(verifyResult.isValid()){System.out.println("Digital signature is valid!");System.out.println("Found "+verifyResult.getSucceeded().size()+" verified signatures.");}else{System.out.println("Digital signature is invalid or not found.");}}finally{signature.dispose();}
Complete Verification Example
StringsignedFile="signed_image.png";Stringpassword="SecurePassword123";// Verify the signed image
Signaturesignature=newSignature(signedFile);try{ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions(password);VerificationResultverifyResult=signature.verify(verifyOptions);if(verifyResult.isValid()){System.out.println("Digital signature is valid!");System.out.println("Verified signatures: "+verifyResult.getSucceeded().size());for(BaseSignaturesig:verifyResult.getSucceeded()){if(siginstanceofDigitalSignature){DigitalSignaturedigitalSig=(DigitalSignature)sig;System.out.println(" Is Valid: "+digitalSig.isValid());}}}else{System.out.println("Digital signature is invalid or not found.");}}finally{signature.dispose();}
Advanced Usage
Custom Detection Threshold
The detection threshold determines the minimum percentage of signature data that must be detected for verification to succeed (0-100%). Default is 75%.
Signaturesignature=newSignature("signed_image.png");try{ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions("MySecurePassword123");verifyOptions.setDetectionThresholdPercent(90);// Require 90% detection
VerificationResultverifyResult=signature.verify(verifyOptions);if(verifyResult.isValid()){System.out.println("Signature verified with 90% threshold");}else{System.out.println("Signature verification failed - threshold not met");}}finally{signature.dispose();}
Testing Different Thresholds
Signaturesignature=newSignature("signed_image.png");try{Stringpassword="MyPassword123";// Test different threshold values
int[]thresholds={50,75,90,95,100};for(intthreshold:thresholds){ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions(password);verifyOptions.setDetectionThresholdPercent(threshold);VerificationResultverifyResult=signature.verify(verifyOptions);System.out.println("Threshold "+threshold+"%: "+(verifyResult.isValid()?"Valid":"Invalid"));}}finally{signature.dispose();}
Full Data Extraction Mode
For maximum accuracy, you can enable full data extraction mode. This performs a more thorough analysis and provides a probability score.
Signaturesignature=newSignature("signed_image.png");try{ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions("MySecurePassword123");verifyOptions.setUseFullDataExtraction(true);// Enable full extraction
verifyOptions.setDetectionThresholdPercent(85);VerificationResultverifyResult=signature.verify(verifyOptions);// getDetectedProbability() is set when full data extraction is enabled
Integerprobability=verifyOptions.getDetectedProbability();if(verifyResult.isValid()){System.out.println("Signature verified with full data extraction");if(probability!=null){System.out.println("Detection probability: "+probability+"%");}}else{System.out.println("Signature not detected or below threshold. Probability: "+probability+"%");}}finally{signature.dispose();}
Working with Streams
You can verify images using streams instead of file paths:
// Verifying from stream
try(InputStreamimageStream=newFileInputStream("signed_image.png")){Signaturesignature=newSignature(imageStream);try{ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions("MySecurePassword123");VerificationResultverifyResult=signature.verify(verifyOptions);if(verifyResult.isValid()){System.out.println("Signature is valid!");}}finally{signature.dispose();}}
Detection Threshold
The setDetectionThresholdPercent method controls how strict the verification is:
Lower Threshold (0-50%): More lenient, may detect partial signatures
Default (75%): Balanced detection
Higher Threshold (90-100%): Very strict, requires nearly complete signature data
Choosing the Right Threshold
// For lenient verification (may detect partial signatures)
ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions("MyPassword");verifyOptions.setDetectionThresholdPercent(50);// Lower threshold
// For balanced verification (default)
verifyOptions.setDetectionThresholdPercent(75);// Default
// For strict verification (requires complete signature)
verifyOptions.setDetectionThresholdPercent(95);// High threshold
Full Data Extraction
When setUseFullDataExtraction(true) is set:
Performs a more thorough analysis of the image data
Provides a probability score (0-100%) in getDetectedProbability(); without full data extraction it returns null
The signature is valid when the probability reaches the detection threshold
Useful when standard verification is uncertain
When to Use Full Data Extraction
Use full data extraction when:
Standard verification gives uncertain results
You need maximum accuracy
Image may have been slightly modified
You want probability scores
Stringpassword="MyPassword";// First try standard verification
ImageDigitalVerifyOptionsstandardOptions=newImageDigitalVerifyOptions(password);VerificationResultresult=signature.verify(standardOptions);if(!result.isValid()){// If standard verification fails, try full extraction with a lower threshold
ImageDigitalVerifyOptionsfullOptions=newImageDigitalVerifyOptions(password);fullOptions.setUseFullDataExtraction(true);fullOptions.setDetectionThresholdPercent(70);result=signature.verify(fullOptions);System.out.println("Full extraction: "+(result.isValid()?"valid":"invalid")+", probability "+fullOptions.getDetectedProbability()+"%");}
Error Handling
Wrong Password
A wrong password does not raise an error: the signature is simply not detected and isValid() returns false.
Signaturesignature=newSignature("signed_image.png");try{ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions("WrongPassword");VerificationResultverifyResult=signature.verify(verifyOptions);if(!verifyResult.isValid()){System.out.println("Signature verification failed - wrong password or no signature found.");}}finally{signature.dispose();}
Password and Threshold Validation
A password shorter than 4 characters and a detection threshold outside the 0-100 range are rejected with GroupDocsSignatureException:
try{Signaturesignature=newSignature("signed_image.png");try{ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions("123");// Too short, minimum is 4 characters
// verifyOptions.setDetectionThresholdPercent(150); // Invalid as well: must be 0-100
VerificationResultverifyResult=signature.verify(verifyOptions);}finally{signature.dispose();}}catch(GroupDocsSignatureExceptionex){System.out.println("Validation error: "+ex.getMessage());// Output: "Password must be at least 4 characters long."
// or "Detection threshold must be within 0-100 range."
}
Best Practices
1. Use Appropriate Thresholds
// For critical applications, use a higher threshold and full data extraction
ImageDigitalVerifyOptionsverifyOptions=newImageDigitalVerifyOptions(password);verifyOptions.setDetectionThresholdPercent(95);verifyOptions.setUseFullDataExtraction(true);
2. Handle Verification Results Properly
Check isValid() of the VerificationResult before you process the getSucceeded() list.
3. Try Full Extraction if Standard Verification Fails