我正在try 使用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, '\0', null, true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
String fieldName = appearance.getFieldName();
document.setSigField(fieldName);
logger.info("Singnature 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. 然后,我创建了文档的散列
// PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256",
// "SunMSCAPI");
String hash算法rithm = "SHA256"; // "SHA-256"
BouncyCastleDigest digest = new BouncyCastleDigest();
// saving the hash for using later
byte hash[] = Digest算法rithms.digest(rg, digest.getMessageDigest(hash算法rithm));

// logger.debug("pre-hash: " + Arrays.toString(hash));
String encodedhHash = Base64.getEncoder().encodeToString(hash);
document.setDigest(encodedhHash);
  1. 然后,我使用远程客户端对摘要进行签名
PdfPKCS7 sgn = new PdfPKCS7(null, certs, Constants.hash算法rithm, 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 new 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: message-digest attribute value does not match calculated value - when trying to digitally sign document using deferred signature. 

同样在pdf查看器中,它说pdf在签名后已被修改

Adobe reader

我使用的是带有bouncyCastle 1.70和itext 7.0.4的Java 11

推荐答案

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);

这应该是远程签名服务器的工作.但发送带有前缀的摘要解决了这个问题.

Java相关问答推荐

Spring bootstrap @ Asmat注释与@ Routed

int Array Stream System. out. print方法在打印Java8时在末尾添加% sign

Javascript更新alert 可扩展内容样式与CSS—按钮更多/更少

空手道比赛条件

如何粘合(合并)文件Lucene?

Select 按位运算序列

@org.springframework.beans.factory.annotation.Autowired(required=true)-注入点有以下注释:-SpringBoot

嵌入式ActiveMQ Artemis Web控制台加载错误

使用Testcontainers与OpenLiberty Server进行集成测试会抛出SocketException

调用引发泛型异常的泛型方法时出现编译错误

更新GWT 2.5.1到2.11.0和sencha GXT 3.1.1到4.1时出现错误

使用GridBagLayout正确渲染

我可以在MacOS上使用什么Java函数来在适当的设备上以适当的音量播放适当的alert 声音?

对字符串长度进行排序,但颠倒了顺序(最长字符串在前)

声明MessageChannel Bean的首选方式

如何在我的世界中为互动增加冷却时间?

Spring动态反序列化JSON可以是列表,也可以只是一个对象

深度优先搜索实现:算法只向右搜索

使用原子整数的共享计数器并发增量

OpenAPI Maven插件生成错误的Java接口名称