首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PAdES签名级别- Acrobat

PAdES签名级别- Acrobat
EN

Stack Overflow用户
提问于 2021-04-12 09:17:28
回答 1查看 443关注 0票数 1

我正在使用pdfbox 3.0.0rc创建一个PADES签名,我的代码使用这个示例来创建数字签名。但是,当我用这个工具打开文档时,我无法看到Acrobat中的签名级别,尽管它能够验证我的签名。

我没有创建VRI,所以我猜测这可能是一个问题,但是如果这是验证我的签名所必需的,我不明白为什么签名显示为有效?

Adobe Acrobat签名:

代码语言:javascript
复制
/**
 * Service for automatically signing a document as part of a workflow. In this instance no user information is 
 * gathered
 *
 * @param taskID
 * @param processName which will be added to the document 
 * @param keyID the ID for the key used to sign the PDF document
 * @return the signed PDF document as a base 64 Encoded String 
 */
@Transactional
public String signPDFService(String processID, 
                             String processName,
                             String keyID,
                             ObjectData signatureImage,
                             String creator)
{
    try {

        
        
        ByteArrayOutputStream ostream = new ByteArrayOutputStream();
        //Optional<ObjectData> pdfDocumentProcessInstance = userDataRepository.findById(documents.get(0).getProcessID());
         
         /*List<Task> tasks = taskService.createTaskQuery()
              .taskId(taskID)
              .list();
    // Optional<ObjectData> pdfDocumentProcessInstance = userDataRepository.findById(documents.get(0).getProcessID());
     
    List<PDFDocument> documents = tasks.stream()
              .map(task -> {
                  String processID =  task.getProcessInstanceId();
                  Map<String, Object> variables = taskService.getVariables(task.getId());
                  PDFDocument document;
                  try {
                      document =  new PDFDocument(
                              (ArrayList) variables.get("assigneeList"),
                              (String) variables.get("unsignedPDFDocument"),
                              task.getProcessInstanceId(),
                              task.getId(),
                              (String) variables.get("name"),
                              (String) variables.get("description")
                        );
                      document.setHistory((ArrayList) variables.get("history"));
                      return document;
                    
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    return null;
                } catch (CMSException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    return null;
                }
              })
              .collect(Collectors.toList());*/


     //Optional<ObjectData> pdfDocumentProcessInstance = userDataRepository.findById(documents.get(0).getProcessID());
        Optional<ObjectData> pdfDocumentProcessInstance = userDataRepository.findById(processID);
     if(pdfDocumentProcessInstance.isEmpty())
         throw new IOException("No process found");
     
        String pdfDocumentBase64String = pdfDocumentProcessInstance.get().getAttributes().get("PDFDocument"); 
        String extractedPDFString = pdfDocumentBase64String.replaceAll("data:application/pdf;base64,", "").replaceAll("data:;base64,", "").replaceAll("data:application/octet-stream", "");
        //String extractedPDFString = base64PDF.replaceAll("data:application/pdf;base64,", "").replaceAll("data:;base64,", "");
        InputStream stream = new ByteArrayInputStream(Base64.getDecoder().decode(extractedPDFString.getBytes()));

        
        //Create the date object to sign the document
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();

        calendar.setTime(date);

        //Retrieve certificate chain for the PDF Signer
        String certChainPEM = kmsService.getCertChainPEM(keyID);
        X509Certificate pdfSignerCertificate = X509Utils.readCertificateChain(certChainPEM).get(0).getCertificate();
 
        //Create the CMS Signing Object
        ExternalSignatureCMSSignedDataGenerator cmsGenerator = new ExternalSignatureCMSSignedDataGenerator();
        ExternalSignatureSignerInfoGenerator signerGenerator = new ExternalSignatureSignerInfoGenerator(CMSSignedDataGenerator.DIGEST_SHA256, "1.2.840.10045.4.3.2");
        signerGenerator.setCertificate(pdfSignerCertificate);
        ExternalSigningSupport externalSigningSupport;
        PDDocument pdDocument = Loader.loadPDF(stream);
        
   
        //Create the PDFBox Signature Object
        PDSignature pdSignature = new PDSignature();
        pdSignature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        pdSignature.setSubFilter(PDSignature.SUBFILTER_ETSI_CADES_DETACHED);
        pdSignature.setLocation("Remote IS Blocks Signer");
        pdSignature.setName("IS Blocks Signer");
        pdSignature.setReason(processName);
        pdDocument.setDocumentId(calendar.getTimeInMillis());
        pdSignature.setSignDate(calendar);
        
        // Optional: Certify the first time signature 
        // can be done only if version is at least 1.5 and if not already set
        // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
        // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
        
        int accessPermissions = SigUtils.getMDPPermission(pdDocument);

        
  
        if (pdDocument.getVersion() >= 1.5f && accessPermissions == 0 && processName.contains("Document Certifying Key"))
        {
            logger.debug("Certifying Document");
            SigUtils.setMDPPermission(pdDocument, pdSignature, 3);
        }
        
        if(signatureImage != null) {
            String data = signatureImage.getAttributes().get("data").replaceAll("data:application/pdf;base64,", "").replaceAll("data:;base64,", "").replaceAll("data:image/png;base64,", "");
            int pageNumber = Integer.parseInt(signatureImage.getAttributes().get("page"));
            float x = Float.parseFloat(signatureImage.getAttributes().get("x"));
            float y = Float.parseFloat(signatureImage.getAttributes().get("y"));
            float width = Float.parseFloat(signatureImage.getAttributes().get("width"));
            float height = Float.parseFloat(signatureImage.getAttributes().get("height"));
            SignatureOptions signatureOptions;
            // register signature dictionary and sign interface
            signatureOptions = new SignatureOptions();
            PDFVisibleSignature pdfVisibleSignature = new PDFVisibleSignature();
            signatureOptions.setVisualSignature(pdfVisibleSignature.createVisualSignatureTemplate(  
                    x, 
                    y, 
                    width, 
                    height, 
                    pdDocument, 
                    pageNumber, 
                    pdSignature, 
                    Base64.getDecoder().decode(data.getBytes("UTF-8"))));
            signatureOptions.setPage(pageNumber);
            pdDocument.addSignature(pdSignature, null, signatureOptions);
        } else {
            pdDocument.addSignature(pdSignature);
        }

            
        
     

        
        
        externalSigningSupport = pdDocument.saveIncrementalForExternalSigning(ostream);

        //Create the message digest of the pre-signed PDF
        MessageDigest digest = MessageDigest.getInstance("SHA-256");     
        byte[] bytes = org.apache.commons.io.IOUtils.toByteArray(externalSigningSupport.getContent());
        byte[] hashBytes = digest.digest(bytes);
        
        //CMS Signature 
        InputStream isBytes = new ByteArrayInputStream(bytes);
        CMSProcessable input = new CMSProcessableInputStream(isBytes);
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());


        MessageDigest messageDigest1 = MessageDigest.getInstance("SHA-256");
        byte[] hash = messageDigest1.digest(bytes);
        byte[] bytesToSign = signerGenerator.getBytesToSign(PKCSObjectIdentifiers.data,  hash, new Date(),
                "BC");
        String encodedData = Base64.getEncoder().encodeToString(bytesToSign);

        logger.debug("Bytes to Sign:" + (Base64.getEncoder().encodeToString(bytesToSign)));
        logger.debug("Hash:" + Base64.getEncoder().encodeToString(hash));
 
        //Create the signature using the keyID
        //At this time only ECDSAWithSHA256 is supported
        Map<String, String> signature = kmsService.sign(keyID, encodedData);

        byte[] signedBytes = Base64.getDecoder().decode(signature.get("signature"));

        X509Certificate[] chain;

        signerGenerator.setCertificate(pdfSignerCertificate);
        signerGenerator.setSignedBytes(signedBytes);

        cmsGenerator.addSignerInf(signerGenerator);
        cmsGenerator.addCertificatesAndCRLs(X509Utils.getCertStore(signature.get("certificateChain")));
        CMSSignedData signedData = cmsGenerator.generate(new CMSProcessableByteArray(hash), false);

        //Add a RFC3161 Time Stamp
        ValidationTimeStamp validation = new ValidationTimeStamp("https://freetsa.org/tsr");
        signedData = validation.addSignedTimeStamp(signedData);

        ContentSigner nonSigner = new ContentSigner() {

        @Override
        public byte[] getSignature() {
            return signedBytes;
        }

        @Override
        public OutputStream getOutputStream() {
            return new ByteArrayOutputStream();
        }

        @Override
        public AlgorithmIdentifier getAlgorithmIdentifier() {
            return new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA256WithECDSA" );
        }
        };
            
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());      
        gen.addCertificate(new X509CertificateHolder(pdfSignerCertificate.getEncoded()));
        sigb.setDirectSignature( true );

        gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(pdfSignerCertificate.getEncoded())));
        CMSTypedData msg = new CMSProcessableInputStream( new ByteArrayInputStream( "not used".getBytes() ) );
       
        CMSSignedData signedData1 = gen.generate((CMSTypedData)msg, false);
        signedData1.getEncoded();

        externalSigningSupport.setSignature(signedData.getEncoded());
        
        //documents.get(0).addHistoricEvent("Signed " + processName);
        //ArrayList<String> history = documents.get(0).getHistory();

        //Post Signature

        String signedPDFDocument = Base64.getEncoder().encodeToString(ostream.toByteArray());
        PDDocument newPdf1;     
        newPdf1 = Loader.loadPDF(ostream.toByteArray());
        byte[] fileContent = ostream.toByteArray();
  
        List<PDSignature> pdfSignatures;

            
        pdfSignatures = newPdf1.getSignatureDictionaries();
        
  
        byte[] signatureAsBytes;

            signatureAsBytes = newPdf1.getLastSignatureDictionary().getContents( fileContent );
    
        byte[] signedContentAsBytes;
    
            signedContentAsBytes = newPdf1.getLastSignatureDictionary().getSignedContent( fileContent );

            // Now we construct a PKCS #7 or CMS.
            CMSProcessable cmsProcessableInputStream = new CMSProcessableByteArray(signedContentAsBytes);
            CMSSignedData cmsSignedData;

            cmsSignedData = new CMSSignedData(cmsProcessableInputStream, signatureAsBytes);
     
            Store certificatesStore = cmsSignedData.getCertificates();
            Collection<SignerInformation> signers = cmsSignedData.getSignerInfos().getSigners();
            SignerInformation signerInformation = signers.iterator().next();
            Collection matches = certificatesStore.getMatches(signerInformation.getSID());
            X509CertificateHolder certificateHolder = (X509CertificateHolder) matches.iterator().next();
            
            
            ObjectMapper mapper = new ObjectMapper();
            ArrayList<String> signatures = null;
            //signatures =  documents.get(0).getSignatures();
            
            
            for(int iCount = 0; iCount < pdfSignatures.size(); iCount++) {
            PDFSignature pdfSignature = new PDFSignature(
                    pdfSignatures.get(iCount).getName(), 
                    pdfSignatures.get(iCount).getLocation(), 
                    pdfSignatures.get(iCount).getSignDate().getDisplayName(Calendar.LONG_FORMAT,  java.util.Calendar.LONG, Locale.UK), 
                    pdfSignatures.get(iCount).getReason(),
                    certificateHolder.getSubject().toString(), 
                    certificateHolder.getIssuer().toString(), 
                    Base64.getEncoder().encodeToString(certificateHolder.getEncoded()));
                    //signatures.add(mapper.writeValueAsString(pdfSignature));
                    logger.info("Signature" + mapper.writeValueAsString(pdfSignature));
            
            }
            
            Map<String, Object> variables = new HashMap<String, Object>();
           // variables.put("history", history);
            //variables.put("unsignedPDFDocument", signedPDFDocument);
            //variables.put("signatures", signatures);
            //variables.put("status", value)

      
            Map<String, String> pdfDocumentProcessInstanceAttributes = pdfDocumentProcessInstance.get().getAttributes();
            pdfDocumentProcessInstanceAttributes.put("PDFDocument", signedPDFDocument);
            ObjectData newpdfProcessInstance = pdfDocumentProcessInstance.get();
            newpdfProcessInstance.setAttributes(pdfDocumentProcessInstanceAttributes);
            userDataRepository.save(newpdfProcessInstance);
            newpdfProcessInstance.getHistory().add(new Date() + "Signed by:" + creator);
            System.out.println(newpdfProcessInstance.getId() + " " + newpdfProcessInstance.toString());

            newPdf1.close();
            ostream.close();


        } catch (Exception e){
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    
    return null;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-05 13:17:26

在分析您在评论中提供的文件https://www.dropbox.com/s/srhu9raym0ozneu/document-2.pdf?dl=0时,我发现了其中的一个问题。注意到这个问题,我重新检查了您原来的https://www.dropbox.com/s/eg9uolx35sja9es/document-17%2021.08.14.pdf?dl=0,并在其中确认了这个问题,所以这个问题可能会导致您要解决的验证问题。因此,..。

您的两个示例文件(文件-17 21.08.14.pdf文档-使用signingTime.pdf)实际上都包含同一个多版本PDF的两个连接副本,每个副本都有一个签名的Signature1,而第二个副本只包含一个更改的ID条目。添加到它们的是带有签名的Signature2的增量更新。

代码语言:javascript
复制
%PDF-1.4
...
15 0 obj
<<
/FT /Sig
...
/T (Signature1)
...
>>
endobj 
...
/ID [<1952AB9C134E46B58251246E985D5C15> <7F887303DDC0ED7C37AE77403E30DFB0>] 
...
%%EOF
%PDF-1.4 
...
15 0 obj
<<
/FT /Sig
...
/T (Signature1)
...
>>
endobj 
...
/ID [<1952AB9C134E46B58251246E985D5C15> <A57CD5B87222756EC4A096125C7E8A42>] 
...
%%EOF
...
35 0 obj
<<
/FT /Sig
...
/T (Signature2)
...
>> 
...
%%EOF 

这个结构被打破了,尽管它不会导致错误的交叉引用(因为第一个副本和第二个副本是相同的,除了ID之外,交叉引用和startxref偏移量指向第一个副本中各自的位置)。Adobe签名验证可以对此类问题做出非常敏感的反应。

因此,您应该删除这里的第二个副本,以取得进展。

此外,正如注释中已经提到的,CMS签名容器的SignerInfo包含一个1.2.840.113549.1.9.5 signingTime签名属性。这在PAdES基线配置文件中是禁止的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67055789

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档