Sign PDF mit einem externen Dienst und iText

Ich habe dieses Szenario.

Ich habe eine Anwendung, die ein PDF generiert und signiert werden muss.

Wir verfügen nicht über die Zertifikate, um das Dokument zu signieren, da sie sich in einem HSM befinden und die Zertifikate nur über einen Webservice verwendet werden können.

Dieser Webservice bietet zwei Optionen, sendet das PDF-Dokument und gibt ein signiertes PDF zurück oder sendet einen zu signierenden Hash.

Die erste Option ist nicht möglich, da die PDF-Datei ohne Zeitstempel signiert ist (dies ist eine sehr wichtige Voraussetzung). Daher wird die zweite Option ausgewählt.

Dies ist unser Code. Zuerst erhalten wir das Erscheinungsbild der Signatur und berechnen den Hash:

PdfReader reader = new PdfReader(Base64.decode(pdfB64));
reader.setAppendable(true);
baos = new ByteArrayOutputStream();

PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0', null, true);
appearance = stamper.getSignatureAppearance();
appearance.setCrypto(null, chain, null, PdfSignatureAppearance.SELF_SIGNED);
appearance.setVisibleSignature("Representant");
cal = Calendar.getInstance();
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.TYPE, PdfName.SIG);
dic.put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
dic.put(PdfName.SUBFILTER, new PdfName("adbe.pkcs7.detached"));
dic.put(PdfName.M, new PdfDate(cal));
appearance.setCryptoDictionary(dic);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, Integer.valueOf(reservedSpace.intValue() * 2 + 2));
appearance.setCertificationLevel(1);
appearance.preClose(exc);

AbstractChecksum checksum = JacksumAPI.getChecksumInstance("sha1");
checksum.reset();
checksum.update(Utils.streamToByteArray(appearance.getRangeStream()));
hash = checksum.getByteArray();

An dieser Stelle haben wir den Hash-Code des Dokuments. Dann senden wir den Hash an den Webservice und erhalten den signierten Hash-Code.

Zum Schluss setzen wir den signierten Hash in das PDF:

byte[] paddedSig = new byte[reservedSpace.intValue()];
System.arraycopy(signedHash, 0, paddedSig, 0, signedHash.length);

PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
appearance.close(dic);

byte[] pdf = baos.toByteArray();

An dieser Stelle wird ein PDF mit einer ungültigen Signatur signiert. Laut Adobe wurde das Dokument seit seiner Unterzeichnung geändert oder beschädigt.

Ich denke, dass wir dabei etwas falsch machen, und wir wissen nicht genau, woran es liegen könnte.

Wir freuen uns über Hilfe oder eine alternative Möglichkeit, dies zu tun.

Vielen Dank

EDITED

Wie von mkl vorgeschlagen, habe ich den Abschnitt 4.3.3 dieses Buches befolgtDigitale Signaturen für PDF-Dokumente, und mein Code nun was folgt:

Der erste Teil, wenn wir den Hash berechnen:

PdfReader reader = new PdfReader(Base64.decode(pdfB64));
reader.setAppendable(true);
baos = new ByteArrayOutputStream();

PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');
appearance = stamper.getSignatureAppearance();

appearance.setReason("Test");
appearance.setLocation("A casa de la caputeta");
appearance.setVisibleSignature("TMAQ-TSR[0].Pagina1[0].DadesSignatura[0].Representant[0]");
appearance.setCertificate(chain[0]);

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, new Integer(reservedSpace.intValue() * 2 + 2));
appearance.preClose(exc);

ExternalDigest externalDigest = new ExternalDigest()
{
    public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException
    {
        return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
    }
};

sgn = new PdfPKCS7(null, chain, "SHA256", null, externalDigest, false);
InputStream data = appearance.getRangeStream();
hash = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));
cal = Calendar.getInstance();

byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, cal, null, null, CryptoStandard.CMS);
sh = MessageDigest.getInstance("SHA256", "BC").digest(sh);

hashPdf = new String(Base64.encode(sh));

Und im zweiten Teil erhalten wir den signierten Hash und fügen diesen in das PDF ein:

sgn.setExternalDigest(Base64.decode(hashSignat), null, "RSA");
byte[] encodedSign = sgn.getEncodedPKCS7(hash, cal, null, null, null, CryptoStandard.CMS);
byte[] paddedSig = new byte[reservedSpace.intValue()];
System.arraycopy(encodedSign, 0, paddedSig, 0, encodedSign.length);

PdfDictionary dic2 = new PdfDictionary();
dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));

appearance.close(dic2);

byte[] pdf = baos.toByteArray();

Jetzt löst Adobe einen internen Fehler in der Kryptografiebibliothek aus. Fehlercode: 0x2726, wenn wir versuchen, die Signatur zu validieren.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage