Search in sources :

Example 1 with RandomAccessReadBufferedFile

use of org.apache.pdfbox.io.RandomAccessReadBufferedFile in project pdfbox by apache.

the class ShowSignature method checkContentValueWithFile.

private void checkContentValueWithFile(File file, int[] byteRange, byte[] contents) throws IOException {
    // to allow extra space
    try (RandomAccessReadBufferedFile raf = new RandomAccessReadBufferedFile(file)) {
        raf.seek(byteRange[1]);
        int c = raf.read();
        if (c != '<') {
            System.err.println("'<' expected at offset " + byteRange[1] + ", but got " + (char) c);
        }
        byte[] contentFromFile = new byte[byteRange[2] - byteRange[1] - 2];
        int contentLength = contentFromFile.length;
        int contentBytesRead = raf.read(contentFromFile);
        while (contentBytesRead > -1 && contentBytesRead < contentLength) {
            contentBytesRead += raf.read(contentFromFile, contentBytesRead, contentLength - contentBytesRead);
        }
        byte[] contentAsHex = Hex.getString(contents).getBytes(StandardCharsets.US_ASCII);
        if (contentBytesRead != contentAsHex.length) {
            System.err.println("Raw content length from file is " + contentBytesRead + ", but internal content string in hex has length " + contentAsHex.length);
        }
        // also check that it is really hex
        for (int i = 0; i < contentBytesRead; ++i) {
            try {
                if (Integer.parseInt(String.valueOf((char) contentFromFile[i]), 16) != Integer.parseInt(String.valueOf((char) contentAsHex[i]), 16)) {
                    System.err.println("Possible manipulation at file offset " + (byteRange[1] + i + 1) + " in signature content");
                    break;
                }
            } catch (NumberFormatException ex) {
                System.err.println("Incorrect hex value");
                System.err.println("Possible manipulation at file offset " + (byteRange[1] + i + 1) + " in signature content");
                break;
            }
        }
        c = raf.read();
        if (c != '>') {
            System.err.println("'>' expected at offset " + byteRange[2] + ", but got " + (char) c);
        }
    }
}
Also used : RandomAccessReadBufferedFile(org.apache.pdfbox.io.RandomAccessReadBufferedFile)

Example 2 with RandomAccessReadBufferedFile

use of org.apache.pdfbox.io.RandomAccessReadBufferedFile in project pdfbox by apache.

the class ShowSignature method showSignature.

private void showSignature(String[] args) throws IOException, GeneralSecurityException, TSPException, CertificateVerificationException {
    if (args.length != 2) {
        usage();
    } else {
        String password = args[0];
        File infile = new File(args[1]);
        // use old-style document loading to disable leniency
        // see also https://www.pdf-insecurity.org/
        RandomAccessReadBufferedFile raFile = new RandomAccessReadBufferedFile(infile);
        // If your files are not too large, you can also download the PDF into a byte array
        // with IOUtils.toByteArray() and pass a RandomAccessBuffer() object to the
        // PDFParser constructor.
        PDFParser parser = new PDFParser(raFile, password);
        try (PDDocument document = parser.parse(false)) {
            for (PDSignature sig : document.getSignatureDictionaries()) {
                COSDictionary sigDict = sig.getCOSObject();
                byte[] contents = sig.getContents();
                // we're doing this as a stream, to be able to handle huge files
                try (FileInputStream fis = new FileInputStream(infile);
                    InputStream signedContentAsStream = new COSFilterInputStream(fis, sig.getByteRange())) {
                    System.out.println("Signature found");
                    if (sig.getName() != null) {
                        System.out.println("Name:     " + sig.getName());
                    }
                    if (sig.getSignDate() != null) {
                        System.out.println("Modified: " + sdf.format(sig.getSignDate().getTime()));
                    }
                    String subFilter = sig.getSubFilter();
                    if (subFilter != null) {
                        switch(subFilter) {
                            case "adbe.pkcs7.detached":
                            case "ETSI.CAdES.detached":
                                verifyPKCS7(signedContentAsStream, contents, sig);
                                break;
                            case "adbe.pkcs7.sha1":
                                {
                                    // example: PDFBOX-1452.pdf
                                    CertificateFactory factory = CertificateFactory.getInstance("X.509");
                                    ByteArrayInputStream certStream = new ByteArrayInputStream(contents);
                                    Collection<? extends Certificate> certs = factory.generateCertificates(certStream);
                                    System.out.println("certs=" + certs);
                                    @SuppressWarnings({ "squid:S5542", "lgtm [java/weak-cryptographic-algorithm]" }) MessageDigest md = MessageDigest.getInstance("SHA1");
                                    try (DigestInputStream dis = new DigestInputStream(signedContentAsStream, md)) {
                                        while (dis.read() != -1) {
                                        // do nothing
                                        }
                                    }
                                    byte[] hash = md.digest();
                                    verifyPKCS7(new ByteArrayInputStream(hash), contents, sig);
                                    break;
                                }
                            case "adbe.x509.rsa_sha1":
                                {
                                    // example: PDFBOX-2693.pdf
                                    COSString certString = (COSString) sigDict.getDictionaryObject(COSName.CERT);
                                    // TODO this could also be an array.
                                    if (certString == null) {
                                        System.err.println("The /Cert certificate string is missing in the signature dictionary");
                                        return;
                                    }
                                    byte[] certData = certString.getBytes();
                                    CertificateFactory factory = CertificateFactory.getInstance("X.509");
                                    ByteArrayInputStream certStream = new ByteArrayInputStream(certData);
                                    Collection<? extends Certificate> certs = factory.generateCertificates(certStream);
                                    System.out.println("certs=" + certs);
                                    X509Certificate cert = (X509Certificate) certs.iterator().next();
                                    try {
                                        if (sig.getSignDate() != null) {
                                            cert.checkValidity(sig.getSignDate().getTime());
                                            System.out.println("Certificate valid at signing time");
                                        } else {
                                            System.err.println("Certificate cannot be verified without signing time");
                                        }
                                    } catch (CertificateExpiredException ex) {
                                        System.err.println("Certificate expired at signing time");
                                    } catch (CertificateNotYetValidException ex) {
                                        System.err.println("Certificate not yet valid at signing time");
                                    }
                                    if (CertificateVerifier.isSelfSigned(cert)) {
                                        System.err.println("Certificate is self-signed, LOL!");
                                    } else {
                                        System.out.println("Certificate is not self-signed");
                                        if (sig.getSignDate() != null) {
                                            @SuppressWarnings("unchecked") Store<X509CertificateHolder> store = new JcaCertStore(certs);
                                            SigUtils.verifyCertificateChain(store, cert, sig.getSignDate().getTime());
                                        }
                                    }
                                    break;
                                }
                            case "ETSI.RFC3161":
                                // e.g. PDFBOX-1848, file_timestamped.pdf
                                verifyETSIdotRFC3161(signedContentAsStream, contents);
                                // verifyPKCS7(hash, contents, sig) does not work
                                break;
                            default:
                                System.err.println("Unknown certificate type: " + subFilter);
                                break;
                        }
                    } else {
                        throw new IOException("Missing subfilter for cert dictionary");
                    }
                    int[] byteRange = sig.getByteRange();
                    if (byteRange.length != 4) {
                        System.err.println("Signature byteRange must have 4 items");
                    } else {
                        long fileLen = infile.length();
                        long rangeMax = byteRange[2] + (long) byteRange[3];
                        // multiply content length with 2 (because it is in hex in the PDF) and add 2 for < and >
                        int contentLen = contents.length * 2 + 2;
                        if (fileLen != rangeMax || byteRange[0] != 0 || byteRange[1] + contentLen != byteRange[2]) {
                            // a false result doesn't necessarily mean that the PDF is a fake
                            // see this answer why:
                            // https://stackoverflow.com/a/48185913/535646
                            System.out.println("Signature does not cover whole document");
                        } else {
                            System.out.println("Signature covers whole document");
                        }
                        checkContentValueWithFile(infile, byteRange, contents);
                    }
                }
            }
            analyseDSS(document);
        } catch (CMSException | OperatorCreationException ex) {
            throw new IOException(ex);
        }
        System.out.println("Analyzed: " + args[1]);
    }
}
Also used : CertificateNotYetValidException(java.security.cert.CertificateNotYetValidException) CertificateExpiredException(java.security.cert.CertificateExpiredException) CollectionStore(org.bouncycastle.util.CollectionStore) KeyStore(java.security.KeyStore) Store(org.bouncycastle.util.Store) JcaCertStore(org.bouncycastle.cert.jcajce.JcaCertStore) JcaCertStore(org.bouncycastle.cert.jcajce.JcaCertStore) COSString(org.apache.pdfbox.cos.COSString) PDSignature(org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature) CertificateFactory(java.security.cert.CertificateFactory) RandomAccessReadBufferedFile(org.apache.pdfbox.io.RandomAccessReadBufferedFile) MessageDigest(java.security.MessageDigest) OperatorCreationException(org.bouncycastle.operator.OperatorCreationException) COSString(org.apache.pdfbox.cos.COSString) COSDictionary(org.apache.pdfbox.cos.COSDictionary) DigestInputStream(java.security.DigestInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) COSFilterInputStream(org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream) FileInputStream(java.io.FileInputStream) DigestInputStream(java.security.DigestInputStream) InputStream(java.io.InputStream) PDFParser(org.apache.pdfbox.pdfparser.PDFParser) COSFilterInputStream(org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) X509Certificate(java.security.cert.X509Certificate) ByteArrayInputStream(java.io.ByteArrayInputStream) PDDocument(org.apache.pdfbox.pdmodel.PDDocument) Collection(java.util.Collection) RandomAccessReadBufferedFile(org.apache.pdfbox.io.RandomAccessReadBufferedFile) File(java.io.File) X509Certificate(java.security.cert.X509Certificate) Certificate(java.security.cert.Certificate) CMSException(org.bouncycastle.cms.CMSException)

Aggregations

RandomAccessReadBufferedFile (org.apache.pdfbox.io.RandomAccessReadBufferedFile)2 ByteArrayInputStream (java.io.ByteArrayInputStream)1 File (java.io.File)1 FileInputStream (java.io.FileInputStream)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 DigestInputStream (java.security.DigestInputStream)1 KeyStore (java.security.KeyStore)1 MessageDigest (java.security.MessageDigest)1 Certificate (java.security.cert.Certificate)1 CertificateExpiredException (java.security.cert.CertificateExpiredException)1 CertificateFactory (java.security.cert.CertificateFactory)1 CertificateNotYetValidException (java.security.cert.CertificateNotYetValidException)1 X509Certificate (java.security.cert.X509Certificate)1 Collection (java.util.Collection)1 COSDictionary (org.apache.pdfbox.cos.COSDictionary)1 COSString (org.apache.pdfbox.cos.COSString)1 PDFParser (org.apache.pdfbox.pdfparser.PDFParser)1 PDDocument (org.apache.pdfbox.pdmodel.PDDocument)1 COSFilterInputStream (org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream)1