import com.itextpdf.kernel.pdf.*;
import com.itextpdf.signatures.ExternalBlankSignatureContainer;
import com.itextpdf.signatures.IExternalSignatureContainer;
import com.itextpdf.signatures.PdfSigner;
import java.io.*;
import java.security.MessageDigest;
import java.util.Base64;
public class PrepareHash {
public static void main(String[] args) throws Exception {
if (args.length != 3) {
System.err.println("Usage: java PrepareHash input.pdf output_hash.txt output_prepared.pdf");
System.exit(1);
}
String inputPdf = args[0];
String outputHash = args[1];
String outputPreparedPdf = args[2];
File input = new File(inputPdf);
if (!input.exists() || !input.isFile()) {
System.err.println("ERROR: Input PDF does not exist at: " + inputPdf);
return;
}
System.out.println("Input PDF found at: " + inputPdf);
PdfReader reader = new PdfReader(inputPdf);
PdfSigner signer = new PdfSigner(reader, new FileOutputStream(outputPreparedPdf), new StampingProperties().useAppendMode());
signer.setFieldName("Signature1");
signer.getSignatureAppearance()
.setReason("Swisscom QES")
.setLocation("DE")
.setReuseAppearance(false)
.setPageNumber(1)
.setPageRect(new Rectangle(0, 0, 0, 0)); // invisible
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite, PdfName.ETSI_CAdES_DETACHED);
signer.signExternalContainer(external, 32000);
signer.getDocument().close(); // finalize writing
// Compute SHA-256 hash of prepared PDF
MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (InputStream is = new FileInputStream(outputPreparedPdf)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
}
}
String hashBase64 = Base64.getEncoder().encodeToString(digest.digest());
try (FileWriter writer = new FileWriter(outputHash)) {
writer.write(hashBase64);
}
System.out.println("Prepared PDF written to: " + outputPreparedPdf);
System.out.println("SHA-256 hash (Base64): " + hashBase64);
}
}
< /code>
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.signatures.IExternalSignatureContainer;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.json.JSONObject;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Base64;
public class SignQES {
public static void main(String[] args) {
if (args.length < 6) {
System.err.println("Usage: java SignQES ");
System.exit(1);
}
String inputPdf = args[0];
String outputPdf = args[1];
String signatureCmsPath = args[2];
String reason = args[3];
JSONObject qesJson = new JSONObject(args[4]);
String expectedHash = args[5];
Security.addProvider(new BouncyCastleProvider());
try {
// Read the CMS signature
byte[] signatureBytes = Files.readAllBytes(Paths.get(signatureCmsPath));
// Read the prepared PDF to verify its hash matches
byte[] fileBytes = Files.readAllBytes(Paths.get(inputPdf));
String actualHash = Base64.getEncoder().encodeToString(
MessageDigest.getInstance("SHA-256").digest(fileBytes)
);
System.out.println("Java hash before signing: " + actualHash);
System.out.println("Expected hash: " + expectedHash);
if (!actualHash.equals(expectedHash)) {
throw new RuntimeException("Hash mismatch! Expected: " + expectedHash + ", Got: " + actualHash);
}
// Reopen the prepared PDF to inject the actual signature
PdfReader reader = new PdfReader(inputPdf);
reader.setUnethicalReading(true); // needed if pre-signed with external tool
PdfDocument doc = new PdfDocument(reader);
FileOutputStream finalOutput = new FileOutputStream(outputPdf);
// Signature container that returns the Swisscom CMS
IExternalSignatureContainer realContainer = new IExternalSignatureContainer() {
@Override
public byte[] sign(InputStream data) {
return signatureBytes;
}
@Override
public void modifySigningDictionary(PdfDictionary signDic) {
// Already prepared, nothing to modify here
}
};
// Inject the signature into the pre-allocated space
PdfSigner.signDeferred(doc, "Signature1", finalOutput, realContainer);
System.out.println("QES signature written to: " + outputPdf);
} catch (Exception e) {
System.err.println(" Signing failed: " + e.getMessage());
e.printStackTrace();
System.exit(2);
}
}
}
< /code>
Ich habe es ausprobiert: < /p>
- Verifiziert useAppendMode () wird während der Vorbereitung und Signierung eingestellt. /> < /li>
Verwendet dasselbe output_prepared.pdf für Hashing und Signierung. Nach der Verarbeitung findet auf dem PDF zwischen Hashing und Signierung statt. /> < /li>
< /ul>
Frage (s): < /p>
Was könnte dazu führen, dass der SHA-256-Hash des vorbereiteten PDF zwischen dem anfänglichen Hashing und dem endgültigen Signiervertrag ändert. Durch Byterange-ausgeschlossene Teile des PDF, ähnlich wie Ittext es erwartet? /> iText 7.2.5 < /p>
< /li>
BouncyCastle 1.70 < /p>
< /li>
Swisscom QES -API (ETSI -Format)
Alle Ideen oder Arbeitsbeispiele wären zutiefst geschätzt!