/** * <a href="http://itext.2136553.n4.nabble.com/trying-to-remove-a-signature-from-pdf-file-tt4660983.html"> * trying to remove a signature from pdf file * </a> * <br/> * <a href="http://itext.2136553.n4.nabble.com/attachment/4660983/0/PDFSignedFirmaCerta.pdf"> * PDFSignedFirmaCerta.pdf * </a> * <p> * Indeed, this code fails with a {@link NullPointerException}. The cause is that a dubious construct * created by the signature software then is processed by iText code not sufficiently defensively programmed: * The signature claims to have an annotation on a page but that page does claim not to have any anotations * at all. * </p> */ @Test public void testRemoveSignatureFromPDFSignedFirmaCerta() throws IOException, GeneralSecurityException, DocumentException { try ( InputStream inputStream = getClass().getResourceAsStream("PDFSignedFirmaCerta.pdf"); OutputStream outputStream = new FileOutputStream(new File(RESULT_FOLDER, "PDFSignedFirmaCerta-withoutSig.pdf"))) { Provider provider = new BouncyCastleProvider(); Security.addProvider(provider); PdfReader reader = new PdfReader(inputStream, null); AcroFields af = reader.getAcroFields(); ArrayList<String> names = af.getSignatureNames(); for (String name : names) { System.out.println("Signature name: " + name); System.out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name)); PdfPKCS7 pk = af.verifySignature(name, provider.getName()); System.out.println("SignatureDate: " + pk.getSignDate()); System.out.println("Certificate: " + pk.getSigningCertificate()); System.out.println("Document modified: " + !pk.verify()); af.removeField(name); } PdfStamper stamper = new PdfStamper(reader, outputStream, '\0'); stamper.close(); } }
/** * <a href="http://stackoverflow.com/questions/37726215/why-does-my-signature-revision-number-increment-by-2-in-itext-after-detached-s"> * Why does my signature revision number increment by 2 (in itext) after detached signing? * </a> * <br/> * <a href="https://onedrive.live.com/redir?resid=2F03BFDA84B77A41!113&authkey=!ABPGZ7pxuxoE8A0&ithint=file%2Cpdf"> * signedoutput.pdf * </a> * <p> * The issue cannot be reproduced. In particular the PDF contains only a single revision. * </p> */ @Test public void testVerifySignedOutput() throws IOException, GeneralSecurityException { System.out.println("\n\nsignedoutput.pdf\n================"); try ( InputStream resource = getClass().getResourceAsStream("signedoutput.pdf") ) { 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()); } } System.out.println(); }
/** * <a href="http://stackoverflow.com/questions/42824577/itext-can-not-verify-signed-pdf-docs-edited-by-nitro-pro-10-11"> * itext can not verify signed pdf docs edited by nitro pro 10/11 * </a> * <br/> * <a href="https://alimail.fadada.com/signed.pdf"> * babylove_signed.pdf * </a> * <p> * Validation correctly shows verification success for a single * signature that does cover the whole document. * </p> */ @Test public void testVerifyBabyloveSigned() throws IOException, GeneralSecurityException { System.out.println("\n\nbabylove_signed.pdf\n==================="); try ( InputStream resource = getClass().getResourceAsStream("babylove_signed.pdf") ) { 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()); } } System.out.println(); }
/** * <a href="http://stackoverflow.com/questions/42824577/itext-can-not-verify-signed-pdf-docs-edited-by-nitro-pro-10-11"> * itext can not verify signed pdf docs edited by nitro pro 10/11 * </a> * <br/> * <a href="https://alimail.fadada.com/signed&modify_by_nitro.pdf"> * babylove_signed&modify_by_nitro.pdf * </a> * <p> * Validation correctly shows verification success for a single * signature that does <b>not</b> cover the whole document. * </p> */ @Test public void testVerifyBabyloveSignedAndModifyByNitro() throws IOException, GeneralSecurityException { System.out.println("\n\nbabylove_signed&modify_by_nitro.pdf\n==================="); try ( InputStream resource = getClass().getResourceAsStream("babylove_signed&modify_by_nitro.pdf") ) { 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()); } } System.out.println(); }
/** * <a href="https://stackoverflow.com/questions/45027712/invalid-signature-when-signing-an-existing-sigrature-field-with-cosign-sapi"> * Invalid signature when signing an existing sigrature field with CoSign SAPI * </a> * <br/> * <a href="https://www.dropbox.com/s/j6eme53lleaok13/test_signed.pdf?dl=0"> * test_signed-1.pdf * </a> * <p> * Validation shows verification success while both Adobe and SD DSS fail. * Embedded certificates have issues (emailAddress RDN is typed PrintableString * which is wrong - specified is IA5String - and does not even make sense as * there is no '@' in PrintableString), but does this explain it? * </p> */ @Test public void testVerifyTestSigned1() throws IOException, GeneralSecurityException { System.out.println("\n\ntest_signed-1.pdf\n==================="); try ( InputStream resource = getClass().getResourceAsStream("test_signed-1.pdf") ) { 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()); } } System.out.println(); }
/** * <a href="https://stackoverflow.com/questions/46346144/digital-signature-verification-with-itext-not-working"> * Digital Signature Verification with itext not working * </a> * <br/> * <a href="https://drive.google.com/open?id=0B1XKjvoeoyPZWnk5bzc5T3VSQUk"> * test_dsp.pdf * </a> * <p> * The issue is that the signature uses ECDSA and iText 5 does not (yet) * support ECDSA. "Support" here actually means that iText cannot find * the name ECDSA for the OID 1.2.840.10045.4.3.2 (SHA256withECDSA) to * build a proper algorithm name to use for verification. * </p> * <p> * Adding a mapping "1.2.840.10045.4.3.2" to "ECDSA" resolves the issue. * </p> * @see #testVerify20180115an_signed_original() */ @Test public void testVerifyTestDsp() throws IOException, GeneralSecurityException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Field algorithmNamesField = 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"); System.out.println("\n\ntest_dsp.pdf\n==================="); try ( InputStream resource = getClass().getResourceAsStream("test_dsp.pdf") ) { 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()); } } System.out.println(); }
/** * <a href="https://stackoverflow.com/questions/48285453/verifying-certificate-of-signed-and-secured-pdf-in-itext-pdf-java"> * Verifying certificate of signed and secured PDF in iText PDF Java * </a> * <br/> * <a href="https://drive.google.com/drive/folders/1KAqHUh-Iij0I4WXJUCx-rMd8FQFq5tCe?usp=sharing"> * pdf-sample-signed.pdf * </a> * <p> * The PDF is both signed and encrypted. Apparently iText "decrypts" the * values of the <b>Contents</b> key in signature dictionaries even though * this is an explicit exception. The parsing of this "decrypted" signature * container obviously fails. * </p> */ @Test public void testVerifyPdfSampleSigned() throws IOException, GeneralSecurityException { System.out.println("\n\npdf-sample-signed.pdf\n==================="); try ( InputStream resource = getClass().getResourceAsStream("pdf-sample-signed.pdf") ) { PdfReader reader = new PdfReader(resource, "password".getBytes()); 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()); } } System.out.println(); }
/** * <a href="https://stackoverflow.com/questions/48211757/itext-pdf-timestamp-validation-returns-false-why"> * iText pdf timestamp validation returns false, why? * </a> * <br/> * <a href="https://drive.google.com/file/d/1skI3NM9cqw2m6eW9jKXaJXKzvCjyQMib/view"> * testpdf_timestamp.pdf * </a> * <p> * The code the OP used for inspiration was for retrieving information * from a signature which may include a signature time stamp. The PDF * of the OP, on the other hand, contains a document time stamp. The * call `pkcs7.verifyTimestampImprint()` checks the time stamp as a * signature time stamp and, therefore, fails. * </p> */ @Test public void testDocumentTimestampLikeRadekKantor() throws IOException, GeneralSecurityException { try ( InputStream resource = getClass().getResourceAsStream("testpdf_timestamp.pdf") ) { PdfReader reader = new PdfReader(resource); AcroFields fields = reader.getAcroFields(); ArrayList<String> names = fields.getSignatureNames(); for (String name : names) { System.out.println("===== " + name + " ====="); System.out.println("Signature covers whole document: " + fields.signatureCoversWholeDocument(name)); System.out.println("Document revision: " + fields.getRevision(name) + " of " + fields.getTotalRevisions()); PdfPKCS7 pkcs7 = fields.verifySignature(name); System.out.println("Integrity check OK? " + pkcs7.verify()); SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS"); System.out.println("Signed on: " + date_format.format(pkcs7.getSignDate().getTime())); if (pkcs7.getTimeStampDate() != null) { System.out.println("TimeStamp: " + date_format.format(pkcs7.getTimeStampDate().getTime())); TimeStampToken ts = pkcs7.getTimeStampToken(); System.out.println("TimeStamp service: " + ts.getTimeStampInfo().getTsa()); // Why pkcs7.verifyTimestampImprint() returns FLASE? System.out.println("Timestamp verified? " + pkcs7.verifyTimestampImprint()); } } } }
/** * <a href="https://github.com/itext/itextpdf/pull/36"> * Adding Support for OID 1.2.840.113549.1.1.10 #36 * </a> * <br/> * <a href="https://github.com/itext/itextpdf/files/1641593/2018.01.15.an_signed_original.pdf"> * 2018.01.15.an_signed_original.pdf * </a> * <p> * Support for RSASSA-PSS can also be injected using reflection as done here. * </p> * @see #testVerifyTestDsp() */ @Test public void testVerify20180115an_signed_original() throws IOException, GeneralSecurityException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Field algorithmNamesField = EncryptionAlgorithms.class.getDeclaredField("algorithmNames"); algorithmNamesField.setAccessible(true); @SuppressWarnings("unchecked") HashMap<String, String> algorithmNames = (HashMap<String, String>) algorithmNamesField.get(null); algorithmNames.put("1.2.840.113549.1.1.10", "RSAandMGF1"); System.out.println("\n\n2018.01.15.an_signed_original.pdf\n==================="); try ( InputStream resource = getClass().getResourceAsStream("2018.01.15.an_signed_original.pdf") ) { 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()); } } System.out.println(); }
/** * <a href="http://stackoverflow.com/questions/35134568/itext-ltv-enabled-how-to-add-more-crls"> * iText LTV enabled - how to add more CRLs? * </a> * <p> * The original addLtv method of the OP modified merely to allow the * source PDF to be given as {@link InputStream} instead of {@link String}. * </p> */ public void addLtvJanPokorny(InputStream src, String dest) throws IOException, DocumentException, GeneralSecurityException { PdfReader r = new PdfReader(src); FileOutputStream fos = new FileOutputStream(dest); PdfStamper stp = new PdfStamper(r, fos, '\0', true); LtvVerification v = stp.getLtvVerification(); AcroFields fields = stp.getAcroFields(); ArrayList<String> names = fields.getSignatureNames(); String sigName = names.get(names.size() - 1); System.out.println("found signature: " + sigName); PdfPKCS7 pkcs7 = fields.verifySignature(sigName); //add LTV OcspClient ocsp = new OcspClientBouncyCastle(); CrlClient crlClient1 = new CrlClientOnline("http://www.postsignum.cz/crl/psrootqca2.crl"); ArrayList<CrlClient> crllist = new ArrayList<CrlClient>(); crllist.add(crlClient1); CrlClient crlClient2 = new CrlClientOnline("http://www.postsignum.cz/crl/pspublicca2.crl"); crllist.add(crlClient2); System.out.println("crllist.size=" + crllist.size()); if (pkcs7.isTsp()) { for (CrlClient crlclient : crllist) { if (v.addVerification(sigName, new OcspClientBouncyCastle(), crlclient, LtvVerification.CertificateOption.SIGNING_CERTIFICATE, LtvVerification.Level.CRL, LtvVerification.CertificateInclusion.NO)) { System.out.println("crl " + crlclient.toString() + " added to timestamp"); } } } else { for (String name : names) { for (int i = 0; i < crllist.size(); i++) { if (v.addVerification(name, ocsp, crllist.get(i), LtvVerification.CertificateOption.WHOLE_CHAIN, LtvVerification.Level.CRL, LtvVerification.CertificateInclusion.NO)) { System.out.println("crl " + crllist.get(i).toString() + " added to " + name); } if (i > 0) { System.out.println("found verification, merge"); v.merge(); } } } } stp.close(); }
/** * <a href="http://stackoverflow.com/questions/35134568/itext-ltv-enabled-how-to-add-more-crls"> * iText LTV enabled - how to add more CRLs? * </a> * <p> * The original addLtv method of the OP modified to allow the source PDF * to be given as {@link InputStream} instead of {@link String} and fixed * to properly use multiple CRLs. * </p> */ public void addLtvFixed(InputStream src, String dest) throws IOException, DocumentException, GeneralSecurityException { PdfReader r = new PdfReader(src); FileOutputStream fos = new FileOutputStream(dest); PdfStamper stp = new PdfStamper(r, fos, '\0', true); LtvVerification v = stp.getLtvVerification(); AcroFields fields = stp.getAcroFields(); ArrayList<String> names = fields.getSignatureNames(); String sigName = names.get(names.size() - 1); System.out.println("found signature: " + sigName); PdfPKCS7 pkcs7 = fields.verifySignature(sigName); //add LTV OcspClient ocsp = new OcspClientBouncyCastle(); CrlClient crlClient = new CrlClientOnline("http://www.postsignum.cz/crl/psrootqca2.crl", "http://www.postsignum.cz/crl/pspublicca2.crl"); if (pkcs7.isTsp()) { if (v.addVerification(sigName, new OcspClientBouncyCastle(), crlClient, LtvVerification.CertificateOption.SIGNING_CERTIFICATE, LtvVerification.Level.CRL, LtvVerification.CertificateInclusion.NO)) { System.out.println("crl " + crlClient.toString() + " added to timestamp"); } } else { for (String name : names) { if (v.addVerification(name, ocsp, crlClient, LtvVerification.CertificateOption.WHOLE_CHAIN, LtvVerification.Level.CRL, LtvVerification.CertificateInclusion.NO)) { System.out.println("crl " + crlClient.toString() + " added to " + name); } } } stp.close(); }
boolean verify(PdfReader reader, PdfDictionary value) throws GeneralSecurityException { PdfArray byteRange = value.getAsArray(PdfName.BYTERANGE); if (byteRange == null || byteRange.isEmpty()) { System.out.printf(" Signed range: missing\n"); } else { StringBuilder builder = new StringBuilder(); builder.append(" Signed range:"); for (PdfObject arrObj: byteRange) { builder.append(' ').append(arrObj); } int byteRangeSize = byteRange.size(); if (byteRangeSize % 2 == 1) { builder.append(" (Invalid: odd number of entries)"); } else { StringBuilder interoperability = new StringBuilder(); if (byteRangeSize != 4) { interoperability.append(", not exactly one gap"); } int rangeStart = byteRange.getAsNumber(0).intValue(); if (rangeStart != 0) { interoperability.append(", first range does not start at 0"); } for (int i = 2; i < byteRangeSize; i+=2) { int lastRangeEnd = rangeStart + byteRange.getAsNumber(i-1).intValue(); rangeStart = byteRange.getAsNumber(i).intValue(); if (lastRangeEnd > rangeStart) { interoperability.append(", unordered or overlapping ranges"); break; } } if (interoperability.length() > 0) { builder.append(" (Interoperability issues").append(interoperability).append(')'); } int finalRangeEnd = byteRange.getAsNumber(byteRangeSize-2).intValue() + byteRange.getAsNumber(byteRangeSize-1).intValue(); if (finalRangeEnd == reader.getFileLength()) { builder.append(" (covers whole file)"); } else { builder.append(" (covers partial file up to ").append(finalRangeEnd).append(")"); } } System.out.println(builder); } PdfPKCS7 pkcs7 = verifySignature(reader, value, null); System.out.printf(" Validity: %s\n", pkcs7.verify()); return pkcs7 != null; }
public DigestInfo preSign(List<DigestInfo> arg0, List<X509Certificate> certificates) throws NoSuchAlgorithmException { System.out.println("SignatureServiceImpl::preSign"); HttpSession session = getSession(); SignatureRequest request = (SignatureRequest)session.getAttribute(BeidConstants.SIGNATUREREQUEST_SESSION_NAME); ContentStream content = request.getDocument().getContentStream(); try { Certificate[] chain = new Certificate[certificates.size()]; int index = 0; for (X509Certificate cert: certificates) { //System.out.println("CERT: "+cert); chain[index++] = cert; } // we create a reader and a stamper PdfReader reader = new PdfReader(content.getStream()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0'); // we create the signature appearance PdfSignatureAppearance sap = stamper.getSignatureAppearance(); request.fillAppearance(sap, reader); sap.setCertificate(chain[0]); // we create the signature infrastructure PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED); dic.setReason(sap.getReason()); dic.setLocation(sap.getLocation()); dic.setContact(sap.getContact()); dic.setDate(new PdfDate(sap.getSignDate())); sap.setCryptoDictionary(dic); HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>(); exc.put(PdfName.CONTENTS, new Integer(8192 * 2 + 2)); sap.preClose(exc); ExternalDigest externalDigest = new ExternalDigest() { public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException { return DigestAlgorithms.getMessageDigest(hashAlgorithm, null); } }; PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false); InputStream data = sap.getRangeStream(); byte hash[] = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256")); Calendar cal = Calendar.getInstance(); byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS); sh = MessageDigest.getInstance("SHA256", "BC").digest(sh); // We store the objects we'll need for post signing in a session session.setAttribute(BeidConstants.SIGNATURE_SESSION_NAME, sgn); session.setAttribute(BeidConstants.HASH_SESSION_NAME, hash); session.setAttribute(BeidConstants.CAL_SESSION_NAME, cal); session.setAttribute(BeidConstants.SAP_SESSION_NAME, sap); session.setAttribute(BeidConstants.BAOS_SESSION_NAME, baos); DigestInfo info = new DigestInfo(sh, "SHA-256", "BeidSign"); return info; } catch(Exception e) { e.printStackTrace(); } return null; }