【发布时间】:2015-05-30 18:27:20
【问题描述】:
我创建了将图像添加到现有 pdf 文档然后对其进行签名的代码,全部使用 PDFBox(请参见下面的代码)。
代码很好地添加了图像和签名。但是,在某些文档中,Acrobat Reader 会抱怨“签名字节范围无效。”
问题似乎与this问题中描述的问题相同。该问题的答案更详细地描述了该问题:问题是我的代码在文档中留下了交叉引用类型(流和表)的混合。实际上,由于这会产生问题,某些文档甚至无法打开。
我的问题是:如何防止这种情况发生?如何在不创建多个交叉引用类型的情况下将图像添加到现有 pdf 文档?
public class TC3 implements SignatureInterface{
private char[] pin = "123456".toCharArray();
private BouncyCastleProvider provider = new BouncyCastleProvider();
private PrivateKey privKey;
private Certificate[] cert;
public TC3() throws Exception{
Security.addProvider(provider);
KeyStore keystore = KeyStore.getInstance("PKCS12", provider);
keystore.load(new FileInputStream(new File("resources/IIS_keystore.pfx")), pin.clone());
String alias = keystore.aliases().nextElement();
privKey = (PrivateKey) keystore.getKey(alias, pin);
cert = keystore.getCertificateChain(alias);
}
public void doSign() throws Exception{
byte inputBytes[] = IOUtils.toByteArray(new FileInputStream("resources/rooster.pdf"));
PDDocument pdDocument = PDDocument.load(new ByteArrayInputStream(inputBytes));
PDJpeg ximage = new PDJpeg(pdDocument, ImageIO.read(new File("resources/logo.jpg")));
PDPage page = (PDPage)pdDocument.getDocumentCatalog().getAllPages().get(0);
PDPageContentStream contentStream = new PDPageContentStream(pdDocument, page, true, true);
contentStream.drawXObject(ximage, 50, 50, 356, 40);
contentStream.close();
ByteArrayOutputStream os = new ByteArrayOutputStream();
pdDocument.save(os);
os.flush();
pdDocument.close();
inputBytes = os.toByteArray();
pdDocument = PDDocument.load(new ByteArrayInputStream(inputBytes));
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("signer name");
signature.setLocation("signer location");
signature.setReason("reason for signature");
signature.setSignDate(Calendar.getInstance());
pdDocument.addSignature(signature, this);
File outputDocument = new File("resources/signed.pdf");
ByteArrayInputStream fis = new ByteArrayInputStream(inputBytes);
FileOutputStream fos = new FileOutputStream(outputDocument);
byte[] buffer = new byte[8 * 1024];
int c;
while ((c = fis.read(buffer)) != -1)
{
fos.write(buffer, 0, c);
}
fis.close();
FileInputStream is = new FileInputStream(outputDocument);
pdDocument.saveIncremental(is, fos);
pdDocument.close();
}
public byte[] sign(InputStream content) {
CMSProcessableInputStream input = new CMSProcessableInputStream(content);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
List<Certificate> certList = Arrays.asList(cert);
CertStore certStore = null;
try{
certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), provider);
gen.addSigner(privKey, (X509Certificate) certList.get(0), CMSSignedGenerator.DIGEST_SHA256);
gen.addCertificatesAndCRLs(certStore);
CMSSignedData signedData = gen.generate(input, false, provider);
return signedData.getEncoded();
}catch (Exception e){}
return null;
}
public static void main(String[] args) throws Exception {
new TC3().doSign();
}
【问题讨论】:
-
当您开始操作原始 PDF 并再次保存它(不是作为增量更新)时,应该可以以与 pdfbox 签名增量更新兼容的方式进行保存。明天我会试着调查一下。
-
哇,这很奇怪。我刚刚做了一些测试,结果是对于带有外部参照流的 PDF,PDFBox 使用外部参照表保存第一部分(添加了图形的部分),并使用外部参照流附加签名修订。我会再调查一下。
-
非常感谢您的帮助。我不是很懂PDF。您是否使用任何特定工具来检查 PDF,还是仅使用十六进制编辑器?
-
为了识别交叉引用的类型,我实际上只是使用了一个文本查看器。
标签: java digital-signature pdfbox attachment