写在前面
一般情况下我们都是使用iText7自带的
pdfsigner.detach()
复制代码
方法对pdf文件进行签名,iText7已经自己封装好了PKC7,所以这里还是挺方便的。但如果因为某种需求需要我们自己来进行P7签名,那么我们就可以使用
pdfsigner.signExternalContainer()
复制代码
来自己实现对pdf的签名,即itext7只要提供要签名的数据给我们就行了。
签名和验签大致流程
我们可以看下这幅图,来自《Acrobat_DigitalSignatures_in_PDF》:
大致的意思就是说
- 要签名的时候会把文档转换成字节流叫ByteRange
- ByteRange有四个数字,分成三部分(以图为例),我们要用来签名的数据就在0- 840和960-1200这部分,然后签名就存放在840~960里面。
- 因此我们验签的时候获取签名值就来自于840~960也就是Contents里。
- 要验签的原文就是ByteRange里除去签名值的部分。
IExternalSignatureContainer介绍
我们先看下IExternalSignatureContainer这个接口:
/**
* Interface to sign a document. The signing is fully done externally, including the container composition.
* 这是一个用来签署文件的接口,它让所有的签名都完全来自于外部扩展实现。
* @author Paulo Soares
*/
public interface IExternalSignatureContainer {
/**
* Produces the container with the signature.
* @param data the data to sign
* @return a container with the signature and other objects, like CRL and OCSP. The container will generally be a PKCS7 one.
* @throws GeneralSecurityException
*/
byte[] sign(InputStream data) throws GeneralSecurityException;
/**
* Modifies the signature dictionary to suit the container. At least the keys {
@link PdfName#Filter} and
* {
@link PdfName#SubFilter} will have to be set.
* @param signDic the signature dictionary
*/
void modifySigningDictionary(PdfDictionary signDic);
}
复制代码
signExternalContainer()方法介绍
接下来我们看下需要用到IExternalSignatureContainer 的方法 signExternalContainer() 的介绍:
/**
* Sign the document using an external container, usually a PKCS7. The signature is fully composed
* externally, iText will just put the container inside the document.
* <br><br>
* NOTE: This method closes the underlying pdf document. This means, that current instance
* of PdfSigner cannot be used after this method call.
*
* @param externalSignatureContainer the interface providing the actual signing
* @param estimatedSize the reserved size for the signature
* @throws GeneralSecurityException
* @throws IOException
*/
public void signExternalContainer(IExternalSignatureContainer externalSignatureContainer, int estimatedSize) throws GeneralSecurityException, IOException {
//省略部分源码
//关注这里,调用getRangeStream()方法获取到要签名的数据
//传入到externalSignatureContainer.sign()方法里给我们签
InputStream data = getRangeStream();
byte[] encodedSig = externalSignatureContainer.sign(data);
//省略部分源码
/**
* Gets the document bytes that are hashable when using external signatures. 在使用外部签名的时候会返回可用于哈希的文件字节。
* The general sequence is:
* {
@link #preClose(Map)}, {
@link #getRangeStream()} and {
@link #close(PdfDictionary)}.
*
* @return The {
@link InputStream} of bytes to be signed.
* 返回用于签名的字节
*/
protected InputStream getRangeStream() throws IOException {
RandomAccessSourceFactory fac = new RandomAccessSourceFactory();
return new RASInputStream(fac.createRanged(getUnderlyingSource(), range));
}
复制代码
可以看到这个方法需要两个参数IExternalSignatureContainer(扩展签名容器) 和 estimatedSize(预估值)。
开始重写IExternalSignatureContainer
那么我们先重写IExternalSignatureContainer:
注:以下使用到的哈希方法,签名方法是做一个说明,