Search code examples
javaitextdigital-signaturebouncycastle

Digital Signature Verification with itext not working


I am using itext library for verify digitally signed pdf, following example available on https://developers.itextpdf.com/examples/security/digital-signatures-white-paper/digital-signatures-chapter-5 site. I am getting following error while verifying the digitally signed document. Can any one help to resolve this.

Exception in thread "main" ExceptionConverter: java.security.NoSuchAlgorithmException: SHA256with1.2.840.10045.4.3.2 Signature not available
at java.security.Signature.getInstance(Signature.java:229)
at com.itextpdf.text.pdf.security.PdfPKCS7.initSignature(PdfPKCS7.java:697)
at com.itextpdf.text.pdf.security.PdfPKCS7.<init>(PdfPKCS7.java:459)
at com.itextpdf.text.pdf.AcroFields.verifySignature(AcroFields.java:2420)
at com.itextpdf.text.pdf.AcroFields.verifySignature(AcroFields.java:2373)
at nic.test.C5_01_SignatureIntegrity.verifySignature(C5_01_SignatureIntegrity.java:24)
at test.ExtractSignInfor.inspectSignature(ExtractSignInfor.java:95)
at test.ExtractSignInfor.inspectSignatures(ExtractSignInfor.java:135)
at test.ExtractSignInfor.main(ExtractSignInfor.java:63)

Solution

  • As Michaël Demey already explained in his answer, iText 5.x currently does not support the algorithm OID in question.

    Fortunately, though, iText 5.x mostly relies on the underlying BouncyCastle library for security algorithm support during signature validation and only does very little itself, all that is missing is that iText properly finds the name of the "encryption algorithm" ECDSA (in quotes as it is not really an encryption because there is no parallel decryption) for the OID.

    You can add verification support of that OID in iText 5.x (tested in the current SNAPSHOT for 5.5.13) like this:

    java.lang.reflect.Field algorithmNamesField = com.itextpdf.text.pdf.security.EncryptionAlgorithms.class.getDeclaredField("algorithmNames");
    algorithmNamesField.setAccessible(true);
    @SuppressWarnings("unchecked")
    HashMap<String, String> algorithmNames = (HashMap<String, String>) algorithmNamesField.get(null);
    algorithmNames.put("1.2.840.10045.4.3.2", "ECDSA");
    

    With this addition executed, iText validation

    PdfReader reader = new PdfReader(resource);
    AcroFields acroFields = reader.getAcroFields();
    
    List<String> names = acroFields.getSignatureNames();
    for (String name : names) {
       System.out.println("Signature name: " + name);
       System.out.println("Signature covers whole document: " + acroFields.signatureCoversWholeDocument(name));
       System.out.println("Document revision: " + acroFields.getRevision(name) + " of " + acroFields.getTotalRevisions());
       PdfPKCS7 pk = acroFields.verifySignature(name);
       System.out.println("Subject: " + CertificateInfo.getSubjectFields(pk.getSigningCertificate()));
       System.out.println("Document verifies: " + pk.verify());
    }
    

    shows

    Signature name: Signature1
    Signature covers whole document: true
    Document revision: 1 of 1
    Subject: {ST=[Rajasthan], C=[India], CN=[Devi Singh Pilwal], O=[Personal]}
    Document verifies: true
    

    (Test code in VerifySignature.java method testVerifyTestDsp)

    Beware...

    • The code above uses reflection as the Map in question is not public. Depending on your environment, such a solution might not be allowed. In that case you'd have to patch iText accordingly. Furthermore, as the accessed Map is not public, the solution might fail in future versions. (This is unlikely as iText 5 is in maintenance mode, but one never knows.)

    • This only adds support for this very OID. Most likely support for a number of other OIDs can similarly be added but not necessarily for all.

    • Furthermore, I have only tested this in a verification use case. ECDSA signing might require completely different changes.