自签名于5.5.11开始应用以来,文档已被更改或更改. [英] Document has been altered or corruped since the Signature was applied itext 5.5.11
问题描述
基本上,我得到的错误是自从应用签名以来,文档已被更改或更改,我遵循了itext网站的示例并适应了我的情况.
- 准备要签名的文档,添加附加模式,因为它可以已经签名的文件
- 调用了Web服务对哈希进行签名
- 将签名的哈希添加到准备好的文档中
我用尽了所有想法,希望能得到一些帮助这是我的代码段:
公共静态最终字符串src ="DynamicClient \ sample.pdf";公共静态最终String temp ="DynamicClient \ sampleTEMP.pdf";公共静态最终字符串dest ="DynamicClient \ sampleFINAL.pdf";public static void emptySignatureSVC(String src,String dest,String fieldname,Certificate []链)引发IOException,DocumentException,GeneralSecurityException {PdfReader reader =新的PdfReader(src);reader.setAppendable(true);FileOutputStream os = new FileOutputStream(dest);PdfStamper压模= PdfStamper.createSignature(reader,os,'\ 0',null,true);PdfSignatureAppearance外观= stamper.getSignatureAppearance();Appearance.setVisibleSignature(new Rectangle(0,0,0,0),1,字段名称);Appearance.setCertificate(chain [0]);ExternalSignatureContainer external =新的ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE,PdfName.ETSI_CADES_DETACHED);MakeSignature.signExternalContainer(appearance,external,8192);reader.close();}公共静态无效createSignatureSVC(字符串src,字符串目标,字符串字段名,字节[]符号,证书[]链)引发IOException,DocumentException,GeneralSecurityException {PdfReader reader =新的PdfReader(src);FileOutputStream os = new FileOutputStream(dest);ExternalSignatureContainer external =新的MyExternalSignatureContainer(sign,chain);MakeSignature.signDeferred(阅读器,字段名,操作系统,外部);}公共静态void main(String [] args)引发异常{Certificate []链= signWS.getChain();//要获得链的外部WSemptySignatureSVC(src,temp,"signature",chain);InputStream是= new FileInputStream(new File(temp));PdfPKCS7 sgn =新的PdfPKCS7(null,链,"SHA256",null,摘要,false);byte [] hash = DigestAlgorithms.digest(是,digest.getMessageDigest("SHA256"));byte [] sh = sgn.getAuthenticatedAttributeBytes(hash,null,null,CryptoStandard.CADES);byte [] extSignature = signWS(sh);//要签名的外部WSsgn.setExternalDigest(extSignature,null,"RSA");createSignatureSVC(temp,dest,"signature",sgn.getEncodedPKCS7(hash,null,null,null,CryptoStandard.CADES),链);}
,这是文件:
(顺便说一句,草图不是100%正确,因为签名值周围的尖括号定界符'<'和'>'也不能被散列.)> 您在 但是,如您在草图中所见,签名值(或者在您的情况下为占位符)一定不能散列用于签名. 您可以使用@LkbhaiLr在 或者,您可以使用(而不是 我更喜欢后一种方法,但是前一种方法也得到了广泛使用. 您可以像这样检索裸签名值: 即您可以将属性结构原样转发给签名服务,并期望它为它创建一个合适的签名值. 检查您的签名的示例文件,但是很明显,签名服务没有达到您的期望:与使用SHA256withRSA进行签名不同,它仅通过RSA和PKCS#1填充进行加密.因此,它希望您为您完成一半的签名,即使用SHA256进行哈希处理并构建 因此,在调用 在其他地方计算哈希时,这对您来说不是问题.对于将哈希值包装在 对于附录B.1中提到的九种哈希函数, 即您只需用字节序列 Basically I get the error Document has been altered or corruped since the Signature was applied, I followed the example of itext website and adapted to my situation. I ran out of ideas and hope I can get some help
here's a snippet of my code: and here are the files:
original one
Signed one EDIT:
Forgot to add the container I use:
There are two issues in your code: You are hashing the whole prepared PDF like this: This is not correct. A signed PDF essentially has this structure (read here for more details): (By the way, the sketch is not 100% correct as the angled bracket delimiters '<' and '>' around the signature value must also not be hashed.) Your prepared PDF at As you can see in the sketch, though, the signature value (or in your case, the placeholder) must not be hashed for signing. You can calculate the hash using the data returned by Alternatively you can use (instead of the I'd prefer the latter approach but the former approach also is in wide use. You retrieve the naked signature value like this: I.e. you forward the attributes structure as is to your signing service and expect it to create a proper signature value for it. Inspecting your signed example file, though, it becomes clear that that signing service does not live up to your expectations: Instead of signing with SHA256withRSA, it merely encrypts with RSA and PKCS#1 padding. Thus, it expects you to do half the signing for you, i.e. hashing with SHA256 and building a Thus, before calling As you calculate hashes elsewhere, that shouldn't be an issue for you. And for wrapping a hash in a For the nine hash functions mentioned in Appendix B.1, the DER encoding T of the
I.e. you only have to prefix your hash with the byte sequence 这篇关于自签名于5.5.11开始应用以来,文档已被更改或更改.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! temp
下准备的PDF具有相同的结构,只是签名值"还不是一个实际的签名值,而是一个占位符,为8192个十六进制编码的零字节(8192,因为这是您在 MakeSignature.signExternalContainer
中输入的数字). PdfSignatureAppearance.getRangeStream
返回的数据来计算哈希.stackoverflow.com/a/66776866/1729265>他的答案. ExternalBlankSignatureContainer
)自定义的 ExternalSignatureContainer
实现,该实现在其 sign
方法中只是对 InputStream进行哈希处理
参数并提供该哈希值.签名不正确
byte [] sh = sgn.getAuthenticatedAttributeBytes(hash,null,null,CryptoStandard.CADES);byte [] extSignature = signWS(sh);//要签名的外部WS
DigestInfo
结构. signWS
之前,您必须对 sgn.getAuthenticatedAttributeBytes
返回的属性结构进行哈希处理,然后将所得的哈希值包装在 DigestInfo
对象. DigestInfo
RFC 8017 9.2节中,请注意1 提供了一个捷径: DigestInfo
值的DER编码T等于以下值:
...SHA-256:(0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||H....
30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
作为哈希前缀即可.
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
}
}
Hashing the wrong data
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"));
temp
has the same structure, merely the "signature value" is not yet an actual signature value but instead a placeholder of 8192 hex-encoded zero bytes (8192 because that's the number you gave in MakeSignature.signExternalContainer
).PdfSignatureAppearance.getRangeStream
after the MakeSignature.signExternalContainer
as proposed by @LkbhaiLr in his answer.ExternalBlankSignatureContainer
) a custom ExternalSignatureContainer
implementation which in its sign
method simply hashes its InputStream
parameter and provides that hash.Signing incorrectly
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign
DigestInfo
structure.signWS
you have to hash that attributes structure returned by sgn.getAuthenticatedAttributeBytes
and then wrap the resulting hash value in a DigestInfo
object.DigestInfo
RFC 8017 section 9.2 note 1 offers a short cut:
DigestInfo
value is equal to the following:...
SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.
...
30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
.