英文:
External Signature with PDFBox 2.0.12
问题
Signing modified issue.
使用DSC令牌(外部签名)传递文档哈希和签名哈希。
出现错误:“文档在签名后已被更改或损坏”
获取文档的哈希值:
英文:
Signing modified issue.
Iam passing the doc hash and signing hash using a DSC token(External Signing).
iam getting error like:"The document has been altered or corrupted since it was signed"
Getting the Hash of Document:-
public String genrateDigitalCertificateSign() {
try {
src = new FileInputStream(inputFilePath);
OutputStream dest = new FileOutputStream(new File(RESULT_FOLDER, "Test.pdf"));
pdDocument = PDDocument.load(src);
PDSignature pds = null;
String hashdocument = null;
File imgFile = new File(inputImgPath);
PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
if (acroForm == null) {
pdDocument.getDocumentCatalog().setAcroForm(acroForm = new PDAcroForm(pdDocument));
}
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
pds = new PDSignature();
pds.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
pds.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
pds.setSignDate(Calendar.getInstance());
PDPage pdPage = pdDocument.getPage(0);
PDImageXObject pdImage = PDImageXObject.createFromFileByContent(imgFile, pdDocument);
//visible signature rectangle
rectangle = new PDRectangle(200.00, 200.00,150.00,50.00);
List<PDField> acroFormFields = acroForm.getFields();
PDSignatureField signatureField = new PDSignatureField(acroForm);
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
signatureField.setValue(pds);
acroFormFields.add(signatureField);
pdDocument.addSignature(pds);
//creating visible stamp
createVisualSignatureTemplate(pdDocument, signatureField, pdPage, rectangle, pdImage, signDisplayInfo);
externalSigning = pdDocument.saveIncrementalForExternalSigning(dest);
InputStream dataToSign = externalSigning.getContent();
hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
return hashdocument;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
External signing Code:-
public byte[] sign(byte[] hash)
throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
PrivateKey privKey = pk;
try {
List<Certificate> certList = new ArrayList<>();
certList.addAll(Arrays.asList(chain));
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate
.getInstance(chain[0].getEncoded());
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
.build(sha1Signer, new X509CertificateHolder(cert)));
gen.addCertificates(certs);
CMSProcessableInputStream msg = new CMSProcessableInputStream(new ByteArrayInputStream(hash));
CMSSignedData signedData = gen.generate(msg, false);
return signedData.getEncoded();
} catch (GeneralSecurityException e) {
throw new IOException(e);
} catch (CMSException e) {
throw new IOException(e);
} catch (OperatorCreationException e) {
throw new IOException(e);
}
}
Signature appending code:-
public void signedPDF(byte[] hash)
throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException {
byte[] signedHash = sign(hash);
externalSigning.setSignature(signedHash);
IOUtils.closeQuietly(src);
pdDocument.close();
}
After this signature is appending,but while opening the signed PDF getting the error like as shown in the below image.
PDF file Link:
https://drive.google.com/file/d/1qRT2CVgET8Ds1fu0b5psii3j8ytPKaLH/view?usp=sharing
[EDITED]
public byte[] genrateDigitalCertificateSign() {
try {
src = new FileInputStream(inputFilePath);
OutputStream dest = new FileOutputStream(new File(RESULT_FOLDER, "Test.pdf"));
pdDocument = PDDocument.load(src);
PDSignature pds = null;
String hashdocument = null;
File imgFile = new File(inputImgPath);
PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
if (acroForm == null) {
pdDocument.getDocumentCatalog().setAcroForm(acroForm = new PDAcroForm(pdDocument));
}
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
pds = new PDSignature();
pds.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
pds.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
pds.setSignDate(Calendar.getInstance());
PDPage pdPage = pdDocument.getPage(0);
PDImageXObject pdImage = PDImageXObject.createFromFileByContent(imgFile, pdDocument);
//visible signature rectangle
rectangle = new PDRectangle(200.00, 200.00,150.00,50.00);
List<PDField> acroFormFields = acroForm.getFields();
PDSignatureField signatureField = new PDSignatureField(acroForm);
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
signatureField.setValue(pds);
acroFormFields.add(signatureField);
pdDocument.addSignature(pds);
//creating visible stamp
createVisualSignatureTemplate(pdDocument, signatureField, pdPage, rectangle, pdImage, signDisplayInfo);
externalSigning = pdDocument.saveIncrementalForExternalSigning(dest);
InputStream dataToSign = externalSigning.getContent();
hashdocument = DigestUtils.sha256(dataToSign); // hash is generated
return hashdocument;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Recently signed PDF file link:-
https://drive.google.com/file/d/1mZ8Wqppx3EylI1aLYB9Fl8NzibBXZxzR/view?usp=sharing
[Edited 2] this is the flow i tried after your suggestions.
In this what is the 1st issue your pointing out? Iam unable to figure out, please help.
externalSigning = pdDocument.saveIncrementalForExternalSigning(dest);
InputStream dataToSign = externalSigning.getContent();
hashdocument = DigestUtils.sha256(dataToSign); // hash is generated
byte[] signedhash = sign(hashdocument);
externalSigning.setSignature(signedhash);
pdDocument.close();
答案1
得分: 3
以下是翻译好的部分:
双重散列
你的代码存在两个问题:
你的代码创建了一个包含文档数据的哈希的哈希的签名,而实际上只期望文档数据的哈希。
你的代码主要是从PDFBox示例中借用的。在外部签名的情况下,会检索并将“content to sign”转发到“sign”方法:
byte[] cmsSignature = sign(externalSigning.getContent());
(来自扩展了CreateSignatureBase
的CreateSignature
)
然而,在你的代码中,你首先对要签名的内容进行哈希处理,然后将该哈希传递给sign
方法:
public String genrateDigitalCertificateSign() {
...
InputStream dataToSign = externalSigning.getContent();
hashdocument = DigestUtils.sha256Hex(dataToSign); // 生成哈希
return hashdocument;
...
}
byte[] signedHash = sign(hash);
就CMS签名容器的创建而言,你的sign
方法使用了与PDFBox示例相同的代码。
因此,你的代码对文档数据进行了多次哈希处理。要解决这个问题,你可以:
- 要么删除哈希处理步骤,直接将文档数据传递给签名
- 要么更改
sign
方法,不再对其输入进行哈希处理。
我假设你的意图是计算并将哈希传递给一个独立的签名服务,所以你的选择可能是后者。
十六进制编码哈希
你的哈希生成方法genrateDigitalCertificateSign
返回的哈希值不是实际的byte[]
,而是对其进行了十六进制编码,并返回了十六进制字符串:
hashdocument = DigestUtils.sha256Hex(dataToSign); // 生成哈希
return hashdocument;
然而,你的其他方法期望获得并操作实际的byte[]
。
要解决这个问题,你可以:
- 要么一开始不进行十六进制编码,即使用
DigestUtils.sha256
而不是DigestUtils.sha256Hex
,并从genrateDigitalCertificateSign
返回一个byte[]
而不是一个String
, - 要么在将哈希值传递给
signedPDF
方法的sign
方法之前解码十六进制哈希值。
我假设你的意图是以字符串形式传输哈希值,所以你的选择可能是后者。
英文:
Ok, there are two issues in your code:
Double Hashing
Your code creates a signature that contains the hash of the hash of the document data where simply the hash of the document data is expected.
Your code mostly is borrowed from the PDFBox examples. In case of external signing the content to sign is retrieved and forwarded to the sign
method:
byte[] cmsSignature = sign(externalSigning.getContent());
(from CreateSignature
which extends CreateSignatureBase
)
In your code, though, you first hash the content to sign and forward that hash to the sign method:
public String genrateDigitalCertificateSign() {
...
InputStream dataToSign = externalSigning.getContent();
hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
return hashdocument;
...
}
byte[] signedHash = sign(hash);
As fas as creation of the CMS signature container is concerned, your sign
method uses the same code as the PDFBox example.
Thus, your code hashes the document data once too often. To fix this, you have to
- either drop the hashing step and forward the document data to sign
- or change the
sign
method to not hash its input again.
I assume your intention is to calculate and forward the hash to a separate signing service, so your choice would be the latter.
Hex Encoding the Hash
Your hash generating method genrateDigitalCertificateSign
returns the hash value not as the actual byte[]
but hex encodes it and returns that hex string:
hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
return hashdocument;
Your other methods, though, expect to get and operate on the actual byte[]
.
To fix this, you have to
- either not hex encode to start with, i.e. use
DigestUtils.sha256
instead ofDigestUtils.sha256Hex
and return abyte[]
instead of aString
fromgenrateDigitalCertificateSign
, - or hex decode the hash value before feeding it to the
sign
method in yoursignedPDF
method.
I assume your intention is to transport the hash in string form, so your choice would be the latter.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论