Я подписываю цифровой документ PDF с помощью iText7 и GlobalSign DSS. Я реализовал вызовы GlobalSing DSS API в необходимые классы iText. Я получаю правильные ответы сервера и могу вызвать метод pdfSigner.signDetached () со всеми необходимыми аргументами. Подписание с pdfSigner также успешно, и я получаю подписанный PDF, который выглядит хорошо с первого взгляда. Но когда я открываю подписанный pdf в Adobe Reader, он говорит мне, что цепочка доверия сертификата подписи разорвана и что он не может отследить его до CA root. Что странно, потому что это сертификат AATL и список AATL в Adobe Reader обновлен. И я не понимаю, почему это происходит. Вот что я делаю:
вызов DSS для идентификации: возвращает строку идентификатора, сертификат подписи и ответ ocsp
вызов DSS для цепочки доверия: возвращает цепочку сертификатов, используемых для
подписи сертификата подписи, вплоть до GlobalSign root вместе с
их ответами на осцилляции (за исключением root)
Я создаю массив объектов сертификата X509, содержащий подписывающий сертификат
, 2 промежуточных звена и сертификат GlobalSign root (в указанном порядке)
Я реализую IOcspClient, который использует ответ ocsp от вызова DSS для идентификации
Я реализую ITsaClient, который вызывает DSS API / timestamp / {digest}
и наконец я выполняю: pdfSigner.signDetached (externalDigest, externalSignature, chain.toArray (новый X509Certificate [] {}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CMS * 10 *;
- * 10 33 * в котором externalSignature (реализация IExternalSignature) будет вызывать идентификатор DSS / {id} / sign / {digest} API
при отладке в методе signDetached и глубже в Код pdfSigner, я четко вижу, что все сертификаты находятся в цепочке в правильном порядке. Я вижу, как они обрабатываются в классе PdfPKCS7 (однако я не знаю / точно не понимаю, что там происходит). Я вижу, что подписание происходит, никаких исключений не выдается, и в конце созданный PDF выглядит так, как будто он подписан правильно. Какой Adobe говорит, что нет.
Что мне здесь не хватает? Ответ TrustCain от de DSS API не только возвращает сертификаты из цепочки доверия сертификата подписи, но также и ответы OCSP для двух промежуточных звеньев между сертификатом подписи и GlobalSign root. Они никогда не используются. И на самом деле я тоже не знаю, что с ними делать. Могут ли это быть недостающие фрагменты для AdobeReader для восстановления цепочки доверия до GlobalSign root? И если так: как мне поместить их в этот PDF? А если нет: то, что я делаю неправильно, что нарушает эту цепочку доверия? Ответ на эти вопросы спас бы мой день :-) Вот ссылка на PDF, который покажет проблему: тестовый pdf с подписью DSS (после принятия ответа я удалил пример pdf по запросу моего клиента) Ниже приведены некоторые фрагменты кода. Центральная часть, которая собирает информацию о DSS и вызывает метод signDetached
private InputStream sign(byte[] unsignedDocument) throws IOException, DssServiceException, GeneralSecurityException {
SigningIdentity signingIdentity = signingIdentityService.getValidSigningIdentity();
DssOcspClient dssOcspClient = new DssOcspClient(signingIdentity);
TrustChainResponse trustChainResponse = digitalSigningService.getTrustChain();
List<X509Certificate> chain = new ArrayList<>();
chain.add(signingIdentity.getCertificate());
chain.addAll(trustChainResponse.getTrustChain());
IExternalDigest externalDigest = new ProviderDigest(BC_SECURITY_PROVIDER);
IExternalSignature externalSignature = new DssExternalSignature(signingIdentity.getIdentity(), digitalSigningService);
ByteArrayOutputStream signedPdfOut = new ByteArrayOutputStream();
PdfSigner pdfSigner = createPdfSigner(new ByteArrayInputStream(unsignedDocument), signedPdfOut);
pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CADES);
return new ByteArrayInputStream(signedPdfOut.toByteArray());
}
Реализация IExternalSignature
@Override
public byte[] sign(byte[] message) throws GeneralSecurityException {
MessageDigest messageDigest = new BouncyCastleDigest().getMessageDigest(DEFAULT_DIGEST_ALGORITHM);
byte[] documentHash = messageDigest.digest(message);
try {
return digitalSigningService.getSignature(signingIdentity, documentHash);
}
catch (DssServiceException e) {
LOGGER.error("error getting signature", e);
throw new GeneralSecurityException(e);
}
}
Реализация IOcspClient
@Override
public byte[] getEncoded(X509Certificate checkCert, X509Certificate issuerCert, String url) {
try {
if(Objects.equals(signingIdentity.getCertificate(), checkCert)) {
OCSPResp response = new OCSPResp(signingIdentity.getOcsp());
BasicOCSPResp basicResponse = (BasicOCSPResp)response.getResponseObject();
return basicResponse.getEncoded();
}
}
catch (CertificateException | IOException | OCSPException e) {
LOGGER.warn("OCSP validatie gefaald!", e.getMessage());
}
return null;
}
Реализация ITSAClient
@Override
public byte[] getTimeStampToken(byte[] imprint) throws Exception {
String digestAlgorithmOID = DigestAlgorithms.getAllowedDigest(DEFAULT_DIGEST_ALGORITHM);
ASN1ObjectIdentifier digestAlgOID = new ASN1ObjectIdentifier(digestAlgorithmOID);
AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DERNull.INSTANCE);
MessageImprint messageImprint = new MessageImprint(algID, imprint);
byte[] hash = messageImprint.getHashedMessage();
return digitalSigningService.getTimeStamp(hash);
}