Java 将SignedHash插入PDF以进行外部签名过程-WorkingSample

Java 将SignedHash插入PDF以进行外部签名过程-WorkingSample,java,pdf,itext,Java,Pdf,Itext,在电子书第4.3.3节之后“ 我试图创建一个工作示例,其中: 客户需要签署PDF文件,并且只有公共证书 外部硬件(带有私有证书)接受一个散列并返回一个签名的散列 我试着这么做,但是PDF中的签名显示文件在签名过程后被修改了 下面的代码获取原始PDF和公共证书,创建一个带有空符号的临时PDF,并返回一个哈希 该散列从外部发送到另一个远程应用程序(其中有Correspondent Private cert)并返回签名散列,我读取签名散列并将其添加到临时pdf中 完整工作代码已更新: packag

在电子书第4.3.3节之后“ 我试图创建一个工作示例,其中:

  • 客户需要签署PDF文件,并且只有公共证书
  • 外部硬件(带有私有证书)接受一个散列并返回一个签名的散列
我试着这么做,但是PDF中的签名显示文件在签名过程后被修改了

下面的代码获取原始PDF和公共证书,创建一个带有空符号的临时PDF,并返回一个哈希

该散列从外部发送到另一个远程应用程序(其中有Correspondent Private cert)并返回签名散列,我读取签名散列并将其添加到临时pdf中

完整工作代码已更新:

package com.Marloo;


import org.apache.commons.codec.Charsets;
import org.bouncycastle.util.encoders.Base64;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;

import java.io.*;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;

public class Test {

    public static final String CERT = "src/main/resources/certificate.pem";
    public static final String SRC = "src/main/resources/tmp.pdf";
    public static final String DEST = "src/main/resources/signed.pdf";

    public static void main(String args[]) throws IOException {
        getHash(SRC, CERT);
    }



    public static void getHash(String doc, String cert) throws IOException {

        try {

            File initialFile = new File(cert);
            InputStream is = new FileInputStream(initialFile);

            // We get the self-signed certificate from the client
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            Certificate[] chain = new Certificate[1];
            chain[0] = factory.generateCertificate(is);

            // we create a reader and a stamper
            PdfReader reader = new PdfReader(doc);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');

            // we create the signature appearance
            PdfSignatureAppearance sap = stamper.getSignatureAppearance();
            sap.setReason("TEST REASON");
            sap.setLocation("TEST LOCATION");
            //sap.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig"); //visible
            sap.setVisibleSignature(new Rectangle(36, 748, 36, 748), 1, "sig"); //invisible
            sap.setCertificate(chain[0]);

            // we create the signature infrastructure
            PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
            dic.setReason(sap.getReason());
            dic.setLocation(sap.getLocation());
            dic.setContact(sap.getContact());
            dic.setDate(new PdfDate(sap.getSignDate()));
            sap.setCryptoDictionary(dic);
            HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
            exc.put(PdfName.CONTENTS, new Integer(8192 * 2 + 2));
            sap.preClose(exc);
            ExternalDigest externalDigest = new ExternalDigest() {
                public MessageDigest getMessageDigest(String hashAlgorithm)
                        throws GeneralSecurityException {
                    return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
                }
            };
            PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false);
            InputStream data = sap.getRangeStream();
            byte hash[] = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));


            // we get OCSP and CRL for the cert
            OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);
            OcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
            byte[] ocsp = null;
            if (chain.length >= 2 && ocspClient != null) {
                ocsp = ocspClient.getEncoded((X509Certificate) chain[0], (X509Certificate) chain[1], null);
            }

        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);
        InputStream sh_is = new ByteArrayInputStream(sh);
        byte[] signedAttributesHash = DigestAlgorithms.digest(sh_is, externalDigest.getMessageDigest("SHA256"));


        System.out.println("----------------------------------------------");
        System.out.println("Hash to be sign:");
        System.out.println( new String(Base64.encode(signedAttributesHash), Charsets.UTF_8));
            System.out.println("----------------------------------------------");
            System.out.println("Insert b64 signed hash [ENTER]");
            System.out.println("----------------------------------------------");

            Scanner in = new Scanner(System.in);
            String signedHashB64 = in.nextLine();
            System.out.println( signedHashB64);

            ByteArrayOutputStream os = baos;

            byte[] signedHash = org.apache.commons.codec.binary.Base64.decodeBase64(signedHashB64.getBytes());

            // we complete the PDF signing process
            sgn.setExternalDigest(signedHash, null, "RSA");
            Collection<byte[]> crlBytes = null;
            TSAClientBouncyCastle tsaClient = new TSAClientBouncyCastle("http://timestamp.gdca.com.cn/tsa", null, null);

            byte[] encodedSig = sgn.getEncodedPKCS7(hash, tsaClient, ocsp, crlBytes, MakeSignature.CryptoStandard.CMS);
            byte[] paddedSig = new byte[8192];
            System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);
            PdfDictionary dic2 = new PdfDictionary();
            dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));

            try {
                sap.close(dic2);
            } catch (DocumentException e) {
                throw new IOException(e);
            }

            FileOutputStream fos = new FileOutputStream(new File(DEST));
            os.writeTo(fos);

            System.out.println("pdfsig " + System.getProperty("user.dir") + "/" + DEST);
            System.out.println("------------------End Of Life --------------------------");

            System.exit(0);


        } catch (GeneralSecurityException e) {
            throw new IOException(e);
        } catch (DocumentException e) {
            throw new IOException(e);
        }

    }


}
package com.Marloo;
导入org.apache.commons.codec.charset;
导入org.bouncycastle.util.encoders.Base64;
导入com.itextpdf.text.DocumentException;
导入com.itextpdf.text.Rectangle;
导入com.itextpdf.text.pdf.*;
导入com.itextpdf.text.pdf.security.*;
导入java.io.*;
导入java.security.GeneralSecurityException;
导入java.security.MessageDigest;
导入java.security.cert.Certificate;
导入java.security.cert.CertificateFactory;
导入java.security.cert.x509证书;
导入java.util.*;
公开课考试{
公共静态最终字符串CERT=“src/main/resources/certificate.pem”;
公共静态最终字符串SRC=“SRC/main/resources/tmp.pdf”;
公共静态最终字符串DEST=“src/main/resources/signed.pdf”;
公共静态void main(字符串args[])引发IOException{
getHash(SRC,CERT);
}
公共静态void getHash(字符串文档、字符串证书)引发IOException{
试一试{
文件初始文件=新文件(证书);
InputStream is=新文件InputStream(初始文件);
//我们从客户端获得自签名证书
CertificateFactory=CertificateFactory.getInstance(“X.509”);
证书[]链=新证书[1];
链[0]=工厂生成证书(is);
//我们创建了一个读卡器和一个压模
PDF阅读器=新的PDF阅读器(文档);
ByteArrayOutputStream bas=新的ByteArrayOutputStream();
PdfStamper stamper=PdfStamper.createSignature(读取器,BAS,'\0');
//我们创建签名外观
PdfSignatureAppearance sap=stamper.getSignatureAppearance();
sap.setReason(“测试原因”);
sap.设置位置(“测试位置”);
//setVisibleSignature(新矩形(36748144780),1,“sig”);//可见
setVisibleSignature(新矩形(36748,36748),1,“sig”);//不可见
sap.setCertificate(链[0]);
//我们创建签名基础设施
PdfSignature dic=新的PdfSignature(PdfName.ADOBE_PPKLITE,PdfName.ADBE_PKCS7_);
dic.setReason(sap.getReason());
dic.setLocation(sap.getLocation());
dic.setContact(sap.getContact());
dic.setDate(新的PdfDate(sap.getSignDate());
sap.setCryptoDictionary(dic);
HashMap exc=新的HashMap();
exc.put(PdfName.CONTENTS,新整数(8192*2+2));
sap.preClose(exc);
ExternalDigest ExternalDigest=新的ExternalDigest(){
public MessageDigest getMessageDigest(字符串哈希算法)
抛出一般安全性异常{
返回DigestAlgorithms.getMessageDigest(hashAlgorithm,null);
}
};
PdfPKCS7 sgn=新的PdfPKCS7(空,链,“SHA256”,空,外部摘要,假);
InputStream data=sap.getRangeStream();
字节哈希[]=DigestAlgorithms.digest(数据,externalDigest.getMessageDigest(“SHA256”);
//我们得到OCSP和CRL的证书
OCSPVerifier OCSPVerifier=新的OCSPVerifier(null,null);
OcspClient OcspClient=新的OcspClientBouncyCastle(ocspVerifier);
字节[]ocsp=null;
如果(chain.length>=2&&ocspClient!=null){
ocsp=ocspClient.getEncoded((X509Certificate)链[0],(X509Certificate)链[1],null);
}
字节[]sh=sgn.getAuthenticatedAttributeBytes(哈希、null、null、MakeSignature.CryptoStandard.CMS);
InputStream sh_是=新的ByteArrayInputStream(sh);
字节[]signedAttributesHash=DigestAlgorithms.digest(sh_是externalDigest.getMessageDigest(“SHA256”);
System.out.println(“-------------------------------------------------------------”;
System.out.println(“要签名的哈希:”);
System.out.println(新字符串(Base64.encode(signedAttributesHash),Charsets.UTF_8));
System.out.println(“-------------------------------------------------------------”;
System.out.println(“插入b64签名哈希[ENTER]”;
System.out.println(“-------------------------------------------------------------”;
扫描仪输入=新扫描仪(系统输入);
字符串signedHashB64=in.nextLine();
System.out.println(signedHashB64);
ByteArrayOutputStream os=baos;
byte[]signedHash=org.apache.commons.codec.binary.Base64.decodeBase64(signedHashB64.getBytes());
//我们完成了PDF签名过程
sgn.setExternalDigest(signedHash,null,“RSA”);
集合crlBytes=null;
TSAClientBouncyCastle tsaClient=新的TSAClientBouncyCastle(“http://timestamp.gdca.com.cn/tsa“,空,空);
字节[]encodedSig=sgn.getEncodedPKCS7(散列、tsaClient、ocsp、crlBytes、MakeSignature.CryptoStandard.CMS);
字节[]paddedSig=新字节[8192];
System.arraycopy(encodedSig,0,paddedSig,0,encodedSig.length);
PdfDictionary dic2=新的PdfDictionary();
dic2.put(PdfName.CONTENTS,新的PdfString(paddedSig).setHexWriting
InputStream data = sap.getRangeStream();
byte hash[] = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocsp, null, MakeSignature.CryptoStandard.CMS);
byte[] last32 = Arrays.copyOfRange(sh, sh.length - 32, sh.length);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocsp, null, MakeSignature.CryptoStandard.CMS);
byte[] signedAttributesHash = DigestAlgorithms.digest(new ByteArrayInputStream(sh), externalDigest.getMessageDigest("SHA256"));