[ 
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

Reply via email to