使用 GlobalSign DSS AATL 证书的 iText7 pdf 签名显示 Adobe 中的信任链已损坏 Reader
iText7 pdf signing with GlobalSign DSS AATL certificate shows broken trustchain in Adobe Reader
我正在使用 iText7 和 GlobalSign DSS 对 PDF 进行数字签名。我将 GlobalSing DSS API 调用实现到必要的 iText classes 中。我得到了正确的服务器响应,并且能够使用所有需要的参数调用 pdfSigner.signDetached() 方法。使用 pdfSigner 签名也成功了,我得到了一个第一眼看起来不错的签名 PDF。但是,当我在 Adobe Reader 中打开已签名的 pdf 时,它告诉我签名证书的信任链已损坏,无法追溯到 CA 根。这很奇怪,因为它是一个 AATL 证书并且 Adobe Reader 的 AATL 列表是最新的。
我不明白为什么会这样。
我就是这样做的:
为身份调用 DSS : returns id 字符串,签名证书
和一个 ocsp 响应
为信任链调用 DSS:returns 用于
的证书链
签署签名证书,直到 GlobalSign 根,连同
他们的 oscp 响应(root 除外)
我创建了一个包含签名的 X509Certificate 对象数组
证书、2 个中间证书和 GlobalSign 根证书(在
这个顺序)
我实现了一个 IOcspClient,它使用来自 DSS 调用的 ocsp 响应
为了身份
我实现了一个调用 DSS API /timestamp/{digest}
的 ITsaClient
最后我执行:pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CMS);
其中externalSignature(一个实现
IExternalSignature) 将调用 DSS identity/{id}/sign/{digest} API
在调试 signDetached 方法和深入 pdfSigner 代码时,我清楚地看到所有证书都以正确的顺序位于链中。我看到它们在 PdfPKCS7 class 中被处理(但是我不 know/understand 到底发生了什么)。我看到正在签名,没有抛出异常,最后生成的 PDF 看起来像是正确签名的。 Adobe 说不是。
我在这里错过了什么?
来自 de DSS 的信任链响应 API 不仅 returns 来自签名证书信任链的证书,还有签名证书和 GlobalSign 根之间的两个中间体的 ocsp 响应。这些从未被使用过。事实上,我也不知道如何处理它们。
这些是否是 AdobeReader 重建信任链到 GlobalSign 根的缺失部分?
并且如果是这样:我如何将它们放入那个 PDF 中?
如果不是:那么我做错了什么破坏了信任链?
这些问题的答案将节省我的时间:-)
这是将显示问题的 PDF link:
test pdf signed with 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);
}
简而言之
您的签署者证书无效。
详细
您的签名者证书及其证书链(根据 issuer/subject 匹配)嵌入在签名中,特别是您的带有主题
的证书
cn=Homologatie Voertuigen,
ou=Departement Mobiliteit en Openbare Werken,
ou=Vlaams Huis voor de Verkeersveiligheid,
o=Ministeries van de Vlaamse Gemeenschap,
l=Brussel,
st=Brussel,
c=BE
及其声称的发行人
cn=GlobalSign CA 5 for AATL,
o=GlobalSign nv-sa,
c=BE
因此,可以检查您的证书所用的签名。在这样做的同时,您会看到签名者证书的 TBSCertificate
部分(待签名部分)具有此摘要值
C8751FDC7F679CB627F61028ACDD0D09613AFA782412ACFC7E189EA5DA625831
但签名实际上签署了这个摘要值
16090737B41E6E0466E7EB7A7EBD79F5494E438C11D0FB408BCA663A5923AD03
因此,您的签名证书未正确签名。
这是什么意思
在您提问的评论中
But I am a little confused about what it means exactly. Are we actually doing something wrong during signing, sending the wrong document hash to the signing server ? Or do you mean there is something wrong with the server side signing certificate issued by GlobalSign that they use to sign that document hash?
签名的时候你没有做错什么,至少我不这么认为。损坏的签名不是签署文档的签名,而是您的 CA 签署证书的签名。
我基本上看到了三个可能的原因:
证书签名已损坏,无论如何都与您的证书不匹配。
这会让我大吃一惊。
计算的证书签名不是针对您要签名的证书部分的 DER 编码形式,而是针对其他形式。
这并非闻所未闻,如果您的证书最初不是 DER 格式但证书签名过程假定它是,则可能已签署非 DER 格式(即使根据规范,DER 表格必须签名)。如果某个验证器随后检查了同样不确保 DER 形式但按原样采用 TBSCertificate 的签名,该验证器甚至会指示签名有效。
在 PDF 签名中嵌入的证书中,待签名部分采用 DER 编码,但这可能在初始证书生成后的某个步骤强制执行。
您的证书在创建后可能发生了一些细微的变化。
这个也是可以的
您可以尝试以尽可能原始的形式从您的 CA 接收您的证书副本,并与您的签名中嵌入的证书进行比较。如果您发现差异,那么分析这些差异很可能会进一步阐明问题的原因。
我正在使用 iText7 和 GlobalSign DSS 对 PDF 进行数字签名。我将 GlobalSing DSS API 调用实现到必要的 iText classes 中。我得到了正确的服务器响应,并且能够使用所有需要的参数调用 pdfSigner.signDetached() 方法。使用 pdfSigner 签名也成功了,我得到了一个第一眼看起来不错的签名 PDF。但是,当我在 Adobe Reader 中打开已签名的 pdf 时,它告诉我签名证书的信任链已损坏,无法追溯到 CA 根。这很奇怪,因为它是一个 AATL 证书并且 Adobe Reader 的 AATL 列表是最新的。
我不明白为什么会这样。
我就是这样做的:
为身份调用 DSS : returns id 字符串,签名证书 和一个 ocsp 响应
为信任链调用 DSS:returns 用于
的证书链 签署签名证书,直到 GlobalSign 根,连同
他们的 oscp 响应(root 除外)我创建了一个包含签名的 X509Certificate 对象数组
证书、2 个中间证书和 GlobalSign 根证书(在 这个顺序)我实现了一个 IOcspClient,它使用来自 DSS 调用的 ocsp 响应 为了身份
我实现了一个调用 DSS API /timestamp/{digest}
的 ITsaClient
最后我执行:pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CMS);
其中externalSignature(一个实现 IExternalSignature) 将调用 DSS identity/{id}/sign/{digest} API
在调试 signDetached 方法和深入 pdfSigner 代码时,我清楚地看到所有证书都以正确的顺序位于链中。我看到它们在 PdfPKCS7 class 中被处理(但是我不 know/understand 到底发生了什么)。我看到正在签名,没有抛出异常,最后生成的 PDF 看起来像是正确签名的。 Adobe 说不是。
我在这里错过了什么?
来自 de DSS 的信任链响应 API 不仅 returns 来自签名证书信任链的证书,还有签名证书和 GlobalSign 根之间的两个中间体的 ocsp 响应。这些从未被使用过。事实上,我也不知道如何处理它们。
这些是否是 AdobeReader 重建信任链到 GlobalSign 根的缺失部分?
并且如果是这样:我如何将它们放入那个 PDF 中?
如果不是:那么我做错了什么破坏了信任链?
这些问题的答案将节省我的时间:-)
这是将显示问题的 PDF link:
test pdf signed with 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);
}
简而言之
您的签署者证书无效。
详细
您的签名者证书及其证书链(根据 issuer/subject 匹配)嵌入在签名中,特别是您的带有主题
的证书cn=Homologatie Voertuigen, ou=Departement Mobiliteit en Openbare Werken, ou=Vlaams Huis voor de Verkeersveiligheid, o=Ministeries van de Vlaamse Gemeenschap, l=Brussel, st=Brussel, c=BE
及其声称的发行人
cn=GlobalSign CA 5 for AATL, o=GlobalSign nv-sa, c=BE
因此,可以检查您的证书所用的签名。在这样做的同时,您会看到签名者证书的 TBSCertificate
部分(待签名部分)具有此摘要值
C8751FDC7F679CB627F61028ACDD0D09613AFA782412ACFC7E189EA5DA625831
但签名实际上签署了这个摘要值
16090737B41E6E0466E7EB7A7EBD79F5494E438C11D0FB408BCA663A5923AD03
因此,您的签名证书未正确签名。
这是什么意思
在您提问的评论中
But I am a little confused about what it means exactly. Are we actually doing something wrong during signing, sending the wrong document hash to the signing server ? Or do you mean there is something wrong with the server side signing certificate issued by GlobalSign that they use to sign that document hash?
签名的时候你没有做错什么,至少我不这么认为。损坏的签名不是签署文档的签名,而是您的 CA 签署证书的签名。
我基本上看到了三个可能的原因:
证书签名已损坏,无论如何都与您的证书不匹配。
这会让我大吃一惊。
计算的证书签名不是针对您要签名的证书部分的 DER 编码形式,而是针对其他形式。
这并非闻所未闻,如果您的证书最初不是 DER 格式但证书签名过程假定它是,则可能已签署非 DER 格式(即使根据规范,DER 表格必须签名)。如果某个验证器随后检查了同样不确保 DER 形式但按原样采用 TBSCertificate 的签名,该验证器甚至会指示签名有效。
在 PDF 签名中嵌入的证书中,待签名部分采用 DER 编码,但这可能在初始证书生成后的某个步骤强制执行。
您的证书在创建后可能发生了一些细微的变化。
这个也是可以的
您可以尝试以尽可能原始的形式从您的 CA 接收您的证书副本,并与您的签名中嵌入的证书进行比较。如果您发现差异,那么分析这些差异很可能会进一步阐明问题的原因。