使用iText 5对已签文件进行数字签名

huangapple go评论78阅读模式
英文:

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:

使用iText 5对已签文件进行数字签名

(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

huangapple
  • 本文由 发表于 2020年10月27日 16:43:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/64550801.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定