使用 api 提供的签名哈希对 PDF 进行签名
Sign PDF with signature hash provided by api
我正在尝试使用 PDFBox
和第三方签名提供商签署 pdf。
我的程序:
- 从用户输入中获取 pdf
- 使用给定 pdf 的内容创建 PDDocument
- 创建具有所有给定属性(例如组织或位置)但没有签名哈希本身的 PDSignature。
- 将此签名对象添加到 PDDocument 并从 PDDocument 创建摘要以发送到 API。
- 等待来自 API 的成功回答(包含签名哈希)并将此哈希插入到 PDDocuments PDSignature 对象中而不改变整个文档。
我的问题:
我可以创建 PDDocument、PDSignature 及其摘要并将其发送到 API。然后我从 API 得到正确和有效的签名散列,我可以将它添加到 PDDocument 但只要这有效,生成的 pdf 由于“被改变或操纵”(签名者,时间戳和证书有效)。我也尝试使用 PDFBox
中的 ExternalSigningSupport
但我无法使用它,因为我总是 运行 出现错误:
"java.lang.IllegalStateException: signature reserve byte range has been changed after addSignature(), please set the byte range that existed after addSignature()".
我的代码有效但使签名无效:
//Pending Signature is just a DTO
public static PendingSignature createPendingSignatureFromPdf(InputStream pdf2sign, OutputStream fos, String sigName, String sigLocation, String sigReason, String contactInfo, Date forcedDate, Long revisionId) throws IOException {
File pdfFileToSigned = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
File pdfPreparedToBeSigned = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
File pdfHashPreparedToBeSigned = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
try {
if (fos == null) {
fos = new FileOutputStream(pdfPreparedToBeSigned);
}
FileUtils.copyInputStreamToFile(pdf2sign, pdfFileToSigned);
PDDocument doc = PDDocument.load(pdfFileToSigned);
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
if (contactInfo != null && !contactInfo.isEmpty()) {
signature.setContactInfo(contactInfo);
}
if (sigLocation != null && !sigLocation.isEmpty()) {
signature.setLocation(sigLocation);
}
if (sigReason != null && !sigReason.isEmpty()) {
signature.setReason(sigReason);
}
if (sigName != null && !sigName.isEmpty()) {
signature.setName(sigName);
}
if (forcedDate != null) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Calendar cal = Calendar.getInstance();
cal.setTime(sdf.parse(sdf.format(forcedDate)));
signature.setSignDate(cal);
}
final OutputStream outputStream = new FileOutputStream(pdfHashPreparedToBeSigned);
SignatureInterface signatureInterface = new SignatureInterface() {
@Override
public byte[] sign(InputStream content) throws IOException {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] imp = digest.digest(content.toString().getBytes(StandardCharsets.UTF_16));
IOUtils.copy(new ByteArrayInputStream(imp), outputStream);
return new byte[0];
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return new byte[0];
}
}
};
SignatureOptions signatureOptions = new SignatureOptions();
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 8);
doc.addSignature(signature, signatureInterface, signatureOptions);
if (revisionId != null) {
doc.setDocumentId(revisionId);
} else {
doc.setDocumentId(0L);
}
doc.saveIncremental(fos);
PendingSignature pendingSignature = new PendingSignature(doc, signature, IOUtils.toByteArray(new FileInputStream(pdfHashPreparedToBeSigned)));
return pendingSignature;
} catch (IOException | ParseException e) {
throw new IOException(e);
} finally {
fos.close();
pdf2sign.close();
FileUtils.deleteQuietly(pdfFileToSigned);
FileUtils.deleteQuietly(pdfPreparedToBeSigned);
FileUtils.deleteQuietly(pdfHashPreparedToBeSigned);
}
}
//Gets called when the api returns sucess with the signedbytes
public static File insertHashToPdf(PendingSignature pendingSignature, final byte[] signedBytes) throws IOException {
File outputDocument = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
FileOutputStream fos = new FileOutputStream(outputDocument);
PDDocument doc = pendingSignature.getPdfDocument();
PDSignature pdfSignature = pendingSignature.getPdfSignature();
//Produces signature that it invalid because it was altered or manipulated
pdfSignature.setContents(signedBytes);
doc.saveIncremental(fos);
if (fos != null) {
fos.close();
}
return outputDocument;
}
签名看起来像这样:
有人知道如何将此签名哈希插入到整个 PDDocument 中而不使其失效吗?
所以我终于找到了可行的解决方案。
Link 到日储蓄回购:https://github.com/crs2195/signature/tree/master/RemoteSignature-%20PDFBox/CSC-PDFBox
我正在尝试使用 PDFBox
和第三方签名提供商签署 pdf。
我的程序:
- 从用户输入中获取 pdf
- 使用给定 pdf 的内容创建 PDDocument
- 创建具有所有给定属性(例如组织或位置)但没有签名哈希本身的 PDSignature。
- 将此签名对象添加到 PDDocument 并从 PDDocument 创建摘要以发送到 API。
- 等待来自 API 的成功回答(包含签名哈希)并将此哈希插入到 PDDocuments PDSignature 对象中而不改变整个文档。
我的问题:
我可以创建 PDDocument、PDSignature 及其摘要并将其发送到 API。然后我从 API 得到正确和有效的签名散列,我可以将它添加到 PDDocument 但只要这有效,生成的 pdf 由于“被改变或操纵”(签名者,时间戳和证书有效)。我也尝试使用 PDFBox
中的 ExternalSigningSupport
但我无法使用它,因为我总是 运行 出现错误:
"java.lang.IllegalStateException: signature reserve byte range has been changed after addSignature(), please set the byte range that existed after addSignature()".
我的代码有效但使签名无效:
//Pending Signature is just a DTO
public static PendingSignature createPendingSignatureFromPdf(InputStream pdf2sign, OutputStream fos, String sigName, String sigLocation, String sigReason, String contactInfo, Date forcedDate, Long revisionId) throws IOException {
File pdfFileToSigned = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
File pdfPreparedToBeSigned = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
File pdfHashPreparedToBeSigned = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
try {
if (fos == null) {
fos = new FileOutputStream(pdfPreparedToBeSigned);
}
FileUtils.copyInputStreamToFile(pdf2sign, pdfFileToSigned);
PDDocument doc = PDDocument.load(pdfFileToSigned);
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
if (contactInfo != null && !contactInfo.isEmpty()) {
signature.setContactInfo(contactInfo);
}
if (sigLocation != null && !sigLocation.isEmpty()) {
signature.setLocation(sigLocation);
}
if (sigReason != null && !sigReason.isEmpty()) {
signature.setReason(sigReason);
}
if (sigName != null && !sigName.isEmpty()) {
signature.setName(sigName);
}
if (forcedDate != null) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Calendar cal = Calendar.getInstance();
cal.setTime(sdf.parse(sdf.format(forcedDate)));
signature.setSignDate(cal);
}
final OutputStream outputStream = new FileOutputStream(pdfHashPreparedToBeSigned);
SignatureInterface signatureInterface = new SignatureInterface() {
@Override
public byte[] sign(InputStream content) throws IOException {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] imp = digest.digest(content.toString().getBytes(StandardCharsets.UTF_16));
IOUtils.copy(new ByteArrayInputStream(imp), outputStream);
return new byte[0];
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return new byte[0];
}
}
};
SignatureOptions signatureOptions = new SignatureOptions();
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 8);
doc.addSignature(signature, signatureInterface, signatureOptions);
if (revisionId != null) {
doc.setDocumentId(revisionId);
} else {
doc.setDocumentId(0L);
}
doc.saveIncremental(fos);
PendingSignature pendingSignature = new PendingSignature(doc, signature, IOUtils.toByteArray(new FileInputStream(pdfHashPreparedToBeSigned)));
return pendingSignature;
} catch (IOException | ParseException e) {
throw new IOException(e);
} finally {
fos.close();
pdf2sign.close();
FileUtils.deleteQuietly(pdfFileToSigned);
FileUtils.deleteQuietly(pdfPreparedToBeSigned);
FileUtils.deleteQuietly(pdfHashPreparedToBeSigned);
}
}
//Gets called when the api returns sucess with the signedbytes
public static File insertHashToPdf(PendingSignature pendingSignature, final byte[] signedBytes) throws IOException {
File outputDocument = createTempFile("chars", "" + Calendar.getInstance().getTimeInMillis());
FileOutputStream fos = new FileOutputStream(outputDocument);
PDDocument doc = pendingSignature.getPdfDocument();
PDSignature pdfSignature = pendingSignature.getPdfSignature();
//Produces signature that it invalid because it was altered or manipulated
pdfSignature.setContents(signedBytes);
doc.saveIncremental(fos);
if (fos != null) {
fos.close();
}
return outputDocument;
}
签名看起来像这样:
有人知道如何将此签名哈希插入到整个 PDDocument 中而不使其失效吗?
所以我终于找到了可行的解决方案。
Link 到日储蓄回购:https://github.com/crs2195/signature/tree/master/RemoteSignature-%20PDFBox/CSC-PDFBox