CMSSignerDigestMismatchException: when trying to digitally sign document using deferred signature

huangapple go评论83阅读模式
英文:

CMSSignerDigestMismatchException: when trying to digitally sign document using deferred signature

问题

我正在尝试使用Java itext库进行数字签名来数字签名PDF文档。

以下是我的实现细节:

  1. 首先,我检索了用于签名的证书链。
CertificateFactory cf = CertificateFactory.getInstance("X509");
Certificate myCert = cf.generateCertificate(new ByteArrayInputStream(Hex.decode(certHexStr)));
Certificate[] chain = new Certificate[] { myCert };
  1. 然后,我添加了空的签名字段。
PdfStamper stamper = PdfStamper.createSignature(reader, os, '
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
String fieldName = appearance.getFieldName();
document.setSigField(fieldName);
logger.info("Signature fieldName: " + fieldName);
appearance.setCertificate(clientCertChain[0]);

if (document.getSignReq() != null) {
	SignRequest signReq = document.getSignReq();
	if (StringUtils.isNotBlank(signReq.getReason()))
		appearance.setReason(signReq.getReason());

	if (StringUtils.isNotBlank(signReq.getLocation()))
		appearance.setLocation(signReq.getLocation());
	int pageNumber = signReq.getPageNumber() == 0 ? 1 : signReq.getPageNumber();
	Rectangle rect = new Rectangle(signReq.getLeftX(), signReq.getLeftY(), signReq.getRightX(),
			signReq.getRightY());
	logger.info(rect.toString());
	appearance.setVisibleSignature(rect, pageNumber, fieldName);
'
, null, true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); String fieldName = appearance.getFieldName(); document.setSigField(fieldName); logger.info("Signature fieldName: " + fieldName); appearance.setCertificate(clientCertChain[0]); if (document.getSignReq() != null) { SignRequest signReq = document.getSignReq(); if (StringUtils.isNotBlank(signReq.getReason())) appearance.setReason(signReq.getReason()); if (StringUtils.isNotBlank(signReq.getLocation())) appearance.setLocation(signReq.getLocation()); int pageNumber = signReq.getPageNumber() == 0 ? 1 : signReq.getPageNumber(); Rectangle rect = new Rectangle(signReq.getLeftX(), signReq.getLeftY(), signReq.getRightX(), signReq.getRightY()); logger.info(rect.toString()); appearance.setVisibleSignature(rect, pageNumber, fieldName);
  1. 然后,我创建了文档的哈希值。
String hashAlgorithm = "SHA256";
BouncyCastleDigest digest = new BouncyCastleDigest();
// 保存哈希以供后续使用
byte hash[] = DigestAlgorithms.digest(rg, digest.getMessageDigest(hashAlgorithm));

// 将哈希编码为Base64字符串
String encodedHash = Base64.getEncoder().encodeToString(hash);
document.setDigest(encodedHash);
  1. 接下来,我使用远程客户端对哈希进行了签名。
PdfPKCS7 sgn = new PdfPKCS7(null, certs, Constants.hashAlgorithm, null, bcDigest, false);
  1. 然后,我合并了签名。
byte[] signedEncoded = Base64.getDecoder().decode(document.getSignedEncoded());

logger.info("Signature fieldname: " + document.getSigField());
AcroFields af = reader.getAcroFields();

PdfDictionary v = af.getSignatureDictionary(document.getSigField());
if (v == null)
	throw new DocumentException("No field");
if (!af.signatureCoversWholeDocument(document.getSigField()))
	throw a DocumentException("Not the last signature");
PdfArray b = v.getAsArray(PdfName.BYTERANGE);
long[] gaps = b.asLongArray();
if (b.size() != 4 || gaps[0] != 0)
	throw new DocumentException("Single exclusion space supported");
RandomAccessSource readerSource = reader.getSafeFile().createSourceView();

int spaceAvailable = (int) (gaps[2] - gaps[1]) - 2;
if ((spaceAvailable & 1) != 0)
	throw new DocumentException("Gap is not a multiple of 2");
spaceAvailable /= 2;
if (spaceAvailable < signedContent.length)
	throw new DocumentException("Not enough space");
StreamUtil.CopyBytes(readerSource, 0, gaps[1] + 1, outs);
ByteBuffer bb = new ByteBuffer(spaceAvailable * 2);
for (byte bi : signedContent) {
	bb.appendHex(bi);
}
int remain = (spaceAvailable - signedContent.length) * 2;
for (int k = 0; k < remain; ++k) {
	bb.append((byte) 48);
}
bb.writeTo(outs);
StreamUtil.CopyBytes(readerSource, gaps[2] - 1, gaps[3] + 1, outs);
reader.close();
bb.close();
outs.close();

但是,当我尝试验证签名时,它显示以下错误:

CMSSignerDigestMismatchException: 在尝试使用延迟签名时,消息摘要属性值与计算的值不匹配。

而且在PDF查看器中,它说PDF在签名后已被修改。

CMSSignerDigestMismatchException: when trying to digitally sign document using deferred signature

我使用的是Java 11,bouncycastle 1.70和itext 7.0.4。

英文:

I am trying to digitially sign a PDF document using Digital signature using Java itext library.

Here are my implementation details:

  1. I first retrieved the certificate chain with which to sign.
		CertificateFactory cf = CertificateFactory.getInstance(&quot;X509&quot;);
		Certificate myCert = cf.generateCertificate(new ByteArrayInputStream(Hex.decode(certHexStr)));
		Certificate[] chain = new Certificate[] { myCert };
  1. Then I added empty signature field
PdfStamper stamper = PdfStamper.createSignature(reader, os, &#39;\0&#39;, null, true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
String fieldName = appearance.getFieldName();
document.setSigField(fieldName);
logger.info(&quot;Singnature fieldName: &quot; + fieldName);
appearance.setCertificate(clientCertChain[0]);

if (document.getSignReq() != null) {
	SignRequest signReq = document.getSignReq();
	if (StringUtils.isNotBlank(signReq.getReason()))
		appearance.setReason(signReq.getReason());

	if (StringUtils.isNotBlank(signReq.getLocation()))
		appearance.setLocation(signReq.getLocation());
	int pageNumber = signReq.getPageNumber() == 0 ? 1 : signReq.getPageNumber();
	Rectangle rect = new Rectangle(signReq.getLeftX(), signReq.getLeftY(), signReq.getRightX(),
			signReq.getRightY());
	logger.info(rect.toString());
	appearance.setVisibleSignature(rect, pageNumber, fieldName);
  1. Then I created the hash of the document
// PrivateKeySignature signature = new PrivateKeySignature(pk, &quot;SHA256&quot;,
// &quot;SunMSCAPI&quot;);
String hashAlgorithm = &quot;SHA256&quot;; // &quot;SHA-256&quot;
BouncyCastleDigest digest = new BouncyCastleDigest();
// saving the hash for using later
byte hash[] = DigestAlgorithms.digest(rg, digest.getMessageDigest(hashAlgorithm));

// logger.debug(&quot;pre-hash: &quot; + Arrays.toString(hash));
String encodedhHash = Base64.getEncoder().encodeToString(hash);
document.setDigest(encodedhHash);
  1. I then signed the digest using a remote client
PdfPKCS7 sgn = new PdfPKCS7(null, certs, Constants.hashAlgorithm, null, bcDigest, false);
  1. I then merged the signature
byte[] signedEncoded = Base64.getDecoder().decode(document.getSignedEncoded());

logger.info(&quot;Signature fieldname: &quot; + document.getSigField());
AcroFields af = reader.getAcroFields();

PdfDictionary v = af.getSignatureDictionary(document.getSigField());
if (v == null)
	throw new DocumentException(&quot;No field&quot;);
if (!af.signatureCoversWholeDocument(document.getSigField()))
	throw new DocumentException(&quot;Not the last signature&quot;);
PdfArray b = v.getAsArray(PdfName.BYTERANGE);
long[] gaps = b.asLongArray();
if (b.size() != 4 || gaps[0] != 0)
	throw new DocumentException(&quot;Single exclusion space supported&quot;);
RandomAccessSource readerSource = reader.getSafeFile().createSourceView();

int spaceAvailable = (int) (gaps[2] - gaps[1]) - 2;
if ((spaceAvailable &amp; 1) != 0)
	throw new DocumentException(&quot;Gap is not a multiple of 2&quot;);
spaceAvailable /= 2;
if (spaceAvailable &lt; signedContent.length)
	throw new DocumentException(&quot;Not enough space&quot;);
StreamUtil.CopyBytes(readerSource, 0, gaps[1] + 1, outs);
ByteBuffer bb = new ByteBuffer(spaceAvailable * 2);
for (byte bi : signedContent) {
	bb.appendHex(bi);
}
int remain = (spaceAvailable - signedContent.length) * 2;
for (int k = 0; k &lt; remain; ++k) {
	bb.append((byte) 48);
}
bb.writeTo(outs);
StreamUtil.CopyBytes(readerSource, gaps[2] - 1, gaps[3] + 1, outs);
reader.close();
bb.close();
outs.close();

But when I try to verify the signature, it says,

CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value - when trying to digitally sign document using deferred signature. 

Also in pdf viewer, it says the pdf has been modified after signature

CMSSignerDigestMismatchException: when trying to digitally sign document using deferred signature

I am using Java 11 with bouncycastle 1.70 and itext 7.0.4

答案1

得分: 1

正如https://www.rfc-editor.org/rfc/rfc8017#section-9.2所示,摘要应使用字节值(0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20(我们使用SHA256)进行填充。

byte[] sha256bytes = new byte[] { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
return ArrayUtils.addAll(sha256bytes, digest);

这应该是远程签名服务器的工作。但是,通过在摘要前面添加前缀解决了问题。

英文:

As evident in https://www.rfc-editor.org/rfc/rfc8017#section-9.2, the digest is to be padded with bytevalue (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 (we are using SHA256).

		byte[] sha256bytes = new byte[] { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03,
				0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
		return ArrayUtils.addAll(sha256bytes, digest);

It should be the job of the remote signing server. But sending digest with the prefix fixed the issue.

huangapple
  • 本文由 发表于 2023年6月6日 14:34:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76411998.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定