[ https://issues.apache.org/jira/browse/PDFBOX-4421?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17197520#comment-17197520 ]
Christian Appl edited comment on PDFBOX-4421 at 9/17/20, 10:30 AM: ------------------------------------------------------------------- Hmmm, I am not entirely sure yet, but possibly the current code is missing only 2 simple steps for success? (speaking about decryption) I must state this first: I am absolutely uncertain about the following! I tried to understand and play arround with your code and attempted to understand which part does what and which variable represents what COS Structure. Leading me to two simple assumptions, that I guessed would lead to satisfying results. (I can neither guarantee, that this really solves the issue, nor that this won't brake for some cases, but your thoughts about it would be absolutely helpful!) *Basic Assumption:* This should work! I can find the parts of the Reference manual, that are represented here and I can't find big definition gaps at first glance. *1. Assumption: The assumed key length is wrong (40 instead of 128):* The method "prepareForDecryption" in PublicKeySecurityHandler is currently determining the key length via the PDEncryption dictionary encryption.getLength(). for the case +if (encryption.getVersion() == 4 || encryption.getVersion() == 5)+ possibly it should be modified like: {code:java} // detect whether AES encryption is used. This assumes that the encryption algo is // stored in the PDCryptFilterDictionary // However, crypt filters are used only when V is 4 or 5. PDCryptFilterDictionary defaultCryptFilterDictionary = encryption.getDefaultCryptFilterDictionary(); if (defaultCryptFilterDictionary != null) { if (defaultCryptFilterDictionary.getLength() != 0) { setKeyLength(defaultCryptFilterDictionary.getLength()); } } {code} *2. Assumption: Now that it accepts, that the key length is 128, the message digest is wrong.* For my test document it now succeeded in creating the cipher, but reported a wrong padding / a wrong key, when trying doFinal(). I was absolutely certain to provide the corrrect password, alias and keystore to a matching document, therefore: The message digest - determining the key - can not be correct. I found this: {code:java} if (encryption.getVersion() == 4 || encryption.getVersion() == 5) { mdResult = MessageDigests.getSHA256().digest(sha1Input); {code} And simply changed it to: {code:java} if (encryption.getVersion() == 4 || encryption.getVersion() == 5) { mdResult = MessageDigests.getSHA1().digest(sha1Input); {code} A change which will most definately brake the AES256 handling, but I ignored that for now. (possibly V4 and V5 should be handled in separate cases for this reason? *Could these assumptions be possibly correct?* I created a AES128 encrypted document using Adobe DC and wrote a most simplistic test, that shall load said document using my testkeystore and shall determine the number of pages: {code:java} private static final String password = "w!z%C*F-JaNdRgUk"; private static final String alias = "testnutzer"; @Test public void openEncDoc() throws Exception { File keystore = new File("C:\\Users\\cap.SVD\\Desktop", "keystore.pfx"); File encDoc = new File("C:\\Users\\cap.SVD\\Desktop", "B2-Adobe-128-aes-sec.pdf"); System.out.println("Now loading and decrypting: " + encDoc.getAbsolutePath()); InputStream keyStoreData = new FileInputStream(keystore); PDDocument doc = PDDocument.load(encDoc, password, keyStoreData, alias); try { System.out.println(" SUCCESS decrypting document, it has " + doc.getNumberOfPages() + " pages."); } finally { keyStoreData.close(); doc.close(); } } {code} Which failed previously for said document and now results in the output: Now loading and decrypting: C:\Users\cap.SVD\Desktop\B2-Adobe-128-aes-sec.pdf SUCCESS decrypting document, it has 3 pages. -The document has been successfully decrypted and the number of pages could be determined? I really don't trust myself here, therefore: - Where am I seeing things too simple or could this possibly be the solution? I will definately follow this lead for now - as it resulted in a success - but I don't trust that success yet neither, further testing is required and this is for now quick and dirty trash code, to simply make it work (somehow). *Edit:* Applying the same assumptions to "prepareDocumentForEncryption" also resulted in a AES128 encrypted document, that could be opened by Adobe DC and still contained everything it shall contain (or so it seems). {code:java} if (version == 4) { dictionary.setSubFilter(SUBFILTER5); mdResult = MessageDigests.getSHA1().digest(shaInput); COSName aesVName = COSName.AESV2; prepareEncryptionDictAES(dictionary, aesVName, recipientsFields); } else if(version == 5) { dictionary.setSubFilter(SUBFILTER5); mdResult = MessageDigests.getSHA256().digest(shaInput); COSName aesVName = COSName.AESV3; prepareEncryptionDictAES(dictionary, aesVName, recipientsFields); } else { dictionary.setSubFilter(SUBFILTER4); mdResult = MessageDigests.getSHA1().digest(shaInput); dictionary.setRecipients(recipientsFields); } {code} Currently I would not agree to, that you were unssuccesful here - but maybe you missed two things? *Edit2:* I saved the decrypted document using doc.setAllSecurityToBeRemoved(true); to be sure, that PDFBox would do something with it and would not only write it back as is. The decrypted document also seems to be complete, readable and perfectly fine. I guess I will wait for your feedback now - either I am missing some obvious issues here, or this ticket had been nearly resolved then and is simply missing some minor details. *Edit3:* I am attaching the draft for my current suggestion as a patch file to this issue. This does currently not contain additional Tests (as my currently existing tests are not really helpful for automated testing) I did not yet run any of the PDFBox tests and it contains some further suggestions. (mostly pushing some functionality to super classes to avoid code redundancies.) This is simply a draft, so that you can reproduce what I am doing here. was (Author: capsvd): Hmmm, I am not entirely sure yet, but possibly the current code is missing only 2 simple steps for success? (speaking about decryption) I must state this first: I am absolutely uncertain about the following! I tried to understand and play arround with your code and attempted to understand which part does what and which variable represents what COS Structure. Leading me to two simple assumptions, that I guessed would lead to satisfying results. (I can neither guarantee, that this really solves the issue, nor that this won't brake for some cases, but your thoughts about it would be absolutely helpful!) *Basic Assumption:* This should work! I can find the parts of the Reference manual, that are represented here and I can't find big definition gaps at first glance. *1. Assumption: The assumed key length is wrong (40 instead of 128):* The method "prepareForDecryption" in PublicKeySecurityHandler is currently determining the key length via the PDEncryption dictionary encryption.getLength(). for the case +if (encryption.getVersion() == 4 || encryption.getVersion() == 5)+ possibly it should be modified like: {code:java} // detect whether AES encryption is used. This assumes that the encryption algo is // stored in the PDCryptFilterDictionary // However, crypt filters are used only when V is 4 or 5. PDCryptFilterDictionary defaultCryptFilterDictionary = encryption.getDefaultCryptFilterDictionary(); if (defaultCryptFilterDictionary != null) { if (defaultCryptFilterDictionary.getLength() != 0) { setKeyLength(defaultCryptFilterDictionary.getLength()); } } {code} *2. Assumption: Now that it accepts, that the key length is 128, the message digest is wrong.* For my test document it now succeeded in creating the cipher, but reported a wrong padding / a wrong key, when trying doFinal(). I was absolutely certain to provide the corrrect password, alias and keystore to a matching document, therefore: The message digest - determining the key - can not be correct. I found this: {code:java} if (encryption.getVersion() == 4 || encryption.getVersion() == 5) { mdResult = MessageDigests.getSHA256().digest(sha1Input); {code} And simply changed it to: {code:java} if (encryption.getVersion() == 4 || encryption.getVersion() == 5) { mdResult = MessageDigests.getSHA1().digest(sha1Input); {code} A change which will most definately brake the AES256 handling, but I ignored that for now. (possibly V4 and V5 should be handled in separate cases for this reason? *Could these assumptions be possibly correct?* I created a AES128 encrypted document using Adobe DC and wrote a most simplistic test, that shall load said document using my testkeystore and shall determine the number of pages: {code:java} private static final String password = "w!z%C*F-JaNdRgUk"; private static final String alias = "testnutzer"; @Test public void openEncDoc() throws Exception { File keystore = new File("C:\\Users\\cap.SVD\\Desktop", "keystore.pfx"); File encDoc = new File("C:\\Users\\cap.SVD\\Desktop", "B2-Adobe-128-aes-sec.pdf"); System.out.println("Now loading and decrypting: " + encDoc.getAbsolutePath()); InputStream keyStoreData = new FileInputStream(keystore); PDDocument doc = PDDocument.load(encDoc, password, keyStoreData, alias); try { System.out.println(" SUCCESS decrypting document, it has " + doc.getNumberOfPages() + " pages."); } finally { keyStoreData.close(); doc.close(); } } {code} Which failed previously for said document and now results in the output: Now loading and decrypting: C:\Users\cap.SVD\Desktop\B2-Adobe-128-aes-sec.pdf SUCCESS decrypting document, it has 3 pages. -The document has been successfully decrypted and the number of pages could be determined? I really don't trust myself here, therefore: - Where am I seeing things too simple or could this possibly be the solution? I will definately follow this lead for now - as it resulted in a success - but I don't trust that success yet neither, further testing is required and this is for now quick and dirty trash code, to simply make it work (somehow). *Edit:* Applying the same assumptions to "prepareDocumentForEncryption" also resulted in a AES128 encrypted document, that could be opened by Adobe DC and still contained everything it shall contain (or so it seems). {code:java} if (version == 4) { dictionary.setSubFilter(SUBFILTER5); mdResult = MessageDigests.getSHA1().digest(shaInput); COSName aesVName = COSName.AESV2; prepareEncryptionDictAES(dictionary, aesVName, recipientsFields); } else if(version == 5) { dictionary.setSubFilter(SUBFILTER5); mdResult = MessageDigests.getSHA256().digest(shaInput); COSName aesVName = COSName.AESV3; prepareEncryptionDictAES(dictionary, aesVName, recipientsFields); } else { dictionary.setSubFilter(SUBFILTER4); mdResult = MessageDigests.getSHA1().digest(shaInput); dictionary.setRecipients(recipientsFields); } {code} Currently I would not agree to, that you were unssuccesful here - but maybe you missed two things? *Edit2:* I saved the decrypted document using doc.setAllSecurityToBeRemoved(true); to be sure, that PDFBox would do something with it and would not only write it back as is. The decrypted document also seems to be complete, readable and perfectly fine. I guess I will wait for your feedback now - either I am missing some obvious issues here, or this ticket had been nearly resolved then and is simply missing some minor details. *Edit3:* I am attaching the draft for my current suggestion as a patch file to this issue. This does currently not contain additional Tests (as may currently existing tests are not really helpful for automated testing) I did not yet run any of the PDFBox tests and it contains some further suggestions. (mostly pushing some functionality to super classes to avoid code redundancies.) This is simply a draft, so that you can reproduce what I am doing here. > Add support for AES128 encryption for public key > ------------------------------------------------ > > Key: PDFBOX-4421 > URL: https://issues.apache.org/jira/browse/PDFBOX-4421 > Project: PDFBox > Issue Type: Bug > Components: Crypto > Affects Versions: 2.0.13 > Reporter: Tilman Hausherr > Priority: Major > Labels: AES128 > Attachments: B2-Adobe-128-aes-sec.pdf, > PDFBOX-4421_Add_support_for_AES128_encryption_for_public_key_(DRAFT).patch, > image-2020-09-16-10-32-11-060.png, image-2020-09-16-10-33-55-201.png, > image-2020-09-16-11-55-33-275.png, keystore.pfx > > > Follow-up of PDFBOX-4413. AES256 works for public key crypto, but AES128 > doesn't when the file is generated by an external software. (local tests > work) We should at least get the decryption to work. -- This message was sent by Atlassian Jira (v8.3.4#803005) --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@pdfbox.apache.org For additional commands, e-mail: dev-h...@pdfbox.apache.org