use of org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream 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]);
}
}
use of org.apache.pdfbox.pdmodel.interactive.digitalsignature.COSFilterInputStream in project pdfbox by apache.
the class COSWriter method getDataToSign.
/**
* Return the stream of PDF data to be signed. Clients should use this method only to create
* signatures externally. {@link #write(PDDocument)} method should have been called prior. The
* created signature should be set using {@link #writeExternalSignature(byte[])}.
* <p>
* When {@link SignatureInterface} instance is used, COSWriter obtains and writes the signature
* itself.
* </p>
*
* @return data stream to be signed
* @throws IllegalStateException if PDF is not prepared for external signing
* @throws IOException if input data is closed
*/
public InputStream getDataToSign() throws IOException {
if (incrementPart == null || incrementalInput == null) {
throw new IllegalStateException("PDF not prepared for signing");
}
// range of incremental bytes to be signed (includes /ByteRange but not /Contents)
int incPartSigOffset = (int) (signatureOffset - incrementalInput.length());
int afterSigOffset = incPartSigOffset + (int) signatureLength;
int[] range = { 0, incPartSigOffset, afterSigOffset, incrementPart.length - afterSigOffset };
return new SequenceInputStream(new RandomAccessInputStream(incrementalInput), new COSFilterInputStream(incrementPart, range));
}
Aggregations