Java 具有多个签名的iText7 PDF

Java 具有多个签名的iText7 PDF,java,itext7,Java,Itext7,我正在将代码从iText5迁移到iText7,目前我正在努力将一个签名附加到已经包含另一个签名的PDF。 这些签名是用我们的国民身份证(公民证)签名的 在iText5中,我使用了PdfStamper,但iText7中缺少它 这就是我到目前为止所做的: package cartaocidadao; import java.io.FileOutputStream; import java.security.KeyStore; import java.security.PrivateKey; imp

我正在将代码从iText5迁移到iText7,目前我正在努力将一个签名附加到已经包含另一个签名的PDF。 这些签名是用我们的国民身份证(公民证)签名的

在iText5中,我使用了PdfStamper,但iText7中缺少它

这就是我到目前为止所做的:

package cartaocidadao;

import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import javax.swing.JOptionPane;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.poreid.config.POReIDConfig;
import org.poreid.crypto.POReIDProvider;
import com.itextpdf.signatures.OcspClientBouncyCastle;
import com.itextpdf.signatures.TSAClientBouncyCastle;
import java.io.IOException;
import java.util.Collection;

import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.signatures.BouncyCastleDigest;
import com.itextpdf.signatures.ICrlClient;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.IExternalDigest;
import com.itextpdf.signatures.IExternalSignature;
import com.itextpdf.signatures.IOcspClient;
import com.itextpdf.signatures.PdfSignatureAppearance;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.signatures.PrivateKeySignature;
import com.itextpdf.signatures.ITSAClient;
import com.itextpdf.signatures.OCSPVerifier;
import java.security.GeneralSecurityException;
import static javax.swing.JOptionPane.ERROR_MESSAGE;

/**
 *
 * @author i.lourenco
 */
public class Signature {

    /**
     *  Signs the PDF with the Citizen Card Certificate
     * @param src Source file
     * @param dest Destination file
     * @return TRUE if the PDF was signed successfully
     */
    protected static boolean signPDF(String src, String dest) {

        try {

            Security.addProvider(new POReIDProvider());

            BouncyCastleProvider provider = new BouncyCastleProvider();
            Security.addProvider(provider);

            KeyStore ks = KeyStore.getInstance(POReIDConfig.POREID);
            ks.load(null);

            PrivateKey pk = (PrivateKey) ks.getKey(POReIDConfig.ASSINATURA, null);

            Certificate[] chain = ks.getCertificateChain(POReIDConfig.ASSINATURA);

            OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);

            IOcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);

            ITSAClient tsaClient = new TSAClientBouncyCastle("http://ts.cartaodecidadao.pt/tsa/server", "", "");

            sign(src, dest, chain, pk, DigestAlgorithms.SHA256, POReIDConfig.POREID, PdfSigner.CryptoStandard.CMS, "", "", null, ocspClient, tsaClient, 0);
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null, e.getMessage(), "Erro", ERROR_MESSAGE);
        }

        return true;
    }

    /**
     * Applies the certificate, timestamp and revocation list to a PDF
     * @param src Original PDF document
     * @param dest Signed PDF document
     * @param chain List of certificates
     * @param pk Private key
     * @param digestAlgorithm Encryption algorithm
     * @param provider Citizen Card provider
     * @param subfilter CMS
     * @param reason Reason for signature
     * @param location Location
     * @param crlList Revocation list
     * @param ocspClient Online Certification Status
     * @param tsaClient Timestamp server
     * @param estimatedSize
     * @throws IOException
     * @throws GeneralSecurityException 
     */
    private static void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm, 
            String provider, PdfSigner.CryptoStandard subfilter, String reason, String location, Collection<ICrlClient> crlList, 
            IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize) throws IOException, GeneralSecurityException {

        PdfReader reader = new PdfReader(src);

        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), false);

        PdfSignatureAppearance appearance = signer.getSignatureAppearance()
                .setReason(reason)
                .setLocation(location)
                .setReuseAppearance(false);
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        appearance.setPageRect(rect).setPageNumber(1);

        signer.getNewSigFieldName();

        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();

        signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }


}
packattaocidao;
导入java.io.FileOutputStream;
导入java.security.KeyStore;
导入java.security.PrivateKey;
导入java.security.security;
导入java.security.cert.Certificate;
导入javax.swing.JOptionPane;
导入org.bouncycastle.jce.provider.BouncyCastleProvider;
导入org.porelid.config.porelidconfig;
导入org.poreid.crypto.POReIDProvider;
导入com.itextpdf.signatures.OcspClientBouncyCastle;
导入com.itextpdf.signatures.TSAClientBouncyCastle;
导入java.io.IOException;
导入java.util.Collection;
导入com.itextpdf.kernel.geom.Rectangle;
导入com.itextpdf.kernel.pdf.PdfReader;
导入com.itextpdf.signatures.BouncyCastleDigest;
导入com.itextpdf.signatures.ICrlClient;
导入com.itextpdf.signatures.DigestAlgorithms;
导入com.itextpdf.signatures.IExternalDigest;
导入com.itextpdf.signatures.IExternalSignature;
导入com.itextpdf.signatures.IOcspClient;
导入com.itextpdf.signatures.pdfsignaturepearance;
导入com.itextpdf.signatures.PdfSigner;
导入com.itextpdf.signatures.PrivateKeySignature;
导入com.itextpdf.signatures.ITSAClient;
导入com.itextpdf.signatures.OCSPVerifier;
导入java.security.GeneralSecurityException;
导入静态javax.swing.JOptionPane.ERROR\u消息;
/**
*
*@作者i.lourenco
*/
公共类签名{
/**
*在PDF上签署公民卡证书
*@param src源文件
*@param dest目标文件
*@如果PDF已成功签名,则返回TRUE
*/
受保护的静态布尔signPDF(字符串src、字符串dest){
试一试{
addProvider(新的poredProvider());
BouncyCastleProvider提供程序=新的BouncyCastleProvider();
Security.addProvider(提供者);
KeyStore ks=KeyStore.getInstance(POReIDConfig.POREID);
ks.load(空);
PrivateKey pk=(PrivateKey)ks.getKey(POReIDConfig.ASSINATURA,null);
证书[]链=ks.getCertificateChain(POReIDConfig.ASSINATURA);
OCSPVerifier OCSPVerifier=新的OCSPVerifier(null,null);
IOcspClient ocspClient=新OcspClientBouncyCastle(ocspVerifier);
ITSAClient tsaClient=新的TSAClientBouncyCastle(“http://ts.cartaodecidadao.pt/tsa/server", "", "");
符号(src、dest、chain、pk、DigestAlgorithms.SHA256、POReIDConfig.POREID、PdfSigner.CryptoStandard.CMS、“,”、null、ocspClient、tsaClient、0);
}捕获(例外e){
showMessageDialog(null,例如getMessage(),“Erro”,错误消息);
}
返回true;
}
/**
*将证书、时间戳和吊销列表应用于PDF
*@param src原始PDF文档
*@param dest已签名的PDF文档
*@param证书链列表
*@param pk私钥
*@param算法加密算法
*@param提供者公民卡提供者
*@param子过滤器CMS
*@param签名原因
*@param位置
*@param crlList撤销列表
*@param ocspClient联机认证状态
*@param tsaClient时间戳服务器
*@param estimatedSize
*@抛出异常
*@throws-GeneralSecurityException
*/
私有静态无效符号(字符串src、字符串dest、证书[]链、私有密钥pk、字符串算法、,
字符串提供程序、PdfSigner.CryptoStandard子筛选器、字符串原因、字符串位置、集合crlList、,
IOcspClient ocspClient,其客户端tsaClient,int estimatedSize)引发IOException,GeneralSecurityException{
PdfReader读取器=新PdfReader(src);
PdfSigner signer=新PdfSigner(读取器,新文件输出流(dest),false);
PdfSignatureAppearance外观=signer.getSignatureAppearance()
.setReason(原因)
.setLocation(位置)
.SetReuseApearance(假);
矩形rect=新矩形(366482000);
外观.setPageRect(rect).setPageNumber(1);
signer.getNewSigFieldName();
IExternalSignature pks=新的私有密钥签名(pk、digestAlgorithm、provider);
IExternalDigest=新的BouncyCastleDigest();
signer.signDetached(摘要、pks、链、crlList、ocspClient、tsaClient、estimatedSize、子过滤器);
}
}
POReID()是用于与智能卡交互的库

第一次签署文档时,它可以正常工作。再次签名文档时,第一个签名无效,只有最后一个签名有效

PDF:


您的签名代码没有以追加模式打开PDF,因此在第二次签名时会更改内容,从而破坏第一次签名

要在附加模式下登录,只需更改以下行

PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), false);

构造函数中的第三个参数确定是否在追加模式中使用签名者

PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), true);