签名后外部签名导致 pdf 损坏
external signature results in corrupted pdf after signing
我正在尝试从第 3 方签名提供商处签署 pdf 文档。
在创建空签名后,我向他们发送了文档哈希,他们发送了一个时间戳令牌(我们同意这将是一个时间戳签名),然后我将签名添加回 pdf。 api 获取 timestmp、crl 和 ocsp 的调用进行得很顺利,但是一旦我生成带有签名的 pdf,adobe 就会说签名无效并且错误是:
签名验证时出错。
签名包含不正确、无法识别、损坏或可疑的数据。
支持信息:SigDict /Contents 非法数据
这是当前代码:
signatureprovider.java
public ByteArrayOutputStream sign(byte[] src, Rectangle rect, int signPage) throws Exception {
log.info("I am in the signing method");
ByteArrayOutputStream dest = new ByteArrayOutputStream();
PdfDocumentHandler handler = new PdfDocumentHandler(new ByteArrayInputStream(src), dest);
handler.prepareForSigning(rect, signPage);
TrustedTimestampService service = new TrustedTimestampService();
SignResponseWrapper response = new SignResponseWrapper();
response = service.signHash(handler.getEncodedDocumentHash());
String timestampToken = response.getSignResponse().getSignatureObject().getOther().getScSignatureObjects().getScExtendedSignatureObject().getTimestamp().getRFC3161TimeStampToken();
List<String> encodedCrlEntries = new ArrayList<String>();
encodedCrlEntries.add(response.getSignResponse().getOptionalOutputs().getScRevocationInformation().getScCRLs().getScCRL());
List<String> encodedOcspEntries = new ArrayList<String>();
encodedOcspEntries.add(response.getSignResponse().getOptionalOutputs().getScRevocationInformation().getScOCSPs().getScOCSP());
byte[] signed = handler.createSignedPdf(Base64.getDecoder().decode(timestampToken), 15000, encodedCrlEntries, encodedOcspEntries);
dest.flush();
dest.write(signed);
handler.close();
return dest;
}
我会避免添加该服务,它只是发送哈希并获取令牌
pdfdocumenthandler.java
public PdfDocumentHandler(InputStream inputStream, ByteArrayOutputStream outputStream) {
this.inputStream = 输入流;
this.outputStream = 输出流;
}
public void prepareForSigning(Rectangle rect, int signPage)
throws IOException, GeneralSecurityException {
inMemoryStream = new ByteArrayOutputStream();
inMemoryStream.write(StreamUtil.inputStreamToArray(inputStream));
boolean hasSignature = hasDocumentSignature();
pdfReader = new PdfReader(new ByteArrayInputStream(inMemoryStream.toByteArray()), new ReaderProperties());
inMemoryStream.reset();
pdfWriter = new PdfWriter(inMemoryStream, new WriterProperties().addXmpMetadata().setPdfVersion(PdfVersion.PDF_1_0));
StampingProperties stampingProperties = new StampingProperties();
pdfSigner = new PdfDocumentSigner(pdfReader, inMemoryStream, hasSignature ? stampingProperties.useAppendMode() : stampingProperties);
pdfSigner.getSignatureAppearance()
.setReason("myreason")
.setLocation("mylocation")
.setContact("mycontact")
.setPageRect(rect)
.setPageNumber(signPage)
.setSignatureCreator("creator");
pdfSigner.setFieldName("digitalSig");
try {
imgStorage = new ImageStorage();
ImageData imgData = ImageDataFactory.create(Base64.getDecoder().decode(imgStorage.getimgData().getBytes()));
pdfSigner.getSignatureAppearance().setRenderingMode(RenderingMode.GRAPHIC);
pdfSigner.getSignatureAppearance().setSignatureGraphic(imgData);
} catch(Exception e){
pdfSigner.getSignatureAppearance().setRenderingMode(RenderingMode.DESCRIPTION);
}
Map<PdfName, PdfObject> signatureDictionary = new HashMap<>();
signatureDictionary.put(PdfName.Filter, PdfName.Adobe_PPKLite);
signatureDictionary.put(PdfName.SubFilter, PdfName.ETSI_RFC3161);
Calendar signDate = Calendar.getInstance();
pdfSigner.setSignDate(signDate);
PdfHashSignatureContainer hashSignatureContainer = new PdfHashSignatureContainer("SHA-512", new PdfDictionary(signatureDictionary));
documentHash = pdfSigner.computeHash(hashSignatureContainer, 15_000);
log.info("prepared document - outputstream: " + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
log.info("prepared document - inmemorystream: " + Base64.getEncoder().encodeToString(inMemoryStream.toByteArray()));
}
public byte[] createSignedPdf(byte[] externalSignature, int estimatedSize, List<String> encodedCrlEntries,
List<String> encodedOcspEntries) {
if (pdfSigner.getCertificationLevel() == PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED) {
throw new RuntimeException(String.format("Could not apply signature because source file contains a certification that does not allow "
+ "any changes to the document with id %s"));
}
if (estimatedSize < externalSignature.length) {
throw new RuntimeException(String.format("Not enough space for signature in the document. The estimated size needs to be " +
" %d bytes.", externalSignature.length));
}
try {
pdfSigner.signWithAuthorizedSignature(new PdfSignatureContainer(externalSignature), estimatedSize);
if (null != encodedCrlEntries || null!=encodedOcspEntries) {
extendDocumentWithCrlOcspMetadata(encodedCrlEntries, encodedOcspEntries);
} else {
log.info("No CRL and OCSP entries were received to be embedded into the PDF");
outputStream.write(inMemoryStream.toByteArray());
}
//outputStream.write(inMemoryStream.toByteArray());
closeResource(inMemoryStream);
closeResource(outputStream);
log.info("complete document - outputstream: " + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
log.info("complete document - inmemorystream: " + Base64.getEncoder().encodeToString(inMemoryStream.toByteArray()));
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(String.format("Failed to embed the signature in the document"), e);
}
return outputStream.toByteArray();
}
private void extendDocumentWithCrlOcspMetadata(List<String> encodedCrlEntries, List<String> encodedOcspEntries) {
if (pdfSigner.getCertificationLevel() == PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED) {
throw new RuntimeException(String.format("Could not apply revocation information (LTV) to the DSS Dictionary. Document contains a " +
"certification that does not allow any changes"));
}
log.info("document without crl and ocsp - outputstream: " + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
log.info("document without crl and ocsp - inmemorystream: " + Base64.getEncoder().encodeToString(inMemoryStream.toByteArray()));
List<byte[]> crl = mapEncodedEntries(encodedCrlEntries, this::mapEncodedCrl);
List<byte[]> ocsp = mapEncodedEntries(encodedOcspEntries, this::mapEncodedOcsp);
try (InputStream documentStream = new ByteArrayInputStream(inMemoryStream.toByteArray());
PdfReader reader = new PdfReader(documentStream);
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(reader, writer, new StampingProperties().preserveEncryption().useAppendMode())) {
LtvVerification validation = new LtvVerification(pdfDocument);
List<String> signatureNames = new SignatureUtil(pdfDocument).getSignatureNames();
String signatureName = signatureNames.get(signatureNames.size() - 1);
boolean isSignatureVerificationAdded = validation.addVerification(signatureName, ocsp, crl, null);
validation.merge();
logSignatureVerificationInfo(isSignatureVerificationAdded);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to embed the signature(s) in the document(s) and close the streams"));
}
}
public String getId() {
return id;
}
public String getEncodedDocumentHash() {
return Base64.getEncoder().encodeToString(documentHash);
}
private void logSignatureVerificationInfo(boolean isSignatureVerificationAdded) {
if (isSignatureVerificationAdded) {
log.info("Merged LTV validation information to the output stream");
} else {
log.warn("Failed to merge LTV validation information to the output stream");
}
}
private List<byte[]> mapEncodedEntries(List<String> encodedEntries, Function<String, byte[]> mapperFunction) {
return Objects.nonNull(encodedEntries)
? encodedEntries.stream().map(mapperFunction).collect(Collectors.toList())
: Collections.emptyList();
}
private byte[] mapEncodedCrl(String encodedCrl) {
try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(encodedCrl))) {
X509CRL x509crl = (X509CRL) CertificateFactory.getInstance("X.509").generateCRL(inputStream);
logCrlInfo(x509crl);
return x509crl.getEncoded();
} catch (IOException | CertificateException | CRLException e) {
throw new RuntimeException(String.format("Failed to map the received encoded CRL entry"), e);
}
}
private byte[] mapEncodedOcsp(String encodedOcsp) {
try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(encodedOcsp))) {
OCSPResp ocspResp = new OCSPResp(inputStream);
BasicOCSPResp basicResp = (BasicOCSPResp) ocspResp.getResponseObject();
logOcspInfo(ocspResp, basicResp);
return basicResp.getEncoded();
} catch (IOException | OCSPException e) {
throw new RuntimeException(String.format("Failed to map the received encoded OCSP entry"), e);
}
}
private void logCrlInfo(X509CRL x509crl) {
int revokedCertificatesNo = Objects.isNull(x509crl.getRevokedCertificates()) ? 0 : x509crl.getRevokedCertificates().size();
String message = "Embedding CRL response... ["
+ "IssuerDN: " + x509crl.getIssuerX500Principal() + " "
+ "This update: " + x509crl.getThisUpdate() + " "
+ "Next update: " + x509crl.getNextUpdate() + " "
+ "No. of revoked certificates: " + revokedCertificatesNo
+ "]";
log.info(message);
}
private void logOcspInfo(OCSPResp ocspResp, BasicOCSPResp basicResp) {
SingleResp response = basicResp.getResponses()[0];
BigInteger serialNumber = response.getCertID().getSerialNumber();
X509CertificateHolder firstCertificate = basicResp.getCerts()[0];
String message = "Embedding OCSP response... ["
+ "Status: " + (ocspResp.getStatus() == 0 ? "OK" : "NOK") + " "
+ "Produced at: " + basicResp.getProducedAt() + " "
+ "This update: " + response.getThisUpdate() + " "
+ "Next update: " + response.getNextUpdate() + " "
+ "X509 cert issuer: " + firstCertificate.getIssuer() + " "
+ "X509 cert subject: " + firstCertificate.getSubject() + " "
+ "Certificate ID: " + serialNumber.toString() + "(" + serialNumber.toString(16).toUpperCase() + ")"
+ "]";
log.info(message);
}
private boolean hasDocumentSignature() throws IOException {
try (ByteArrayInputStream is = new ByteArrayInputStream(inMemoryStream.toByteArray());
PdfReader reader = new PdfReader(is, new ReaderProperties());
PdfDocument pdfDocument = new PdfDocument(reader)) {
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
return signatureUtil.getSignatureNames().size() > 0;
}
}
pdfdocumentsigner.java
public class PdfDocumentSigner extends PdfSigner {
public PdfDocumentSigner(PdfReader reader, OutputStream outputStream, boolean properties) throws IOException {
super(reader, outputStream, properties);
}
public byte[] computeHash(IExternalSignatureContainer externalHashContainer, int estimatedSize) throws GeneralSecurityException, IOException {
if (closed) {
throw new PdfException(PdfException.ThisInstanceOfPdfSignerAlreadyClosed);
}
PdfSignature signatureDictionary = new PdfSignature();
PdfSignatureAppearance appearance = getSignatureAppearance();
signatureDictionary.setReason(appearance.getReason());
signatureDictionary.setLocation(appearance.getLocation());
signatureDictionary.setSignatureCreator(appearance.getSignatureCreator());
signatureDictionary.setContact(appearance.getContact());
signatureDictionary.setDate(new PdfDate(getSignDate()));
externalHashContainer.modifySigningDictionary(signatureDictionary.getPdfObject());
cryptoDictionary = signatureDictionary;
Map<PdfName, Integer> exc = new HashMap<>();
exc.put(PdfName.Contents, estimatedSize * 2 + 2);
preClose(exc);
InputStream dataRangeStream = getRangeStream();
return externalHashContainer.sign(dataRangeStream);
}
public void signWithAuthorizedSignature(IExternalSignatureContainer externalSignatureContainer, int estimatedSize)
throws GeneralSecurityException, IOException {
InputStream dataRangeStream = getRangeStream();
byte[] authorizedSignature = externalSignatureContainer.sign(dataRangeStream);
if (estimatedSize < authorizedSignature.length) {
throw new IOException(String.format("Not enough space. The estimated signature size [%d bytes] is less than the received authorized "
+ "signature [%d bytes] which needs to be embedded into the document.", estimatedSize,
authorizedSignature.length));
}
byte[] paddedSignature = new byte[estimatedSize];
System.arraycopy(authorizedSignature, 0, paddedSignature, 0, authorizedSignature.length);
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.Contents, new PdfString(paddedSignature).setHexWriting(true));
close(dic2);
closed = true;
}
我一直在寻找,似乎我没有遗漏任何东西,谁能帮我解决这个问题?
这里是最终的 pdf 示例
pdfWriter = new PdfWriter(inMemoryStream, new WriterProperties().addXmpMetadata().setPdfVersion(PdfVersion.PDF_1_0));
这就是问题所在,将其更改为
pdfWriter = new PdfWriter(inMemoryStream, new WriterProperties().addXmpMetadata().setPdfVersion(PdfVersion.PDF_1_3));
效果很好
我正在尝试从第 3 方签名提供商处签署 pdf 文档。 在创建空签名后,我向他们发送了文档哈希,他们发送了一个时间戳令牌(我们同意这将是一个时间戳签名),然后我将签名添加回 pdf。 api 获取 timestmp、crl 和 ocsp 的调用进行得很顺利,但是一旦我生成带有签名的 pdf,adobe 就会说签名无效并且错误是:
签名验证时出错。
签名包含不正确、无法识别、损坏或可疑的数据。 支持信息:SigDict /Contents 非法数据
这是当前代码:
signatureprovider.java
public ByteArrayOutputStream sign(byte[] src, Rectangle rect, int signPage) throws Exception {
log.info("I am in the signing method");
ByteArrayOutputStream dest = new ByteArrayOutputStream();
PdfDocumentHandler handler = new PdfDocumentHandler(new ByteArrayInputStream(src), dest);
handler.prepareForSigning(rect, signPage);
TrustedTimestampService service = new TrustedTimestampService();
SignResponseWrapper response = new SignResponseWrapper();
response = service.signHash(handler.getEncodedDocumentHash());
String timestampToken = response.getSignResponse().getSignatureObject().getOther().getScSignatureObjects().getScExtendedSignatureObject().getTimestamp().getRFC3161TimeStampToken();
List<String> encodedCrlEntries = new ArrayList<String>();
encodedCrlEntries.add(response.getSignResponse().getOptionalOutputs().getScRevocationInformation().getScCRLs().getScCRL());
List<String> encodedOcspEntries = new ArrayList<String>();
encodedOcspEntries.add(response.getSignResponse().getOptionalOutputs().getScRevocationInformation().getScOCSPs().getScOCSP());
byte[] signed = handler.createSignedPdf(Base64.getDecoder().decode(timestampToken), 15000, encodedCrlEntries, encodedOcspEntries);
dest.flush();
dest.write(signed);
handler.close();
return dest;
}
我会避免添加该服务,它只是发送哈希并获取令牌 pdfdocumenthandler.java
public PdfDocumentHandler(InputStream inputStream, ByteArrayOutputStream outputStream) { this.inputStream = 输入流; this.outputStream = 输出流;
}
public void prepareForSigning(Rectangle rect, int signPage)
throws IOException, GeneralSecurityException {
inMemoryStream = new ByteArrayOutputStream();
inMemoryStream.write(StreamUtil.inputStreamToArray(inputStream));
boolean hasSignature = hasDocumentSignature();
pdfReader = new PdfReader(new ByteArrayInputStream(inMemoryStream.toByteArray()), new ReaderProperties());
inMemoryStream.reset();
pdfWriter = new PdfWriter(inMemoryStream, new WriterProperties().addXmpMetadata().setPdfVersion(PdfVersion.PDF_1_0));
StampingProperties stampingProperties = new StampingProperties();
pdfSigner = new PdfDocumentSigner(pdfReader, inMemoryStream, hasSignature ? stampingProperties.useAppendMode() : stampingProperties);
pdfSigner.getSignatureAppearance()
.setReason("myreason")
.setLocation("mylocation")
.setContact("mycontact")
.setPageRect(rect)
.setPageNumber(signPage)
.setSignatureCreator("creator");
pdfSigner.setFieldName("digitalSig");
try {
imgStorage = new ImageStorage();
ImageData imgData = ImageDataFactory.create(Base64.getDecoder().decode(imgStorage.getimgData().getBytes()));
pdfSigner.getSignatureAppearance().setRenderingMode(RenderingMode.GRAPHIC);
pdfSigner.getSignatureAppearance().setSignatureGraphic(imgData);
} catch(Exception e){
pdfSigner.getSignatureAppearance().setRenderingMode(RenderingMode.DESCRIPTION);
}
Map<PdfName, PdfObject> signatureDictionary = new HashMap<>();
signatureDictionary.put(PdfName.Filter, PdfName.Adobe_PPKLite);
signatureDictionary.put(PdfName.SubFilter, PdfName.ETSI_RFC3161);
Calendar signDate = Calendar.getInstance();
pdfSigner.setSignDate(signDate);
PdfHashSignatureContainer hashSignatureContainer = new PdfHashSignatureContainer("SHA-512", new PdfDictionary(signatureDictionary));
documentHash = pdfSigner.computeHash(hashSignatureContainer, 15_000);
log.info("prepared document - outputstream: " + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
log.info("prepared document - inmemorystream: " + Base64.getEncoder().encodeToString(inMemoryStream.toByteArray()));
}
public byte[] createSignedPdf(byte[] externalSignature, int estimatedSize, List<String> encodedCrlEntries,
List<String> encodedOcspEntries) {
if (pdfSigner.getCertificationLevel() == PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED) {
throw new RuntimeException(String.format("Could not apply signature because source file contains a certification that does not allow "
+ "any changes to the document with id %s"));
}
if (estimatedSize < externalSignature.length) {
throw new RuntimeException(String.format("Not enough space for signature in the document. The estimated size needs to be " +
" %d bytes.", externalSignature.length));
}
try {
pdfSigner.signWithAuthorizedSignature(new PdfSignatureContainer(externalSignature), estimatedSize);
if (null != encodedCrlEntries || null!=encodedOcspEntries) {
extendDocumentWithCrlOcspMetadata(encodedCrlEntries, encodedOcspEntries);
} else {
log.info("No CRL and OCSP entries were received to be embedded into the PDF");
outputStream.write(inMemoryStream.toByteArray());
}
//outputStream.write(inMemoryStream.toByteArray());
closeResource(inMemoryStream);
closeResource(outputStream);
log.info("complete document - outputstream: " + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
log.info("complete document - inmemorystream: " + Base64.getEncoder().encodeToString(inMemoryStream.toByteArray()));
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(String.format("Failed to embed the signature in the document"), e);
}
return outputStream.toByteArray();
}
private void extendDocumentWithCrlOcspMetadata(List<String> encodedCrlEntries, List<String> encodedOcspEntries) {
if (pdfSigner.getCertificationLevel() == PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED) {
throw new RuntimeException(String.format("Could not apply revocation information (LTV) to the DSS Dictionary. Document contains a " +
"certification that does not allow any changes"));
}
log.info("document without crl and ocsp - outputstream: " + Base64.getEncoder().encodeToString(outputStream.toByteArray()));
log.info("document without crl and ocsp - inmemorystream: " + Base64.getEncoder().encodeToString(inMemoryStream.toByteArray()));
List<byte[]> crl = mapEncodedEntries(encodedCrlEntries, this::mapEncodedCrl);
List<byte[]> ocsp = mapEncodedEntries(encodedOcspEntries, this::mapEncodedOcsp);
try (InputStream documentStream = new ByteArrayInputStream(inMemoryStream.toByteArray());
PdfReader reader = new PdfReader(documentStream);
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(reader, writer, new StampingProperties().preserveEncryption().useAppendMode())) {
LtvVerification validation = new LtvVerification(pdfDocument);
List<String> signatureNames = new SignatureUtil(pdfDocument).getSignatureNames();
String signatureName = signatureNames.get(signatureNames.size() - 1);
boolean isSignatureVerificationAdded = validation.addVerification(signatureName, ocsp, crl, null);
validation.merge();
logSignatureVerificationInfo(isSignatureVerificationAdded);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to embed the signature(s) in the document(s) and close the streams"));
}
}
public String getId() {
return id;
}
public String getEncodedDocumentHash() {
return Base64.getEncoder().encodeToString(documentHash);
}
private void logSignatureVerificationInfo(boolean isSignatureVerificationAdded) {
if (isSignatureVerificationAdded) {
log.info("Merged LTV validation information to the output stream");
} else {
log.warn("Failed to merge LTV validation information to the output stream");
}
}
private List<byte[]> mapEncodedEntries(List<String> encodedEntries, Function<String, byte[]> mapperFunction) {
return Objects.nonNull(encodedEntries)
? encodedEntries.stream().map(mapperFunction).collect(Collectors.toList())
: Collections.emptyList();
}
private byte[] mapEncodedCrl(String encodedCrl) {
try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(encodedCrl))) {
X509CRL x509crl = (X509CRL) CertificateFactory.getInstance("X.509").generateCRL(inputStream);
logCrlInfo(x509crl);
return x509crl.getEncoded();
} catch (IOException | CertificateException | CRLException e) {
throw new RuntimeException(String.format("Failed to map the received encoded CRL entry"), e);
}
}
private byte[] mapEncodedOcsp(String encodedOcsp) {
try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(encodedOcsp))) {
OCSPResp ocspResp = new OCSPResp(inputStream);
BasicOCSPResp basicResp = (BasicOCSPResp) ocspResp.getResponseObject();
logOcspInfo(ocspResp, basicResp);
return basicResp.getEncoded();
} catch (IOException | OCSPException e) {
throw new RuntimeException(String.format("Failed to map the received encoded OCSP entry"), e);
}
}
private void logCrlInfo(X509CRL x509crl) {
int revokedCertificatesNo = Objects.isNull(x509crl.getRevokedCertificates()) ? 0 : x509crl.getRevokedCertificates().size();
String message = "Embedding CRL response... ["
+ "IssuerDN: " + x509crl.getIssuerX500Principal() + " "
+ "This update: " + x509crl.getThisUpdate() + " "
+ "Next update: " + x509crl.getNextUpdate() + " "
+ "No. of revoked certificates: " + revokedCertificatesNo
+ "]";
log.info(message);
}
private void logOcspInfo(OCSPResp ocspResp, BasicOCSPResp basicResp) {
SingleResp response = basicResp.getResponses()[0];
BigInteger serialNumber = response.getCertID().getSerialNumber();
X509CertificateHolder firstCertificate = basicResp.getCerts()[0];
String message = "Embedding OCSP response... ["
+ "Status: " + (ocspResp.getStatus() == 0 ? "OK" : "NOK") + " "
+ "Produced at: " + basicResp.getProducedAt() + " "
+ "This update: " + response.getThisUpdate() + " "
+ "Next update: " + response.getNextUpdate() + " "
+ "X509 cert issuer: " + firstCertificate.getIssuer() + " "
+ "X509 cert subject: " + firstCertificate.getSubject() + " "
+ "Certificate ID: " + serialNumber.toString() + "(" + serialNumber.toString(16).toUpperCase() + ")"
+ "]";
log.info(message);
}
private boolean hasDocumentSignature() throws IOException {
try (ByteArrayInputStream is = new ByteArrayInputStream(inMemoryStream.toByteArray());
PdfReader reader = new PdfReader(is, new ReaderProperties());
PdfDocument pdfDocument = new PdfDocument(reader)) {
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
return signatureUtil.getSignatureNames().size() > 0;
}
}
pdfdocumentsigner.java
public class PdfDocumentSigner extends PdfSigner {
public PdfDocumentSigner(PdfReader reader, OutputStream outputStream, boolean properties) throws IOException {
super(reader, outputStream, properties);
}
public byte[] computeHash(IExternalSignatureContainer externalHashContainer, int estimatedSize) throws GeneralSecurityException, IOException {
if (closed) {
throw new PdfException(PdfException.ThisInstanceOfPdfSignerAlreadyClosed);
}
PdfSignature signatureDictionary = new PdfSignature();
PdfSignatureAppearance appearance = getSignatureAppearance();
signatureDictionary.setReason(appearance.getReason());
signatureDictionary.setLocation(appearance.getLocation());
signatureDictionary.setSignatureCreator(appearance.getSignatureCreator());
signatureDictionary.setContact(appearance.getContact());
signatureDictionary.setDate(new PdfDate(getSignDate()));
externalHashContainer.modifySigningDictionary(signatureDictionary.getPdfObject());
cryptoDictionary = signatureDictionary;
Map<PdfName, Integer> exc = new HashMap<>();
exc.put(PdfName.Contents, estimatedSize * 2 + 2);
preClose(exc);
InputStream dataRangeStream = getRangeStream();
return externalHashContainer.sign(dataRangeStream);
}
public void signWithAuthorizedSignature(IExternalSignatureContainer externalSignatureContainer, int estimatedSize)
throws GeneralSecurityException, IOException {
InputStream dataRangeStream = getRangeStream();
byte[] authorizedSignature = externalSignatureContainer.sign(dataRangeStream);
if (estimatedSize < authorizedSignature.length) {
throw new IOException(String.format("Not enough space. The estimated signature size [%d bytes] is less than the received authorized "
+ "signature [%d bytes] which needs to be embedded into the document.", estimatedSize,
authorizedSignature.length));
}
byte[] paddedSignature = new byte[estimatedSize];
System.arraycopy(authorizedSignature, 0, paddedSignature, 0, authorizedSignature.length);
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.Contents, new PdfString(paddedSignature).setHexWriting(true));
close(dic2);
closed = true;
}
我一直在寻找,似乎我没有遗漏任何东西,谁能帮我解决这个问题? 这里是最终的 pdf 示例
pdfWriter = new PdfWriter(inMemoryStream, new WriterProperties().addXmpMetadata().setPdfVersion(PdfVersion.PDF_1_0));
这就是问题所在,将其更改为
pdfWriter = new PdfWriter(inMemoryStream, new WriterProperties().addXmpMetadata().setPdfVersion(PdfVersion.PDF_1_3));
效果很好