Я использую PDFBox 2.0.34, и я пытаюсь подписать PDF, используя внешний API, который возвращает подпись в формате Der. < /p>
Вот мой код: < /p>
private static String signPdf(String inputPdfPath, String outputPdfPath, String reason, String location,
SignatureDetails signatureDetails, String sessionId)
throws IOException, GeneralSecurityException, Exception {
File inputFile = new File(inputPdfPath);
File outputFile = new File(outputPdfPath);
X509Certificate signerCertificate;
try {
if (signatureDetails == null || signatureDetails.getCertificate() == null
|| signatureDetails.getCertificate().isEmpty()) {
throw new GeneralSecurityException("Signer certificate details missing from the first API response.");
}
byte[] certBytes = Base64.decodeBase64(signatureDetails.getCertificate());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509",
BouncyCastleProvider.PROVIDER_NAME);
try (InputStream certInputStream = new ByteArrayInputStream(certBytes)) {
signerCertificate = (X509Certificate) certFactory.generateCertificate(certInputStream);
}
signerCertificate.checkValidity();
System.out.println(" Successfully loaded signer certificate provided by API.");
System.out.println(" Cert Subject: " + signerCertificate.getSubjectX500Principal());
System.out.println(" Cert Issuer: " + signerCertificate.getIssuerX500Principal());
System.out.println(" Cert Valid Until: " + signerCertificate.getNotAfter());
} catch (CertificateException | IllegalArgumentException e) {
System.err.println(" Error decoding/parsing signer certificate from API details: " + e.getMessage());
throw new GeneralSecurityException("Failed to process signer certificate from API.", e);
}
System.out.println(" Loading base PDF document for signing: " + inputFile.getAbsolutePath());
try (PDDocument document = PDDocument.load(inputFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
System.out.println(" Base PDF loaded.");
PDSignature signatureDict = new PDSignature();
signatureDict.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signatureDict.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signatureDict.setName("Ahmad Bin Abu");
signatureDict.setLocation(location);
signatureDict.setReason(reason);
signatureDict.setSignDate(Calendar.getInstance());
System.out.println(" Adding signature dictionary structure to the document...");
document.addSignature(signatureDict);
System.out.println(" Saving signed document incrementally (this triggers the actual signing call)...");
ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(baos);
InputStream content = externalSigning.getContent();
byte[] contentBytes = IOUtils.toByteArray(content);
String hashHex = DigestUtils.sha256Hex(contentBytes);
String signedData2 = signExternal(sessionId, hashHex);
System.out.println("signedData2:" + signedData2);
byte[] derSignature = Base64.decodeBase64(signedData2);
ContentSigner contentSigner2 = new ContentSigner() {
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@Override
public OutputStream getOutputStream() {
return outputStream;
}
@Override
public byte[] getSignature() {
return derSignature;
}
@Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withECDSA");
}
};
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
JcaCertStore certStore = new JcaCertStore(Collections.singletonList(signerCertificate));
gen.addCertificates(certStore);
DigestCalculatorProvider digestProvider = new JcaDigestCalculatorProviderBuilder()
.setProvider(new BouncyCastleProvider()).build();
JcaX509CertificateHolder jcaX509CertificateHolder = new JcaX509CertificateHolder(signerCertificate);
SignerInfoGenerator signerInfoGen = new JcaSignerInfoGeneratorBuilder(digestProvider).build(contentSigner2,
jcaX509CertificateHolder);
gen.addSignerInfoGenerator(signerInfoGen);
CMSTypedData msg = new CMSProcessableByteArray(contentBytes);
CMSSignedData signedData = gen.generate(msg, false);
externalSigning.setSignature(signedData.getEncoded());
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
baos.writeTo(fos);
}
return outputPdfPath;
}
}
< /code>
Когда я открываю PDF внутри Adobe Reader, подпись недействительна, а сообщение об ошибке - «документ был изменен или поврежден с момента применения подписи». Каким-то образом, сертификат, по-видимому, правильно вставлен, и я подозреваю, что хэш документа недействителен.
[ https://github.com/apache/pdfbox/blob/t ... .java#l134, но, похоже, их содержимое, что требует подрядного ключа, который я не храню. Signexternal () возвращает кодированную базовую сигнатуру ECDSA Base-64 (пример: Meuciqdhbx5csjbx1pd618qa7kv/5uhj2tzxq2rmbkycsqrbdqigi6louynhajzsuaroh+ghhvlpaa5sq9a9spmlfynat7i =)
вот выборка. Оценка, если кто -то может указать мне в правильном направлении. Заранее спасибо!
Подробнее здесь: https://stackoverflow.com/questions/796 ... d-or-corru