自签名应用 itext 5.5.11 以来,文档已被更改或损坏
Document has been altered or corruped since the Signature was applied itext 5.5.11
基本上我收到错误文档已被更改或损坏,因为应用了签名,我按照 itext 网站的示例并适应了我的情况。
- 准备待签名文档,添加追加模式,因为可以
一份已经签署的文件
- 调用网络服务对哈希进行签名
- 向准备好的文档添加了签名哈希
我运行没有想法,希望能得到一些帮助
这是我的代码片段:
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, '[=10=]',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);
}
文件如下:
original one
Signed one
编辑:
忘记添加我使用的容器:
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
}
}
试试这个
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 byte[] 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, '[=10=]',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);
InputStream inp = appearance.getRangeStream();
BouncyCastleDigest digest = new BouncyCastleDigest();
reader.close();
byte[] hash = DigestAlgorithms.digest(inp, digest.getMessageDigest("SHA256"));
return hash;
}
public byte[] signed_hash(byte[] hash, PrivateKey pk, Certificate[] chain) throws GeneralSecurityException {
PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", "SunPKCS11-eToken");
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, cal, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.sign(sh);
System.out.println(signature.getEncryptionAlgorithm());
sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
return sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CMS);
}
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 IOException, GeneralSecurityException, DocumentException {
byte[] hh = emptySignatureSVC(src, temp, "sig1", chain);
byte[] hh_sign = (app.signed_hash(hh, pk, chain));
createSignatureSVC(temp, dest, "sig1",hh_sign, chain);
}
您的代码中有两个问题:
- 你散列了错误的数据并且
- 您签名不正确。
散列错误数据
您正在像这样散列整个准备好的 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 基本上具有以下结构(阅读 here 了解更多详情):
(顺便说一句,草图不是 100% 正确的,因为签名值周围的尖括号分隔符“<”和“>”也不能被散列。)
您在 temp
准备的 PDF 具有相同的结构,只是“签名值”还不是实际的签名值,而是 8192 十六进制编码零字节的占位符(8192 因为这是您的数字放弃了 MakeSignature.signExternalContainer
).
不过,正如您在草图中看到的那样,签名值(或在您的情况下为占位符)不得经过哈希处理以进行签名。
您可以根据@LkbhaiLr 在.
中提出的MakeSignature.signExternalContainer
之后使用PdfSignatureAppearance.getRangeStream
返回的数据计算散列
或者,您可以使用(而不是 ExternalBlankSignatureContainer
)自定义 ExternalSignatureContainer
实现,它在其 sign
方法中简单地散列其 InputStream
参数并提供该散列。
我更喜欢后一种方法,但前一种方法也被广泛使用。
签名不正确
您像这样检索裸签名值:
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign
即您将属性结构按原样转发给您的签名服务,并期望它为其创建适当的签名值。
检查您的 signed example file,但是,很明显签名服务没有达到您的期望:不是使用 SHA256withRSA signing,它只是 用 RSA 和 PKCS#1 填充加密。因此,它希望您为自己完成一半的签名,即使用 SHA256 进行散列并构建 DigestInfo
结构。
因此,在调用 signWS
之前,您必须对 sgn.getAuthenticatedAttributeBytes
返回的属性结构进行哈希处理,然后将生成的哈希值包装在 DigestInfo
对象中。
当您在别处计算哈希值时,这对您来说应该不是问题。为了将散列包装在 DigestInfo
RFC 8017 section 9.2 note 1 中,提供了一个快捷方式:
For the nine hash functions mentioned in Appendix B.1, the DER encoding T of the 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
.
基本上我收到错误文档已被更改或损坏,因为应用了签名,我按照 itext 网站的示例并适应了我的情况。
- 准备待签名文档,添加追加模式,因为可以 一份已经签署的文件
- 调用网络服务对哈希进行签名
- 向准备好的文档添加了签名哈希
我运行没有想法,希望能得到一些帮助 这是我的代码片段:
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, '[=10=]',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);
}
文件如下: original one Signed one
编辑: 忘记添加我使用的容器:
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
}
}
试试这个
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 byte[] 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, '[=10=]',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);
InputStream inp = appearance.getRangeStream();
BouncyCastleDigest digest = new BouncyCastleDigest();
reader.close();
byte[] hash = DigestAlgorithms.digest(inp, digest.getMessageDigest("SHA256"));
return hash;
}
public byte[] signed_hash(byte[] hash, PrivateKey pk, Certificate[] chain) throws GeneralSecurityException {
PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", "SunPKCS11-eToken");
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, cal, null, null, CryptoStandard.CMS);
byte[] extSignature = signature.sign(sh);
System.out.println(signature.getEncryptionAlgorithm());
sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
return sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CMS);
}
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 IOException, GeneralSecurityException, DocumentException {
byte[] hh = emptySignatureSVC(src, temp, "sig1", chain);
byte[] hh_sign = (app.signed_hash(hh, pk, chain));
createSignatureSVC(temp, dest, "sig1",hh_sign, chain);
}
您的代码中有两个问题:
- 你散列了错误的数据并且
- 您签名不正确。
散列错误数据
您正在像这样散列整个准备好的 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 基本上具有以下结构(阅读 here 了解更多详情):
(顺便说一句,草图不是 100% 正确的,因为签名值周围的尖括号分隔符“<”和“>”也不能被散列。)
您在 temp
准备的 PDF 具有相同的结构,只是“签名值”还不是实际的签名值,而是 8192 十六进制编码零字节的占位符(8192 因为这是您的数字放弃了 MakeSignature.signExternalContainer
).
不过,正如您在草图中看到的那样,签名值(或在您的情况下为占位符)不得经过哈希处理以进行签名。
您可以根据@LkbhaiLr 在
MakeSignature.signExternalContainer
之后使用PdfSignatureAppearance.getRangeStream
返回的数据计算散列
或者,您可以使用(而不是 ExternalBlankSignatureContainer
)自定义 ExternalSignatureContainer
实现,它在其 sign
方法中简单地散列其 InputStream
参数并提供该散列。
我更喜欢后一种方法,但前一种方法也被广泛使用。
签名不正确
您像这样检索裸签名值:
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CADES);
byte[] extSignature = signWS(sh);//External WS to sign
即您将属性结构按原样转发给您的签名服务,并期望它为其创建适当的签名值。
检查您的 signed example file,但是,很明显签名服务没有达到您的期望:不是使用 SHA256withRSA signing,它只是 用 RSA 和 PKCS#1 填充加密。因此,它希望您为自己完成一半的签名,即使用 SHA256 进行散列并构建 DigestInfo
结构。
因此,在调用 signWS
之前,您必须对 sgn.getAuthenticatedAttributeBytes
返回的属性结构进行哈希处理,然后将生成的哈希值包装在 DigestInfo
对象中。
当您在别处计算哈希值时,这对您来说应该不是问题。为了将散列包装在 DigestInfo
RFC 8017 section 9.2 note 1 中,提供了一个快捷方式:
For the nine hash functions mentioned in Appendix B.1, the DER encoding T of the
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
.