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);
}
}
}
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]);
}
}
Aggregations