#java #pdf #itext #sign #digital
Вопрос:
Я прошел через аналогичные публикации в stackoverflow, но все равно не повезло!
Я генерирую хэш-значение для pdf-файла, отправляю его на сервер подписи, возвращаю возвращенный подписанный хэш и пытаюсь прикрепить его к исходному pdf-файлу.
При проверке подписи подписанного pdf-файла в adobe Reader я получаю предупреждение «Документ был изменен или поврежден с момента его подписания«. Все остальное (личность подписавшего, отметка времени, сертификат) выглядит нормально.
Вот что я делаю (подписанный хэш хранится в hashedSignature
)
PdfReader reader = new PdfReader(src);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = PdfStamper.createSignature(reader, baos, '');
Rectangle rect = new Rectangle(400, 20, 200, 80);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(rect, 1, signatureFieldName);
appearance.setCertificate(chain[0]);
try
{
appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
appearance.setSignatureGraphic(Image.getInstance("[path to image file]");
}
catch (Exception e)
{
appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION);
}
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(appearance.getReason());
dic.setLocation(appearance.getLocation());
dic.setContact(appearance.getContact());
dic.setDate(new PdfDate(appearance.getSignDate()));
appearance.setCryptoDictionary(dic);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, Integer.valueOf(8192 * 2 2));
appearance.preClose(exc);
ExternalDigest externalDigest = new ExternalDigest()
{
public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException
{
return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
}
};
PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false);
OCSPVerifier ocspVerifier = new OCSPVerifier(null, null);
OcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
byte[] ocsp = null;
if (chain.length >= 2 amp;amp; ocspClient != null)
{
ocsp = ocspClient.getEncoded((X509Certificate) chain[0], (X509Certificate) chain[1], null);
}
ByteArrayOutputStream os = baos;
byte[] signedHash = org.apache.commons.codec.binary.Base64.decodeBase64(hashedSignature.getBytes());
sgn.setExternalDigest(signedHash, null, "RSA");
Collection<byte[]> crlBytes = null;
TSAClientBouncyCastle tsaClient = new
TSAClientBouncyCastle("http://timestamp.gdca.com.cn/tsa", null, null);
byte[] encodedSig = sgn.getEncodedPKCS7(signedHash, tsaClient, ocsp, crlBytes, MakeSignature.CryptoStandard.CMS);
byte[] paddedSig = new byte[8192];
System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);
PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
try
{
appearance.close(dic2);
}
catch (DocumentException e)
{
throw new IOException(e);
}
FileOutputStream fos = new FileOutputStream(new File(dest));
os.writeTo(fos);
Любая помощь будет признательна.
Комментарии:
1. Какой хэш вы подписываете? Вам известно, что в ходе выполнения вашего кода PDF-файл подготавливается к подписанию? Это включает в себя добавление в него некоторого содержимого, которое является частью содержимого для хэширования. Однако, по-видимому, вы хэшируете не этот подготовленный PDF-файл, а некоторые другие, не относящиеся к делу данные.
2. Хорошо, я понимаю, о чем вы говорите. Итак, могу ли я что-то сделать с существующим кодом, или мне нужно сгенерировать хэш и отправить его на подпись после подготовки pdf-файла? Спасибо
3. Во-первых, похоже, что вы используете службу подписи, которая генерирует обычные подписи RSA с заполнением PKCS1-v1.5. Таким образом, вы можете легко использовать более высокоуровневый API подписи в
MakeSignature
классе вместо ручногоPdfPKCS7
использования класса, не так ли?