Hash 将签名哈希集成到原始PDF中

Hash 将签名哈希集成到原始PDF中,hash,itext,digital-signature,Hash,Itext,Digital Signature,我正在将签名哈希集成到原始PDF中,但签名的有效性仍然存在错误。据说一个pdf文件在签名后被更改了 以下步骤:我计算散列,然后发送它进行签名,最后得到散列符号,然后在原始pdf中进行集成 package com.example.hashdocument; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.*; import co

我正在将签名哈希集成到原始PDF中,但签名的有效性仍然存在错误。据说一个pdf文件在签名后被更改了

以下步骤:我计算散列,然后发送它进行签名,最后得到散列符号,然后在原始pdf中进行集成

package com.example.hashdocument;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import com.lexpersona.commons.utils.ProcessLauncher;
import java.io.*;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.*;


public class Test2 {

    private static final String SRC = "B:/Hash-et-Reconstitution/tmp/Doc_test.pdf";
    private static final String DST = "B:/Hash-et-Reconstitution/tmp/Doc_test_DST.pdf";
    private static final String HASH = "B:/Hash-et-Reconstitution/tmp/Doc_test_hashed.hash";
    
    private static final String PATH_BAT = "C:/Repo_LP7/lpcommand.bat";
    private static final String PIN = "123456";
    private static final String CERTIFICATE = "C:/lp7command/tools/certificate.p12";
    private static final String SIGNED_HASH = "B:/Hash-et-Reconstitution/tmp/doc_signed.hash";
    
    private static byte[] readFileToByteArray(File file){
        FileInputStream fis = null;
        byte[] bArray = new byte[(int) file.length()];
        try{
          fis = new FileInputStream(file);
          fis.read(bArray);
          fis.close();                   
        }catch(IOException ioExp){
          ioExp.printStackTrace();
        }
        return bArray;
      }
    public static File bytesToFile(byte[] fileByte,String pathFile)  {
        File file = new File(pathFile);
        try {
            OutputStream os = new FileOutputStream(file);
            os.write(fileByte);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return file;
    }
    public static byte[] signDocument() throws IOException {
        
        
        ProcessLauncher p = new ProcessLauncher(System.out, System.err);
        int exec;
        exec = p.exec("cmd.exe /c "+PATH_BAT+" <nul "+ SIGNED_HASH +" "+ PIN+" "
                + HASH+" "+CERTIFICATE, null, null);

        byte[] signedHash = readFileToByteArray(new File(SIGNED_HASH));
        
        return signedHash;
    }
     
    
    public static void main(String[] args) throws IOException, GeneralSecurityException, DocumentException {
        
        PdfSignatureAppearance appearance = null;
        ByteArrayOutputStream  os = null;
        String hash_document = "";
        
        InputStream data = null;
        int contentEstimated = 8192;

        PdfReader reader = new PdfReader(SRC);

        reader.unethicalreading = true;
        reader.setAppendable(true);

        int pdfPagenumber = 1;

        pdfPagenumber = reader.getNumberOfPages(); // Sign on last page

        os = new ByteArrayOutputStream ();
        PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);

        Calendar cal = Calendar.getInstance();        
        appearance = stamper.getSignatureAppearance();        
        appearance.setSignDate(cal);
        //appearance.setAcro6Layers(false);
        appearance.setReason("Signature de contrat");
        appearance.setLocation("MAROC");              
        appearance.setImage(null);
        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);

        Rectangle rect = new Rectangle(300, 300, 20, 20);        
        appearance.setVisibleSignature(rect, pdfPagenumber, null);

        HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
        exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2));
        PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
        dic.setReason(appearance.getReason()); 
        dic.setLocation(appearance.getLocation());
        dic.setContact(appearance.getContact());
        dic.setDate(new PdfDate(appearance.getSignDate()));
        appearance.setCryptoDictionary(dic);

        appearance.preClose(exc);

        data = appearance.getRangeStream();    

        MessageDigest messageDigest;
        String provider = null;
        String hashAlgorithm = DigestAlgorithms.SHA256;
        if (provider == null){
            messageDigest = MessageDigest.getInstance(hashAlgorithm);
        }else {
            messageDigest = MessageDigest.getInstance(hashAlgorithm,provider);
        }


        int read = 0;
        byte[] buff = new byte[contentEstimated];

        while ((read = data.read(buff, 0, contentEstimated)) > 0)
        {
            messageDigest.update(buff,0,read);                      
        }
        byte[] hashDigest = messageDigest.digest();

        byte[] documentHash = org.bouncycastle.util.encoders.Hex.encode(hashDigest);

        //eSign Start        
        hash_document = new String(documentHash, "UTF-8");        
        System.out.println("Document Hash :"+hash_document);
        

        
        PrintStream out = new PrintStream(new FileOutputStream(HASH));
        out.print(hash_document);
        
        
        byte[] hashdocumentByte = signDocument();
        
        //////////////////// ADD SIGNED BYTES/HASH TO PDF DOCUMENT.      
        int contentEstimated2 = 8192;
        byte[] paddedSig = new byte[contentEstimated2];
        byte[] signedDocByte = hashdocumentByte;
        
        System.arraycopy(signedDocByte, 0, paddedSig, 0, signedDocByte.length);
        
        PdfDictionary dic2 = new PdfDictionary();
        dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));

        appearance.close(dic2);
        
        try(OutputStream outputStream = new FileOutputStream(DST)) {
            os.writeTo(outputStream);
        }

        os.close(); 
        
    }
}
package com.example.hashdocument;
导入com.itextpdf.text.DocumentException;
导入com.itextpdf.text.Rectangle;
导入com.itextpdf.text.pdf.*;
导入com.itextpdf.text.pdf.security.*;
导入com.lexpersona.commons.utils.ProcessLauncher;
导入java.io.*;
导入java.security.GeneralSecurityException;
导入java.security.MessageDigest;
导入java.util.*;
公共类Test2{
私有静态最终字符串SRC=“B:/Hash et restruction/tmp/Doc_test.pdf”;
私有静态最终字符串DST=“B:/Hash et restruction/tmp/Doc_test_DST.pdf”;
私有静态最终字符串HASH=“B:/HASH et restruction/tmp/Doc\u test\u HASH.HASH”;
私有静态最终字符串路径\u BAT=“C:/Repo\u LP7/lpcommand.BAT”;
专用静态最终字符串PIN=“123456”;
私有静态最终字符串CERTIFICATE=“C:/lp7command/tools/CERTIFICATE.p12”;
私有静态最终字符串签名\u HASH=“B:/HASH et restruction/tmp/doc\u SIGNED.HASH”;
专用静态字节[]readFileToByteArray(文件){
FileInputStream fis=null;
byte[]baray=新字节[(int)file.length()];
试一试{
fis=新文件输入流(文件);
财政司司长(巴雷);
fis.close();
}捕获(ioExp异常ioExp){
ioExp.printStackTrace();
}
返回营房;
}
公共静态文件bytesToFile(字节[]文件字节,字符串路径文件){
文件文件=新文件(路径文件);
试一试{
OutputStream os=新文件OutputStream(文件);
写入(文件字节);
os.close();
}捕获(例外e){
e、 printStackTrace();
}
返回文件;
}
公共静态字节[]signDocument()引发IOException{
ProcessLauncher p=新的ProcessLauncher(System.out,System.err);
int exec;

exec=p.exec(“cmd.exe/c”+PATH_BAT+”您的
signDocument
方法显然不接受预先计算的哈希值,但似乎会计算您给定数据的哈希值,在您的情况下,是已计算的哈希值的(小写)十六进制表示形式

在第一个示例文档中,您有以下值(所有哈希都是SHA256哈希):

  • 要签名的字节范围哈希:

    91A9F5EBC4F2ECEC819898824E00ECD9194C3E85E4410A3EFCF5193ED7739119
    
    79793C58489EB94A17C365445622B7F7945972A5A0BC4C93B6444BEDFFA5A5BB
    
  • “91A9F5EBC4F2ECECC8198824E00ECD9194C3E85E4410A3EFCF5193ED7739119”的哈希值。getBytes()

  • 由嵌入的签名容器签名的哈希值:

    2F37FE82F4F71770C2B33FB8787DE29627D7319EE77C6B5C48152F6E420A3242
    
    A8BCBC6F9619ECB950864BFDF41D1B5B7CD33D035AF95570C426CF4B0405949B
    
在第一个示例文档中,您有以下值(所有哈希也是SHA256哈希):

  • 要签名的字节范围哈希:

    91A9F5EBC4F2ECEC819898824E00ECD9194C3E85E4410A3EFCF5193ED7739119
    
    79793C58489EB94A17C365445622B7F7945972A5A0BC4C93B6444BEDFFA5A5BB
    
  • “79793C58489EB94A17C36544562B7F79445972A5A0BC4C93B6444BEDFA5A5BB”的哈希值。getBytes()

  • 由嵌入的签名容器签名的哈希值:

    2F37FE82F4F71770C2B33FB8787DE29627D7319EE77C6B5C48152F6E420A3242
    
    A8BCBC6F9619ECB950864BFDF41D1B5B7CD33D035AF95570C426CF4B0405949B
    

因此,您必须更正
signDocument
方法以正确解释数据,或者您必须为其提供一个包含要摘要的整个范围流的字节数组。

您如何看待此代码:首先,我计算哈希并发送到服务器a进行签名

    PdfReader reader = new PdfReader(SRC);
    FileOutputStream os = new FileOutputStream(TEMP);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
    //appearance.setCertificate(chain[0]);
    ExternalSignatureContainer external = new 
    ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    MakeSignature.signExternalContainer(appearance, external, 8192);

    InputStream inp = appearance.getRangeStream();  

    BouncyCastleDigest digest = new BouncyCastleDigest();


     byte[] hash = DigestAlgorithms.digest(inp, digest.getMessageDigest("SHA256"));
     System.out.println("hash to sign : "+ hash);
     bytesToFile(hash, HASH);
     byte[] hashdocumentByte = TEST.signed_hash(hash);
     
     PdfReader reader2 = new PdfReader(TEMP);
    FileOutputStream os2 = new FileOutputStream(DEST);
    ExternalSignatureContainer external2 = new 
    MyExternalSignatureContainer(hashdocumentByte,null);
    MakeSignature.signDeferred(reader2, "sig", os2, external2);
在服务器B中,我在其中签名哈希:

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

        // we load our private key from the key store
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(CERTIFICATE), PIN);
        String alias = (String)ks.aliases().nextElement();
        Certificate[] chain = ks.getCertificateChain(alias);
        PrivateKey pk = (PrivateKey) ks.getKey(alias, PIN);
        
        
     PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", null);
       BouncyCastleDigest digest = new BouncyCastleDigest();
        Calendar cal = Calendar.getInstance();
        String hashAlgorithm = signature.getHashAlgorithm();
        System.out.println(hashAlgorithm);
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);

        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
        byte[] extSignature = signature.sign(sh);

        System.out.println(signature.getEncryptionAlgorithm());
        sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
        return sgn.getEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);

请共享一个由您签名的PDF进行分析。这是已签名的文档:检查了一下PDF后,我认为CMS签名容器本身就可以了,它只是签署了一个与文档签名字节范围哈希不同的哈希。您是否仍然有
System.out.println(“文档哈希:”+hash\u document)的输出;
对于您共享的文档?或者您可以用代码创建另一个签名文档并共享程序输出吗?谢谢您的帮助。以下是要签名的哈希:文档哈希:79793C58489EB94A17C36544562B7F79445972A5A0BC4C93B6444BEDFA5BB,这是签名之前签名函数的输出:哈希到签名:793C58489EB94A17C365445622B7F79445972A55A0BC4C93B6444BEDFA5BB最后这是签名文档:您的输出
79793C58489EB94A17C365445622B7F79445972A55A0BC4C93B6444BEDFA5BB
是正确的。但是您的签名容器签名
A8BCC6F9619ECB950864BFDF41D1B5CD33D035AF9557026CF4B0404949B
。因此,问题不在代码中ur显示,但显然在
signDocument()中
它似乎没有对您计算的哈希值进行签名。不幸的是,我无法修改signDocument函数,否则我该怎么做?因为,当我在将签名哈希值集成到原始PDF之前检查签名哈希值时,我找到了作为签名输入事实传递的真实哈希值,我使用了一个名为LP7的编辑器中的工具。因此,我调用带有输入的jar文件并返回签名文件。我不知道确切的背景是什么好的,我不知道LP7,我只找到了一个转盘…我刚刚看到你调用的命令是一个批处理文件,
C:/Repo_LP7/lpcommand.bat
,所以你可以看一看,可能会找到更多…lpcomland.bat它是just调用一个lp7comman.jar,jar获取输入文件并签名。我的问题是我看不到背景中发生了什么,你能描述一下“MyExternalSignatureContainer”类吗