基本上,我得到的错误文件已被修改或波纹,因为签名被应用,我遵循图文网站的例子,并适应我的情况。
我的想法用完了,希望能得到一些帮助--以下是我的代码片段:
public static final String src = "DynamicClient\sample.pdf";
public static final String temp= "DynamicClient\sampleTEMP.pdf";
public static final String dest= "DynamicClient\sampleFINAL.pdf";
public static void emptySignatureSVC(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
reader.setAppendable(true);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0',null,true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(0, 0, 0, 0), 1, fieldname);
appearance.setCertificate(chain[0]);
ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ETSI_CADES_DETACHED);
MakeSignature.signExternalContainer(appearance, external, 8192);
reader.close();
}
public static void createSignatureSVC(String src, String dest, String fieldname, byte[] sign, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
ExternalSignatureContainer external = new MyExternalSignatureContainer(sign, chain);
MakeSignature.signDeferred(reader, fieldname, os, external);
}
public static void main(String[] args) throws Exception {
Certificate[] chain = signWS.getChain();//External WS to get chain
emptySignatureSVC(src, temp, "signature", chain);
InputStream is = new FileInputStream(new File(temp));
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest("SHA256"));
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign
sgn.setExternalDigest(extSignature, null, "RSA");
createSignatureSVC(temp, dest, "signature", sgn.getEncodedPKCS7(hash,null, null, null, CryptoStandard.CADES), chain);
}编辑:忘记添加我使用的容器:
class MyExternalSignatureContainer implements ExternalSignatureContainer {
protected byte[] sig;
protected Certificate[] chain;
public MyExternalSignatureContainer(byte[] sig,Certificate[] chain) {
this.sig = sig;
this.chain=chain;
}
public byte[] sign(InputStream is)throws GeneralSecurityException {
return sig;
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
// TODO Auto-generated method stub
}
}发布于 2021-03-24 14:38:00
代码中有两个问题:
散列错误的数据
您正在散列所有准备好的PDF,如下所示:
InputStream is = new FileInputStream(new File(temp));
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, digest, false);
byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest("SHA256"));这不对。
签名的PDF实际上具有此结构(请阅读这里以获得更多详细信息):

(顺便说一下,草图不是100%正确的,因为签名值周围的角括号分隔符'<‘’和'>‘’也不能散列.)
您在temp准备的PDF格式具有相同的结构,只是“签名值”还不是实际的签名值,而是8192个十六进制编码的零字节的占位符(8192,因为这是您在MakeSignature.signExternalContainer中给出的数字)。
但是,正如您在草图中所看到的,签名值(或者在您的情况下,占位符)不能被散列以进行签名。
您可以使用PdfSignatureAppearance.getRangeStream在他的回答中的@LkbhaiLr建议的MakeSignature.signExternalContainer之后返回的数据来计算哈希。
或者,您可以使用(而不是ExternalBlankSignatureContainer)一个自定义ExternalSignatureContainer实现,它在其sign方法中简单地散列其InputStream参数并提供该哈希。
我更喜欢后一种方法,但前一种方法也被广泛使用。
签名错误
您可以像这样检索裸露签名值:
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign也就是说,您将属性结构转发给您的签名服务,并期望它为它创建一个适当的签名值。
检查一下您的签名示例文件,很明显,签名服务没有达到您的期望:它只是使用SHA256withRSA和PKCS#1填充进行加密,而不是使用SHA256withRSA进行签名。因此,它希望您完成一半的签名,即使用SHA256进行散列并构建一个DigestInfo结构。
因此,在调用signWS之前,您必须对sgn.getAuthenticatedAttributeBytes返回的属性结构进行哈希,然后将得到的哈希值包装到DigestInfo对象中。
当你在其他地方计算散列时,这对你来说不应该是个问题。对于在DigestInfo中包装散列,RFC 8017节9.2附注1提供了一个捷径:
对于附录B.1中提到的九个哈希函数,
DigestInfo值的DER编码T等于以下内容: ..。沙-256:(0x)30 31 30 d 06 09 60 86 48 01 65 03 04 02 01 05 00 00 00 04 20 x.
也就是说,您只需在哈希前面加上字节序列30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20。
https://stackoverflow.com/questions/66770181
复制相似问题