Встраивание подписанного хэша в pdf — документ было изменено или повреждено с момента его подписания

#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 использования класса, не так ли?