Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用现有证书、中间文件和远程创建的签名,使用itextpdf for Java对PDF进行两步签名_Java_Itext_Digital Signature_X509certificate_Pkcs#7 - Fatal编程技术网

使用现有证书、中间文件和远程创建的签名,使用itextpdf for Java对PDF进行两步签名

使用现有证书、中间文件和远程创建的签名,使用itextpdf for Java对PDF进行两步签名,java,itext,digital-signature,x509certificate,pkcs#7,Java,Itext,Digital Signature,X509certificate,Pkcs#7,使用案例如下:本地应用程序必须使用在远程服务器上生成的完整PKCS#7分离签名对PDF文档进行签名,该签名需要通过电子邮件/SMS发送一次性密码;远程服务器根据相关文档散列生成分离的签名,生成的签名是“外部”签名 “签名”,如CMS RFC(RFC5652)第5.2节所定义,如生成的签名文件是一个单独的文件 我花了好几天的时间来为这个用例实现一个有效的解决方案(使用itextpdf 5.5.13.1),但是最终签名文件的签名有错误,消息是“Certification by is invalid

使用案例如下:本地应用程序必须使用在远程服务器上生成的完整PKCS#7分离签名对PDF文档进行签名,该签名需要通过电子邮件/SMS发送一次性密码;远程服务器根据相关文档散列生成分离的签名,生成的签名是“外部”签名 “签名”,如CMS RFC(RFC5652)第5.2节所定义,如生成的签名文件是一个单独的文件


我花了好几天的时间来为这个用例实现一个有效的解决方案(使用itextpdf 5.5.13.1),但是最终签名文件的签名有错误,消息是“Certification by is invalid”--请参阅附件中的“signed_with_error”(签名错误)。 实施的“两步签字”概念流程为:

第1步:从原始文档文件--请参阅“原始”附加文件--我创建了一个中间文件--请参阅附加的“中间”文件--我在其中插入了一个空签名,根据该签名创建了关联的文档哈希。在这一步中,将保存签名摘要以供下一步使用

第二步:调用远程服务器,接收分离的签名文件,我使用上一步的签名摘要和从远程服务器接收的分离签名内容,通过在预签名文件的签名中插入内容来创建签名文件

原始、中间和带错误签名的示例文件为:


有人知道我下面的代码有什么问题吗?
相关代码部分如下所示:

第一步:

        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";
第二步:

        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";
依赖关系:

        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";



后续评论:

        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";
即使它不正确,我也观察到,如果在第二步中,而不是第二行:

byte[] signatureDigest = documentVersion.getSignatureDigest(); // this is the value from 1st step for ****SIGNATURE_DIGEST****
我将使用:

byte[] signatureDigest = new byte[0];
签名文件将生成一个可见的证书,该证书可以验证,但PDF文档无效,错误为“文档证书无效”。——“自申请认证以来,该文件已被更改或损坏。”-见附件。它看起来是合法的无效,但对我来说奇怪的是,在这种情况下,证书是在文档中显示的,但是在使用时,它被破坏了——我所认为的“正确的”签名摘要值。

您说

远程服务器根据相关文档散列生成分离的签名,生成的签名是CMS RFC(RFC5652)第5.2节中定义的“外部签名”

因此,您在步骤2中检索到的内容:

// 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
已经是CMS签名容器。因此,您不能再像下面几行那样将其插入PKCS#7/CMS签名容器中,但可以立即将其插入PDF

所以你的第二步应该是

// 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
ExternalSignatureContainer externalSignatureContainer = new CustomExternalSignature(detachedSignatureContent);

// 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();

此外,签名错误的哈希。实际上,在步骤1中,您也不应该使用
PdfPKCS7
类,而应该简单地使用

documentDetails.setSigningHash(externalSignatureContainer.getSignatureDigest()); // this is the hash sent to remote server for signing

检查答案是否对您有任何帮助…根据您的建议更正代码后,终于可以工作了!非常感谢。