用例如下:本地应用程序必须使用在远程服务器上生成的完整pkcs#7分离签名对pdf文档进行签名,该签名需要通过电子邮件/sms发送一次性密码;远程服务器根据相关文档散列生成分离的签名,得到的签名是cms rfc(rfc5652)第5.2节中定义的“外部签名”,例如,得到的签名文件是一个单独的文件。
我花了好几天的时间来为这个用例实现一个有效的解决方案(使用itextpdf 5.5.13.1),但是最终的签名文件有一个签名错误,消息是“certificationby is invalid”--请参阅附件中的“signed \u with \u error”文件。概念上的“两步签字”实施过程是:
第一步:从原始文档文件--参见“原始”附加文件--我创建了一个中间文件--参见“中间”附加文件--我在其中插入了一个空签名,根据该签名创建了关联的文档哈希。在这一步中,签名摘要被保存以供下一步使用
第二步:调用远程服务器,接收分离的签名文件,我使用上一步的签名摘要和从远程服务器接收的分离的签名内容,通过在预签名文件的签名中插入内容来创建签名文件。
原始、中间和带错误签名的示例文件为:
起初的
中介的
有错误的签名
有人知道我下面的代码有什么问题吗?
相关代码部分如下:
第一步:
ByteArrayOutputStream preSignedDocument = new ByteArrayOutputStream();
Path customerPathInDataStorage = storageService.resolveCustomerPathInDataStorage(customerExternalId);
PdfReader pdfReader = new PdfReader(originalDocumentContent);
PdfStamper stamper = PdfStamper.createSignature(pdfReader, preSignedDocument, '\0', customerPathInDataStorage.toFile(), true);
// create certificate chain using certificate received from remote server system
byte[] certificateContent = certificateInfo.getData(); // this is the customer certificate received one time from the remote server and used for every document signing initialization
X509Certificate certificate = SigningUtils.buildCertificateFromCertificateContent(certificateContent);
java.security.cert.Certificate[] certificatesChain = CertificateFactory.getInstance(CERTIFICATE_TYPE).generateCertPath(
Collections.singletonList(certificate)).getCertificates().toArray(new java.security.cert.Certificate[0]);
// create empty digital signature inside pre-signed document
PdfSignatureAppearance signatureAppearance = stamper.getSignatureAppearance();
signatureAppearance.setVisibleSignature(new Rectangle(72, 750, 400, 770), 1, "Signature_" + customerExternalId);
signatureAppearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
signatureAppearance.setCertificate(certificate);
CustomPreSignExternalSignature externalSignatureContainer =
new CustomPreSignExternalSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.signExternalContainer(signatureAppearance, externalSignatureContainer, 8192);
ExternalDigest digest = new SignExternalDigest();
PdfPKCS7 pdfPKCS7 = new PdfPKCS7(null, certificatesChain, DOCUMENT_HASHING_ALGORITHM, null, digest, false);
byte[] signatureDigest = externalSignatureContainer.getSignatureDigest();
byte[] authAttributes = pdfPKCS7.getAuthenticatedAttributeBytes(signatureDigest, null, null,
MakeSignature.CryptoStandard.CMS);
pdfReader.close();
documentDetails.setPreSignedContent(preSignedDocument.toByteArray()); // this is the intermediary document content used in 2nd step in the line with the comment***PRESIGNED_CONTENT****
documentDetails.setSignatureDigest(signatureDigest); // this is the signature digest used in 2nd step in the line with comment****SIGNATURE_DIGEST****
byte[] hashForSigning = DigestAlgorithms.digest(new ByteArrayInputStream(authAttributes),
digest.getMessageDigest(DOCUMENT_HASHING_ALGORITHM));
documentDetails.setSigningHash(hashForSigning); // this is the hash sent to remote server for signing
第二步:
// create certificate chain from detached signature
byte[] detachedSignatureContent = documentDetachedSignature.getSignature(); // this is the detached signature file content received from the remote server which contains also customer the certificate
X509Certificate certificate = SigningUtils.extractCertificateFromDetachedSignatureContent(detachedSignatureContent);
java.security.cert.Certificate[] certificatesChain = CertificateFactory.getInstance(CERTIFICATE_TYPE).generateCertPath(
Collections.singletonList(certificate)).getCertificates().toArray(new java.security.cert.Certificate[0]);
// create digital signature from detached signature
ExternalDigest digest = new SignExternalDigest();
PdfPKCS7 pdfPKCS7 = new PdfPKCS7(null, certificatesChain, DOCUMENT_HASHING_ALGORITHM, null, digest, false);
pdfPKCS7.setExternalDigest(detachedSignatureContent, null, SIGNATURE_ENCRYPTION_ALGORITHM);
byte[] signatureDigest = documentVersion.getSignatureDigest(); // this is the value from 1st step for****SIGNATURE_DIGEST****
byte[] encodedSignature = pdfPKCS7.getEncodedPKCS7(signatureDigest, null, null, null, MakeSignature.CryptoStandard.CMS);
ExternalSignatureContainer externalSignatureContainer = new CustomExternalSignature(encodedSignature);
// add signature content to existing signature container of the intermediary PDF document
PdfReader pdfReader = new PdfReader(preSignedDocumentContent);// this is the value from 1st step for***PRESIGNED_CONTENT****
ByteArrayOutputStream signedPdfOutput = new ByteArrayOutputStream();
MakeSignature.signDeferred(pdfReader, "Signature_" + customerExternalId, signedPdfOutput, externalSignatureContainer);
return signedPdfOutput.toByteArray();
依赖项:
public static final String DOCUMENT_HASHING_ALGORITHM = "SHA256";
public static final String CERTIFICATE_TYPE = "X.509";
public static final String SIGNATURE_ENCRYPTION_ALGORITHM = "RSA";
public class CustomPreSignExternalSignature implements ExternalSignatureContainer {
private static final Logger logger = LoggerFactory.getLogger(CustomPreSignExternalSignature.class);
private PdfDictionary dictionary;
private byte[] signatureDigest;
public CustomPreSignExternalSignature(PdfName filter, PdfName subFilter) {
dictionary = new PdfDictionary();
dictionary.put(PdfName.FILTER, filter);
dictionary.put(PdfName.SUBFILTER, subFilter);
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
try {
ExternalDigest digest = new SignExternalDigest();
signatureDigest = DigestAlgorithms.digest(data, digest.getMessageDigest(DOCUMENT_HASHING_ALGORITHM));
} catch (IOException e) {
logger.error("CustomSignExternalSignature - can not create hash to be signed", e);
throw new GeneralSecurityException("CustomPreSignExternalSignature - can not create hash to be signed", e);
}
return new byte[0];
}
@Override
public void modifySigningDictionary(PdfDictionary pdfDictionary) {
pdfDictionary.putAll(dictionary);
}
public byte[] getSignatureDigest() {
return signatureDigest;
}
}
public class CustomExternalSignature implements ExternalSignatureContainer {
private byte[] signatureContent;
public CustomExternalSignature(byte[] signatureContent) {
this.signatureContent = signatureContent;
}
@Override
public byte[] sign(InputStream data) throws GeneralSecurityException {
return signatureContent;
}
@Override
public void modifySigningDictionary(PdfDictionary pdfDictionary) {
}
}
public class SignExternalDigest implements ExternalDigest {
@Override
public MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException {
return DigestAlgorithms.getMessageDigest(hashAlgorithm.toUpperCase(), null);
}
}
稍后评论:
即使它是不正确的,我已经观察到,如果,在第二步,而不是行:
byte[] signatureDigest = documentVersion.getSignatureDigest(); // this is the value from 1st step for****SIGNATURE_DIGEST****
我将使用:
byte[] signatureDigest = new byte[0];
签名文件将生成一个可见的证书,该证书可以验证,但pdf文档无效,错误为“文档证书无效”。---申请认证后,文档已被更改或损坏。“-请参阅附件签名的认证无效。它看起来是合法的无效,但对我来说很奇怪,为什么在这种情况下,证书显示在文档中,但在使用-我认为-“正确的”signaturedigest值时却被破坏了。
1条答案
按热度按时间63lcw9qa1#
你说呢
远程服务器根据相关文档散列生成分离的签名,得到的签名是cms rfc(rfc5652)第5.2节中定义的“外部签名”
因此,您在步骤2中检索到的内容:
已经是cms签名容器。因此,您不能再像下面几行那样将其插入pkcs#7/cms签名容器,而是可以立即将其插入pdf。
所以你的第二步应该是
此外,签署了错误的散列。实际上,在步骤1中,您不应该与
PdfPKCS7
类,但只使用