我正在try 使用Java itext库使用数字签名来对PDF文档进行数字签名.
以下是我的实现细节:
- 我首先检索了要用来签名的证书链.
CertificateFactory cf = CertificateFactory.getInstance("X509");
Certificate myCert = cf.generateCertificate(new ByteArrayInputStream(Hex.decode(certHexStr)));
Certificate[] chain = new Certificate[] { myCert };
- 然后我添加了空的签名域
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);
- 然后,我创建了文档的散列
// 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);
- 然后,我使用远程客户端对摘要进行签名
PdfPKCS7 sgn = new PdfPKCS7(null, certs, Constants.hash算法rithm, null, bcDigest, false);
- 然后我合并了签名
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在签名后已被修改
我使用的是带有bouncyCastle 1.70和itext 7.0.4的Java 11