As the title says I want to know if a given PDF file is already digitally signed.
I used iText to sign it but I'm not able to know if it is already signed to eventually resign it or perform other actions.
Is there a way to do it simply (possibly using iText)?
Using iText:
PdfReader reader = new PdfReader(...);
AcroFields acroFields = reader.getAcroFields();
List<String> signatureNames = acroFields.getSignatureNames();
Now signatureNames contains the names of all reachable signature fields containing signatures, cf. the JavaDoc:
/**
* Gets the field names that have signatures and are signed.
*
* #return the field names that have signatures and are signed
*/
public ArrayList<String> getSignatureNames()
If you are using newer iText version like 5.5.x here is a full working example how you can check a digitally signed PDF (a lot of useful development and changes have been done in iText since version 2.1.7):
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.security.PdfPKCS7;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DigitalSignatureCheck {
private static final Logger LOGGER = LoggerFactory.getLogger(DigitalSignatureCheck.class);
public static final boolean verifySignature(PdfReader pdfReader)
throws GeneralSecurityException, IOException {
boolean valid = false;
AcroFields acroFields = pdfReader.getAcroFields();
List<String> signatureNames = acroFields.getSignatureNames();
if (!signatureNames.isEmpty()) {
for (String name : signatureNames) {
if (acroFields.signatureCoversWholeDocument(name)) {
PdfPKCS7 pkcs7 = acroFields.verifySignature(name);
valid = pkcs7.verify();
String reason = pkcs7.getReason();
Calendar signedAt = pkcs7.getSignDate();
X509Certificate signingCertificate = pkcs7.getSigningCertificate();
Principal issuerDN = signingCertificate.getIssuerDN();
Principal subjectDN = signingCertificate.getSubjectDN();
LOGGER.info("valid = {}, date = {}, reason = '{}', issuer = '{}', subject = '{}'",
valid, signedAt.getTime(), reason, issuerDN, subjectDN);
break;
}
}
}
return valid;
}
private static void validate(String name)
throws IOException, GeneralSecurityException {
InputStream is = DigitalSignatureCheck.class.getClassLoader()
.getResourceAsStream(name);
PdfReader reader = new PdfReader(is);
boolean ok = verifySignature(reader);
LOGGER.info("'{}' is {}signed", name, ok ? "" : "NOT ");
}
public static void main(String[] args) throws Exception {
validate("any.pdf"); // if placed in resources' root
}
}
Using the LOGGER is just for displaying the result.
I had similar problem but I'm using itext 7 and there is no
PdfReader reader = new PdfReader(...);
AcroFields acroFields = reader.getAcroFields();
in current version.
So I have used this solution [UPDATED]
private boolean hasSignatures(final PdfDocument pdfDocument) {
final List<String> signatureNames = new SignatureUtil(pdfDocument).getSignatureNames();
return !signatureNames.isEmpty();
}
One example here
public static final String verifSign(PdfReader pdfReader)
{
KeyStore kall = PdfPKCS7.loadCacertsKeyStore();
AcroFields acroFields = pdfReader.getAcroFields();
List<String> signatureNames = acroFields.getSignatureNames();
if (signatureNames.isEmpty())
{
return ("El documento no tiene ni una firma registrada");
}
for(String name : signatureNames)
{
if (!acroFields.signatureCoversWholeDocument(name))
{
return ("la firma: "+name+" does not covers the whole document.");
}
PdfPKCS7 pk = acroFields.verifySignature(name);
Certificate[] certificates = pk.getCertificates();
Calendar cal = pk.getSignDate();
//System.out.println("Document modified: " + !pk.verify());
Object fails[] = PdfPKCS7.verifyCertificates(certificates, kall, null, cal);
if (fails == null)
{
// Documento PDF firmado correctamente
}
else
{
return ("Firma no vĂ¡lida");
}
}
// Todo firmado correctamente
return null;
}
http://www.berthou.com/us/2009/07/01/verify-pdf-signature-with-itext/
Related
I'm trying to write Ruby code that copies functionality of Java code that I received as a working example. I believe I got most of it, but in the end when I'm trying to verify Signature with server, I'm getting this:
IAM Exception: IAM-1000:Signature length not correct: got 256 but was expecting 384
Here is the Java example:
import java.io.ObjectStreamException;
import java.security.KeyRep;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.codec.binary.Base64;
public class SignatureGenerator {
public static void main(String[] args) {
SignatureGenerator generator = new SignatureGenerator();
String consumerId = "xxxxxx";
String priviateKeyVersion = "1";
String privateKey = ""; // omitted for security reasons
long intimestamp = System.currentTimeMillis();
System.out.println("consumerId: " + consumerId);
System.out.println("intimestamp: " + intimestamp);
Map<String, String> map = new HashMap<>();
map.put("AA_CONSUMER.ID", consumerId);
map.put("AA_CONSUMER.INTIMESTAMP", Long.toString(intimestamp));
map.put("AA_SEC.KEY_VERSION", priviateKeyVersion);
String[] array = canonicalize(map);
String data = null;
try {
data = generator.generateSignature(privateKey, array[1]);
} catch(Exception e) { }
System.out.println("Signature: " + data);
}
public String generateSignature(String key, String stringToSign) throws Exception {
Signature signatureInstance = Signature.getInstance("SHA256WithRSA");
ServiceKeyRep keyRep = new ServiceKeyRep(KeyRep.Type.PRIVATE, "RSA", "PKCS#8", Base64.decodeBase64(key));
PrivateKey resolvedPrivateKey = (PrivateKey) keyRep.readResolve();
signatureInstance.initSign(resolvedPrivateKey);
byte[] bytesToSign = stringToSign.getBytes("UTF-8");
signatureInstance.update(bytesToSign);
byte[] signatureBytes = signatureInstance.sign();
String signatureString = Base64.encodeBase64String(signatureBytes);
return signatureString;
}
protected static String[] canonicalize(Map<String, String> headersToSign) {
StringBuffer canonicalizedStrBuffer=new StringBuffer();
StringBuffer parameterNamesBuffer=new StringBuffer();
Set<String> keySet=headersToSign.keySet();
// Create sorted key set to enforce order on the key names
SortedSet<String> sortedKeySet=new TreeSet<String>(keySet);
for (String key :sortedKeySet) {
Object val=headersToSign.get(key);
parameterNamesBuffer.append(key.trim()).append(";");
canonicalizedStrBuffer.append(val.toString().trim()).append("\n");
}
return new String[] {parameterNamesBuffer.toString(), canonicalizedStrBuffer.toString()};
}
class ServiceKeyRep extends KeyRep {
private static final long serialVersionUID = -7213340660431987616L;
public ServiceKeyRep(Type type, String algorithm, String format, byte[] encoded) {
super(type, algorithm, format, encoded);
}
protected Object readResolve() throws ObjectStreamException {
return super.readResolve();
}
}
}
And here is my Ruby code that tries to do the same + request in the end
consumer_id = 'xxxxx'
key_version = '1'
private_key = '' # omitted for security
to_encrypt = {
"AA_CONSUMER.ID" => consumer_id,
"AA_CONSUMER.INTIMESTAMP" => DateTime.now.strftime('%Q'),
"AA_SEC.KEY_VERSION" => key_version
}
names = ''
canstr = ''
# sort
sorted_keys = to_encrypt.keys.sort
sorted_keys.each do |key|
value = to_encrypt[key]
names += "#{key};"
canstr += "#{value}\n"
end
final_arr = [names, canstr]
key = OpenSSL::PKey::RSA.new(Base64.decode64(private_key))
signature = key.sign('SHA256', final_arr[1])
signature_string = Base64.urlsafe_encode64(signature)
headers = to_encrypt.merge("AA_SEC.AUTH_SIGNATURE" => signature_string)
response = Faraday.send(:get, url, {}, headers)
Any suggestion?
It seems that the server expects a signature 384 bytes long, and you are sending a signature 256 bytes long.
It is possibly worth to try
key.sign('SHA384', final_arr[1])
in order for your digest to be 384 bytes.
I need to remove the root ca from a certificate chain. What is the best way to do that? I found a way to read the values but they can't be deleted.
You have not made it clear what you want, but here is a simple example to remove all certs that appear self-signed (same issuer and subject names -- it would better to actually verify the signature, feel free to add that code) from the special CMS structure used to convey certificate chains. This code uses the Bouncycastle PKIX library, but it's possible to do this with a few more lines of code using only the Bouncycastle core library.
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.util.CollectionStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.function.Predicate;
public class PKCS7Certs {
private static final String P7_CERT_FILENAME = "stackexchange-com-chain.p7b";
private static final String OUT_FILENAME = "stackexchange-com-chain-no-root.p7b";
public static void main(String[] args) throws Exception {
Path p7In = Paths.get(P7_CERT_FILENAME);
Path p7Out = Paths.get(OUT_FILENAME);
removeRoot(p7In, p7Out);
}
private static void removeRoot(Path p7In, Path p7Out) throws Exception{
CMSSignedData contentInfo = new CMSSignedData(Files.newInputStream(p7In));
Collection<X509CertificateHolder> certs = contentInfo.getCertificates().getMatches(null);
// Delete the root cert(s)
certs.removeIf(new Predicate<X509CertificateHolder>() {
#Override
public boolean test(X509CertificateHolder cert) {
return ! cert.getIssuer().equals(cert.getSubject());
}
});
// create a new SignedData ContentInfo with the non-root certs
CollectionStore newStore = new CollectionStore(certs);
CMSSignedData newCI = CMSSignedData.replaceCertificatesAndCRLs(
contentInfo,
newStore,
contentInfo.getAttributeCertificates(),
contentInfo.getCRLs()
);
Files.write(p7Out, newCI.getEncoded());
}
}
Other possibility to remove the root ca from a signature:
private byte[] removeRoot(byte[] b) throws CMSException, IOException {
CMSSignedData signature = new CMSSignedData(b);
Store<X509CertificateHolder> cs = signature.getCertificates();
Collection<X509CertificateHolder> certificates = cs.getMatches(new Selector<>() {
#Override
public boolean match(final X509CertificateHolder obj) {
return !Objects.equals(obj.getIssuer(), obj.getSubject());
}
#Override
public Object clone() {
return null;
}
});
final CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSigners(signature.getSignerInfos());
generator.addAttributeCertificates(signature.getAttributeCertificates());
certificates.forEach(o -> {
try {
generator.addCertificate(o);
} catch (CMSException e) {
throw new RuntimeException(e);
}
});
CMSSignedData signedData = generator.generate(signature.getSignedContent(), true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ASN1InputStream asn1 = new ASN1InputStream(signedData.getEncoded())) {
DEROutputStream dos = new DEROutputStream(out);
dos.writeObject(asn1.readObject());
}
return out.toByteArray();
}
I tagged manually file temp1.zip under bucket with TAG:
key = VirusScan value = succeed. I want to verify the tag i have on the file from my code that indeed it is key = VirusScan value = succeed.
AmazonS3 s3 = null;
s3 = (AmazonS3)((AmazonS3ClientBuilder)((AmazonS3ClientBuilder)AmazonS3ClientBuilder
.standard()
.withCredentials(new ProfileCredentialsProvider()))
.withRegion(Regions.US_EAST_1))
.build();
GetObjectTaggingRequest getTaggingRequest = new GetObjectTaggingRequest(bucketName, keyName);
s3.getObjectTagging(getTaggingRequest);
I am getting exception on:
s3.getObjectTagging(getTaggingRequest);
The specified key does not exist.
This code works nicely:
package aws.example.s3;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
public class GetObjectTags {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println("Please specify a bucket name and key name");
System.exit(1);
}
// snippet-start:[s3.java2.list_objects.main]
String bucketName = args[0];
String keyName = args[1];
System.out.println("Retrieving Object Tags for " + keyName);
final AmazonS3 s3 = AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
try {
GetObjectTaggingRequest getTaggingRequest = new GetObjectTaggingRequest(bucketName, keyName);
GetObjectTaggingResult tags = s3.getObjectTagging(getTaggingRequest);
List<Tag> tagSet= tags.getTagSet();
//Iterate through the list
Iterator<Tag> tagIterator = tagSet.iterator();
while(tagIterator.hasNext()) {
Tag tag = (Tag)tagIterator.next();
System.out.println(tag.getKey());
System.out.println(tag.getValue());
}
} catch (AmazonServiceException e) {
System.err.println(e.getErrorMessage());
System.exit(1);
}
}
}
My server was using java 7 and is running fine in FIPS mode. Now we are upgrading to jre8 and the following exception is coming during the startup while loading cacerts.
java.lang.RuntimeException: java.security.NoSuchProviderException: no such provider: SunEC
at sun.security.util.ECUtil.getKeyFactory(ECUtil.java:96)
at sun.security.util.ECUtil.decodeX509ECPublicKey(ECUtil.java:102)
at sun.security.pkcs11.P11ECKeyFactory.engineGeneratePublic(P11ECKeyFactory.java:170)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at sun.security.x509.X509Key.buildX509Key(X509Key.java:223)
at sun.security.x509.X509Key.parse(X509Key.java:170)
at sun.security.x509.CertificateX509Key.<init>(CertificateX509Key.java:75)
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:667)
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:167)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1806)
at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:195)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:99)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:747)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:55)
at java.security.KeyStore.load(KeyStore.java:1433)
at org.apache.commons.ssl.KeyStoreBuilder.tryJKS(KeyStoreBuilder.java:476)
at org.apache.commons.ssl.KeyStoreBuilder.parse(KeyStoreBuilder.java:383)
at org.apache.commons.ssl.TrustMaterial.<init>(TrustMaterial.java:212)
at org.apache.commons.ssl.TrustMaterial.<init>(TrustMaterial.java:165)
at org.apache.commons.ssl.TrustMaterial.<init>(TrustMaterial.java:170)
at org.apache.commons.ssl.TrustMaterial.<init>(TrustMaterial.java:175)
at org.apache.commons.ssl.TrustMaterial.<clinit>(TrustMaterial.java:88)
at org.opensaml.xml.security.x509.X509Util.decodeCertificate(X509Util.java:317)
Caused by: java.security.NoSuchProviderException: no such provider: SunEC
at sun.security.jca.GetInstance.getService(GetInstance.java:83)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.KeyFactory.getInstance(KeyFactory.java:211)
at sun.security.util.ECUtil.getKeyFactory(ECUtil.java:94)
The only modification i made is to add TLSv1.2 to jdk.tls.disabledAlgorithms=SSLv3,TLSv1.2 since NSS is not supporting TLSv1.2.
I was able to load the same in jre7u72 builds and i see that the sunpkcs11.jar is modified in java 8. It shouldnt be a cacerts issue since i copied the java7 cacerts to java8 and the problem still remains.
Has anyone seen this issue before? Should it be a java bug?
=== EDIT ===
Opened a java 8 bug. I wrote a tool which ran against jre7 with success and failed with jre8.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import sun.security.provider.Sun;
import sun.security.rsa.SunRsaSign;
/**
* Before running the tool run the following in your Linux box.
*
1. export LD_LIBRARY_PATH=/usr/lib64
2. mkdir /tmp/fips/nssdb
3. modutil -create -dbdir /tmp/fips/nssdb/
4. modutil -fips true -dbdir /tmp/fips/nssdb
5. modutil -changepw "NSS FIPS 140-2 Certificate DB" -dbdir /tmp/fips/nssdb
(Give a strong password like password1!)
* #author atulsm#gmail.com
*
*/
public class TestKeyStoreFIPS {
public static final String NSS_LIB_DIR_PROP = "nss.lib";
public static final String NSS_DB_DIR_PROP = "nss.db";
public static final String SUN_JSSE = "SunJSSE";
public static List<String> disabledAlgs = new ArrayList<String>();
private static final Logger logger = Logger.getLogger(TestKeyStoreFIPS.class.getName());
/**
* #param args
*/
public static void main(String[] args) throws Exception{
if(args.length != 2){
System.out.println("Usage eg: java -Dnss.lib=/usr/lib64 -Dnss.db=/tmp/fips/nssdb -Djavax.net.ssl.keyStorePassword=password1! TestKeyStoreFIPS /tmp/jre8/lib/security/cacerts changeit");
System.exit(1);
}
enablePkcs11Jsse(System.getProperty(NSS_LIB_DIR_PROP), System.getProperty(NSS_DB_DIR_PROP));
testFips();
String file = args[0];
char[] keystorePassword = args[1].toCharArray();
FileInputStream keystoreStream = new FileInputStream(file);
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(keystoreStream, keystorePassword);
Enumeration<String> aliases = keyStore.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
System.out.println(alias + " : " + keyStore.getCertificate(alias).getType());
}
}
private static void testFips(){
String keyPass = System.getProperty("javax.net.ssl.keyStorePassword");
KeyStore store;
try {
store = KeyStore.getInstance("PKCS11");
if (keyPass != null) {
store.load(null, keyPass.toCharArray());
} else {
store.load(null, null);
}
System.out.println("FIPS test success");
} catch (Throwable e) {
e.printStackTrace();
store = null;
System.out.println("FIPS test failed");
}
}
public static void enablePkcs11Jsse( String libDir, String dbDir) throws Exception {
removeAllProviders();
Provider nss = getNSSFIPSProvider( libDir, dbDir);
removeDisabledAlgos(nss);
Security.insertProviderAt(nss, 1);
Provider sunJsse = new com.sun.net.ssl.internal.ssl.Provider(nss);
removeDisabledAlgos(sunJsse);
Security.insertProviderAt(sunJsse,2);
Sun sun = new Sun();
removeDisabledAlgos(sun);
Security.insertProviderAt(sun,3);
SunRsaSign sunrsa = new SunRsaSign();
removeDisabledAlgos(sunrsa);
Security.insertProviderAt(sunrsa,4);
}
private static Provider getNSSFIPSProvider( String libDir, String dbDir) throws Exception {
if(libDir == null || dbDir == null) {
throw new Exception(NSS_LIB_DIR_PROP + " or " + NSS_DB_DIR_PROP + " not set.");
}
Properties props = new Properties();
props.put("name", "NSSfips");
props.put("nssLibraryDirectory", libDir);
props.put("nssSecmodDirectory", dbDir);
props.put("nssModule", "fips");
props.put("nssDbMode", "readWrite");
return createProvider(props);
}
private static Provider createProvider(Properties props) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
props.store(out, null);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
Provider ret = new sun.security.pkcs11.SunPKCS11(in);
if (logger.isLoggable(Level.FINE)) {
// Log all of the registered services
for (Map.Entry<Object, Object> entry : ret.entrySet()) {
logger.log(Level.FINE, "{0} = {1}", new Object[]{entry.getKey(), entry.getValue()});
}
}
return ret;
}
private static void removeAllProviders(){
Provider[] providers = Security.getProviders();
for(Provider prov : providers){
Security.removeProvider(prov.getName());
}
}
private static void removeDisabledAlgos(Provider provider){
for(String alg : disabledAlgs){
if(provider.getProperty(alg) != null){
logger.info("Removing algorithm " + alg + " from provider " + provider);
provider.remove(alg);
}
}
}
}
While i explore other options, one approach which worked is to copy one of my truststores as jre/lib/security/jssecacerts. I see that the code is giving precedence to jssecacerts than cacerts and its working fine for now.
Some questions about using Zxing...
I write the following code to read barcode from an image:
public class BarCodeDecode
{
/**
* #param args
*/
public static void main(String[] args)
{
try
{
String tmpImgFile = "D:\\FormCode128.TIF";
Map<DecodeHintType,Object> tmpHintsMap = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
tmpHintsMap.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
tmpHintsMap.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.allOf(BarcodeFormat.class));
tmpHintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
File tmpFile = new File(tmpImgFile);
String tmpRetString = BarCodeUtil.decode(tmpFile, tmpHintsMap);
//String tmpRetString = BarCodeUtil.decode(tmpFile, null);
System.out.println(tmpRetString);
}
catch (Exception tmpExpt)
{
System.out.println("main: " + "Excpt err! (" + tmpExpt.getMessage() + ")");
}
System.out.println("main: " + "Program end.");
}
}
public class BarCodeUtil
{
private static BarcodeFormat DEFAULT_BARCODE_FORMAT = BarcodeFormat.CODE_128;
/**
* Decode method used to read image or barcode itself, and recognize the barcode,
* get the encoded contents and returns it.
* #param whatFile image that need to be read.
* #param config configuration used when reading the barcode.
* #return decoded results from barcode.
*/
public static String decode(File whatFile, Map<DecodeHintType, Object> whatHints) throws Exception
{
// check the required parameters
if (whatFile == null || whatFile.getName().trim().isEmpty())
throw new IllegalArgumentException("File not found, or invalid file name.");
BufferedImage tmpBfrImage;
try
{
tmpBfrImage = ImageIO.read(whatFile);
}
catch (IOException tmpIoe)
{
throw new Exception(tmpIoe.getMessage());
}
if (tmpBfrImage == null)
throw new IllegalArgumentException("Could not decode image.");
LuminanceSource tmpSource = new BufferedImageLuminanceSource(tmpBfrImage);
BinaryBitmap tmpBitmap = new BinaryBitmap(new HybridBinarizer(tmpSource));
MultiFormatReader tmpBarcodeReader = new MultiFormatReader();
Result tmpResult;
String tmpFinalResult;
try
{
if (whatHints != null && ! whatHints.isEmpty())
tmpResult = tmpBarcodeReader.decode(tmpBitmap, whatHints);
else
tmpResult = tmpBarcodeReader.decode(tmpBitmap);
// setting results.
tmpFinalResult = String.valueOf(tmpResult.getText());
}
catch (Exception tmpExcpt)
{
throw new Exception("BarCodeUtil.decode Excpt err - " + tmpExcpt.toString() + " - " + tmpExcpt.getMessage());
}
return tmpFinalResult;
}
}
I try to read the following two images that contains code128 and QRCode.
It can work for the code128 but not for the QRCode.
Any one knows why...
Please go through this link for complete Tutorial. The author of this code is Joe. I have not developed this code, so I am just doing Copy paste to make sure this is available in case link is broken.
The author is using ZXing(Zebra Crossing Library) you can download it from here, for this tutorial.
QR Code Write and Read Program in Java:
package com.javapapers.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QRCode {
public static void main(String[] args) throws WriterException, IOException,
NotFoundException {
String qrCodeData = "Hello World!";
String filePath = "QRCode.png";
String charset = "UTF-8"; // or "ISO-8859-1"
Map<EncodeHintType, ErrorCorrectionLevel> hintMap = new HashMap<EncodeHintType, ErrorCorrectionLevel>();
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
createQRCode(qrCodeData, filePath, charset, hintMap, 200, 200);
System.out.println("QR Code image created successfully!");
System.out.println("Data read from QR Code: "
+ readQRCode(filePath, charset, hintMap));
}
public static void createQRCode(String qrCodeData, String filePath,
String charset, Map hintMap, int qrCodeheight, int qrCodewidth)
throws WriterException, IOException {
BitMatrix matrix = new MultiFormatWriter().encode(
new String(qrCodeData.getBytes(charset), charset),
BarcodeFormat.QR_CODE, qrCodewidth, qrCodeheight, hintMap);
MatrixToImageWriter.writeToFile(matrix, filePath.substring(filePath
.lastIndexOf('.') + 1), new File(filePath));
}
public static String readQRCode(String filePath, String charset, Map hintMap)
throws FileNotFoundException, IOException, NotFoundException {
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(
new BufferedImageLuminanceSource(
ImageIO.read(new FileInputStream(filePath)))));
Result qrCodeResult = new MultiFormatReader().decode(binaryBitmap,
hintMap);
return qrCodeResult.getText();
}
}
Maven dependency for the ZXing QR Code library:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>2.2</version>
</dependency>
Curiously your code works for me, but I had to remove the follow hint.
tmpHintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
When my image is not pure barcode, this hint broke my result.
Thank you!
This Code worked for me.
public static List<string> ScanForBarcodes(string path)
{
return ScanForBarcodes(new Bitmap(path));
}
public static List<string> ScanForBarcodes(Bitmap bitmap)
{
// initialize a new Barcode reader.
BarcodeReader reader = new BarcodeReader
{
TryHarder = true, // TryHarder is slower but recognizes more Barcodes
PossibleFormats = new List<BarcodeFormat> // in the ZXing There is an Enum where all supported barcodeFormats were contained
{
BarcodeFormat.CODE_128,
BarcodeFormat.QR_CODE,
//BarcodeFormat. ... ;
}
};
return reader.DecodeMultiple(bitmap).Select(result => result.Text).ToList(); // return only the barcode string.
// If you want the full Result use: return reader.DecodeMultiple(bitmap);
}
Did you use this (ZXing.Net) Lib?