pdfbox-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Paresh Chouhan <pareshchouhan2...@gmail.com>
Subject Re: PDF Signing, generated PDF Document certification is invalid, using HSM
Date Wed, 24 May 2017 10:17:51 GMT
sorry for double post, indentation wasn't preserved so here's a pastebin :
https://pastebin.com/b3qZH6xW

On Wed, May 24, 2017 at 3:42 PM Paresh Chouhan <pareshchouhan2013@gmail.com>
wrote:

> I have a service which signs the data and provides me with the signed
> hash, it correctly generates PKCS#7 DigestInfo as stated here
> https://tools.ietf.org/html/rfc2315#section-9.4
>
> find the code at the end of this email,
> the problem is the generated PDF is shown as invalid
>
> public class DetachedPkcs7 implements SignatureInterface {
>     public static void main(String[] args) {
>         Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
>         try {
>             //load pdf document
>             PDDocument document = PDDocument.load(new File("test.pdf"));
>             int accessPermissions = getMDPPermission(document);
>             if (accessPermissions == 1)
>             {
>                 throw new IllegalStateException("No changes to the document are permitted
due to DocMDP transform parameters dictionary");
>             }
>             //prepare signature
>             PDSignature signature = new PDSignature();
>             signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
>             signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
>             signature.setName("Ankit Agarwal");
>             signature.setLocation("Bhopal, IN");
>             signature.setReason("Testing");
>             // TODO extract the above details from the signing certificate? Reason as
a parameter?
>
>             // the signing date, needed for valid signature
>             signature.setSignDate(Calendar.getInstance());
>
> //            // Optional: certify
>             if (accessPermissions == 0)
>             {
>                 setMDPPermission(document, signature, 3);
>             }
>
>             FileOutputStream fos = new FileOutputStream(new File("signed_file.pdf"));
>
>             DetachedPkcs7 detachedPkcs7 = new DetachedPkcs7();
>             //populate signature options for visible signature. if any.
>             SignatureOptions signatureOptions = null;
>             document.addSignature(signature);
>             ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(fos);
>             InputStream dataToSign = externalSigning.getContent();
>             byte[] cmsSignature = detachedPkcs7.sign(dataToSign);
>             externalSigning.setSignature(cmsSignature);
>         }
>         catch(FileNotFoundException fex) {
>             fex.printStackTrace();
>         } catch (IOException e) {
>             e.printStackTrace();
>         }
>     }
>
>     class CMSTypedDataInputStream implements CMSTypedData {
>         InputStream in;
>
>         public CMSTypedDataInputStream(InputStream is) {
>             in = is;
>         }
>
>         @Override
>         public ASN1ObjectIdentifier getContentType() {
>             return PKCSObjectIdentifiers.data;
>         }
>
>         @Override
>         public Object getContent() {
>             return in;
>         }
>
>         @Override
>         public void write(OutputStream out) throws IOException,
>                 CMSException {
>             byte[] buffer = new byte[8 * 1024];
>             int read;
>             while ((read = in.read(buffer)) != -1) {
>                 out.write(buffer, 0, read);
>             }
>             in.close();
>         }
>     }
>
>     static class CMSProcessableInputStream implements CMSProcessable, CMSTypedData {
>         private InputStream input;
>         private boolean used = false;
>         private final ASN1ObjectIdentifier contentType;
>
>         public CMSProcessableInputStream(
>                 InputStream input)
>         {
>             this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), input);
>         }
>
>         public CMSProcessableInputStream(ASN1ObjectIdentifier contentType, InputStream
input) {
>             this.input = input;
>             this.contentType = contentType;
>         }
>
>         public InputStream getInputStream()
>         {
> //            checkSingleUsage();
>
>             return input;
>         }
>
>         public void write(OutputStream zOut)
>                 throws IOException, CMSException
>         {
> //            checkSingleUsage();
>
>             Streams.pipeAll(input, zOut);
>             input.close();
>         }
>
>         public Object getContent()
>         {
>             return getInputStream();
>         }
>
> //        private synchronized void checkSingleUsage()
> //        {
> //            if (used)
> //            {
> //                throw new IllegalStateException("CMSProcessableInputStream can only
be used once");
> //            }
> //
> //            used = true;
> //        }
>
>         @Override
>         public ASN1ObjectIdentifier getContentType() {
>             return contentType;
>         }
>     }
>
>     public byte[] sign(InputStream content) throws IOException {
>         try {
>             //prepare message digest
>             MessageDigest mdOriginal = MessageDigest.getInstance("SHA-256");
>             byte[] dataToDigest = IOUtils.toByteArray(content);
>             byte[] digestOriginal = mdOriginal.digest(dataToDigest);
>             System.out.println("SHA256Original : " + Hex.encodeHexString(digestOriginal));
>             //the certificate of the signer.
>             String certPem = "-----BEGIN CERTIFICATE-----\n" +
>                     "USER CERT HERE, THIS IS ALSO TAKEN FROM USB TOKEN (HSM)\n" +
>                     "-----END CERTIFICATE-----";
>             ByteArrayInputStream inStream = new ByteArrayInputStream(certPem.getBytes());
>
>             BufferedInputStream bis = new BufferedInputStream(inStream);
>             System.out.println(bis.available());
>             CertificateFactory cf = null;
>
>             cf = CertificateFactory.getInstance("X.509");
>             List<Certificate> certList = new ArrayList<Certificate>();
>             //generate certificate from input stream
>             Certificate certificate = cf.generateCertificate(bis);
>
>             certList.add(certificate);
>
>             Store certs = new JcaCertStore(certList);
>             CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
>
>             Scanner s = new Scanner(System.in);
>
>            //wait for the user to enter base64 encoded signed value.
>             final String signedHash = s.nextLine();
>             System.out.println("Signing. . . ");
>             s.close();
>             ContentSigner nonSigner = new ContentSigner() {
>
>                 @Override
>                 public byte[] getSignature() {
>                     return Base64.decodeBase64(signedHash);
>                 }
>
>                 @Override
>                 public OutputStream getOutputStream() {
>                     return new ByteArrayOutputStream();
>                 }
>
>                 @Override
>                 public AlgorithmIdentifier getAlgorithmIdentifier() {
>                     return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSAEncryption");
>                 }
>             };
>             org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
>             JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new
JcaDigestCalculatorProviderBuilder().build());
>             sigb.setDirectSignature(true);
>             gen.addCertificates(certs);
>             gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert)));
>
>             CMSTypedDataInputStream msg = new CMSTypedDataInputStream(new ByteArrayInputStream(dataToDigest));
// this is never used.
>             CMSSignedData signedData = gen.generate(msg, false);
>             byte[] pkcs7 = signedData.getEncoded();
>
>             //this is the signature.
>             content.close();
>             return pkcs7;
>         } catch (CertificateException e) {
>             content.close();
>             e.printStackTrace();
>         } catch (CMSException e) {
>             content.close();
>             e.printStackTrace();
>         } catch (OperatorCreationException e) {
>             content.close();
>             e.printStackTrace();
>         } catch (NoSuchAlgorithmException e) {
>             content.close();
>             e.printStackTrace();
>         }
>         content.close();
>         return null;
>     }
>
>     static public int getMDPPermission(PDDocument doc)
>     {
>         COSBase base = doc.getDocumentCatalog().getCOSObject().getDictionaryObject(COSName.PERMS);
>         if (base instanceof COSDictionary)
>         {
>             COSDictionary permsDict = (COSDictionary) base;
>             base = permsDict.getDictionaryObject(COSName.DOCMDP);
>             if (base instanceof COSDictionary)
>             {
>                 COSDictionary signatureDict = (COSDictionary) base;
>                 base = signatureDict.getDictionaryObject("Reference");
>                 if (base instanceof COSArray)
>                 {
>                     COSArray refArray = (COSArray) base;
>                     for (int i = 0; i < refArray.size(); ++i)
>                     {
>                         base = refArray.getObject(i);
>                         if (base instanceof COSDictionary)
>                         {
>                             COSDictionary sigRefDict = (COSDictionary) base;
>                             if (COSName.DOCMDP.equals(sigRefDict.getDictionaryObject("TransformMethod")))
>                             {
>                                 base = sigRefDict.getDictionaryObject("TransformParams");
>                                 if (base instanceof COSDictionary)
>                                 {
>                                     COSDictionary transformDict = (COSDictionary) base;
>                                     int accessPermissions = transformDict.getInt(COSName.P,
2);
>                                     if (accessPermissions < 1 || accessPermissions
> 3)
>                                     {
>                                         accessPermissions = 2;
>                                     }
>                                     return accessPermissions;
>                                 }
>                             }
>                         }
>                     }
>                 }
>             }
>         }
>         return 0;
>     }
>
>     static public void setMDPPermission(PDDocument doc, PDSignature signature, int accessPermissions)
>     {
>         COSDictionary sigDict = signature.getCOSObject();
>
>         // DocMDP specific stuff
>         COSDictionary transformParameters = new COSDictionary();
>         transformParameters.setItem(COSName.TYPE, COSName.getPDFName("TransformParams"));
>         transformParameters.setInt(COSName.P, accessPermissions);
>         transformParameters.setName(COSName.V, "1.2");
>         transformParameters.setNeedToBeUpdated(true);
>
>         COSDictionary referenceDict = new COSDictionary();
>         referenceDict.setItem(COSName.TYPE, COSName.getPDFName("SigRef"));
>         referenceDict.setItem("TransformMethod", COSName.getPDFName("DocMDP"));
>         referenceDict.setItem("DigestMethod", COSName.getPDFName("SHA1"));
>         referenceDict.setItem("TransformParams", transformParameters);
>         referenceDict.setNeedToBeUpdated(true);
>
>         COSArray referenceArray = new COSArray();
>         referenceArray.add(referenceDict);
>         sigDict.setItem("Reference", referenceArray);
>         referenceArray.setNeedToBeUpdated(true);
>
>         // Catalog
>         COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
>         COSDictionary permsDict = new COSDictionary();
>         catalogDict.setItem(COSName.PERMS, permsDict);
>         permsDict.setItem(COSName.DOCMDP, signature);
>         catalogDict.setNeedToBeUpdated(true);
>         permsDict.setNeedToBeUpdated(true);
>     }
>
> }
>
> --
> Regards
> Paresh Chouhan
> https://github.com/pareshchouhan
>
-- 
Regards
Paresh Chouhan
https://github.com/pareshchouhan

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message