英文:
Digitally Signing a signed document using iText 5
问题
你好,我可以使用iText 5来对PDF文档进行数字签名。我有一个再次签名PDF的要求,然而在验证PDF时显示初始签名无效。您可以查看重新签名的文件,以下是用于签名的代码:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
public class Test {
public static void main(String[] args) throws DocumentException, IOException, GeneralSecurityException {
PdfReader reader = null;
PrivateKey pk = null;
String alias = "PRASANTH KARUNAKARAN NAIR";
KeyStore ks = null;
// ...
try {
pk = (PrivateKey)ks.getKey(alias, "abcd".toCharArray());
}
// ...
Certificate[] chain = null;
// ...
try {
reader = new PdfReader("D:///signedSample.pdf");
}
// ...
String signedFileNameWithPath = "D:///signedsignedSample.pdf";
FileOutputStream os = null;
// ...
PdfStamper stamper = null;
// ...
try {
stamper = PdfStamper.createSignature(reader, os, '\0');
}
// ...
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
Integer pageNumber = 2;
Rectangle rect = new Rectangle(50, 100, 220, 140);
appearance.setAcro6Layers(false);
appearance.setLayer4Text(PdfSignatureAppearance.questionMark);
appearance.setVisibleSignature(rect, pageNumber, "sig2");
PrivateKeySignature privateKeySignature = null;
// ...
if (privateKeySignature != null) {
BouncyCastleDigest bouncyCastleDigest = new BouncyCastleDigest();
// ...
try {
MakeSignature.signDetached(appearance, (ExternalDigest)bouncyCastleDigest,
(ExternalSignature)privateKeySignature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
}
// ...
}
}
}
请告诉我出了什么问题。
英文:
Hi I am able to digitally sign a PDF document using iText 5. I have a requirement of signing the PDF again, whereas while validating the PDF it shows that the initial signature is invalid. You can view the file here which was signed again.
See below the code used for sigining,
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
public class Test {
public static void main(String[] args) throws DocumentException, IOException, GeneralSecurityException {
PdfReader reader = null;
PrivateKey pk = null;
String alias = "PRASANTH KARUNAKARAN NAIR";
KeyStore ks = null;
try {
ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
}
catch (KeyStoreException|java.security.NoSuchProviderException e4){
e4.printStackTrace();
}
try {
ks.load(null, null);
}
catch (NoSuchAlgorithmException|java.security.cert.CertificateException|IOException e4){
e4.printStackTrace();
}
try {
pk = (PrivateKey)ks.getKey(alias, "abcd".toCharArray());
}
catch (UnrecoverableKeyException|KeyStoreException|NoSuchAlgorithmException e3){
e3.printStackTrace();
}
Certificate[] chain = null;
try {
chain = ks.getCertificateChain(alias);
}
catch (KeyStoreException e3){
e3.printStackTrace();
}
try {
reader = new PdfReader("D:///signedSample.pdf");
}
catch (IOException e5){
e5.printStackTrace();
}
String signedFileNameWithPath = "D:///signedsignedSample.pdf";
FileOutputStream os = null;
try {
os = new FileOutputStream(signedFileNameWithPath);
}
catch (FileNotFoundException e5){
e5.printStackTrace();
}
PdfStamper stamper = null;
try {
stamper = PdfStamper.createSignature(reader, os,'\0');
}
catch (DocumentException|IOException e5) {
e5.printStackTrace();
}
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
Integer pageNumber = 2;
Rectangle rect=new Rectangle(50,100,220,140);
appearance.setAcro6Layers(false);
appearance.setLayer4Text(PdfSignatureAppearance.questionMark);
appearance.setVisibleSignature(rect,pageNumber, "sig2");
PrivateKeySignature privateKeySignature=null;
try {
privateKeySignature= new PrivateKeySignature(pk, "SHA-256", ks.getProvider().getName());
}
catch (NullPointerException e) {
}
if(privateKeySignature!=null) {
BouncyCastleDigest bouncyCastleDigest = new BouncyCastleDigest();
try {
MakeSignature.signDetached(appearance, (ExternalDigest)bouncyCastleDigest, (ExternalSignature)privateKeySignature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
}
catch (IOException e1){
e1.printStackTrace();
}
catch (DocumentException e1){
e1.printStackTrace();
}
catch (SignatureException e1) {
e1.printStackTrace();
}
catch (GeneralSecurityException e1){
e1.printStackTrace();
}
}
}
}
Please let me know what went wrong.
答案1
得分: 1
如果您想在已签名的PDF中添加另一个签名,显然必须小心,不要更改任何已签名的字节。换句话说,附加的内容和更改必须作为增量更新附加到现有文件中。
例如,带有三个签名的PDF在示意上必须类似于这样:
(有关背景信息,请阅读此答案并从中引用的文档。)
但是,默认情况下,iText的PdfStamper
不使用增量更新,而是使用原始文件中的各个对象创建一个全新的文件,可能完全改变顺序,并且删除新版本中不再需要的对象。当然,这会使第一个签名无效。
要创建一个使用增量更新的PdfStamper
,您必须使用不同的PdfStamper.createSignature
重载。请将
stamper = PdfStamper.createSignature(reader, os, '\0');
替换为
stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
方法的JavaDocs中对这些额外参数进行了如下说明:
/**
* @param reader 原始文档
* @param os 输出流,或者为null以将文档保留在临时文件中
* @param pdfVersion 新的PDF版本,或者为'\0'以保持与原始文档相同的版本
* @param tempFile 临时文件的位置。如果它是目录,将在那里创建一个临时文件。
* 如果它是文件,则将直接使用它。该文件将在退出时被删除,除非os为null。
* 在这种情况下,可以直接从临时文件中检索文档。如果它为null,
* 将不会创建临时文件,并且将使用内存
* @param append 如果为true,则签名和所有其他内容将作为新的修订版本添加,
* 因此不会使现有签名失效
英文:
If you want to add another signature to an already signed PDF, you obviously must take care and not change any signed byte. In other words, additions and changes must be appended to the existing file as an incremental update.
A PDF with three signatures, for example, schematically must look similar to this:
(For backgrounds read this answer and documents referenced from it.)
By default, though, the iText PdfStamper
does not use incremental updates but instead creates a completely new file using the individual objects from the original file in a possibly completely changed order and with objects dropped not needed in the new versions anymore. This of course renders the first signature invalid.
To create a PdfStamper
that works using an incremental update, you have to use a different PdfStamper.createSignature
overload. Please replace
stamper = PdfStamper.createSignature(reader, os,'\0');
by
stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
The extra parameters are documented like this in the method JavaDocs:
/* @param reader the original document
* @param os the output stream or <CODE>null</CODE> to keep the document in the temporary file
* @param pdfVersion the new pdf version or '\0' to keep the same version as the original
* document
* @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
* If it's a file it will be used directly. The file will be deleted on exit unless <CODE>os</CODE> is null.
* In that case the document can be retrieved directly from the temporary file. If it's <CODE>null</CODE>
* no temporary file will be created and memory will be used
* @param append if <CODE>true</CODE> the signature and all the other content will be added as a
* new revision thus not invalidating existing signatures
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论