Browse Source

PDF Encryption works now in every case I tested. Also, the text and string dictionary entries of PDFInfo are encrypted. The rest of these value still need to be done (best done in one task with refactoring PDF dict generation).

Bring back support for encrypting byte arrays.


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196171 13f79535-47bb-0310-9956-ffa450edef68
tags/Root_Temp_KnuthStylePageBreaking
Jeremias Maerki 21 years ago
parent
commit
cbdc818ef5

+ 10
- 1
src/java/org/apache/fop/pdf/PDFEncryption.java View File

@@ -71,7 +71,16 @@ public interface PDFEncryption {
* Adds a PDFFilter to the PDFStream object
* @param stream the stream to add an encryption filter to
*/
void applyFilter(PDFStream stream);
void applyFilter(AbstractPDFStream stream);
/**
* Encrypt an array of bytes using a reference PDFObject for calculating
* the encryption key.
* @param data data to encrypt
* @param refObj reference PDFObject
* @return byte[] the encrypted data
*/
byte[] encrypt(byte[] data, PDFObject refObj);
/**
* Returns the trailer entry for encryption.

+ 78
- 49
src/java/org/apache/fop/pdf/PDFEncryptionJCE.java View File

@@ -54,12 +54,12 @@ package org.apache.fop.pdf;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.BadPaddingException;
import javax.crypto.NoSuchPaddingException;
@@ -89,6 +89,8 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
this.encryption = encryption;
this.number = number;
this.generation = generation;
//System.out.println("new encryption filter for number "
// +number+" and generation "+generation);
}

/**
@@ -128,6 +130,14 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
out.write(buffer);
}
/**
* @see org.apache.fop.pdf.PDFFilter#applyFilter(OutputStream)
*/
public OutputStream applyFilter(OutputStream out) throws IOException {
return new CipherOutputStream(out,
encryption.initCipher(number, generation));
}

}

private static final char [] PAD =
@@ -135,9 +145,6 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A };
private static final char[] DIGITS =
{'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/** Value of PRINT permission */
public static final int PERMISSION_PRINT = 4;
@@ -150,7 +157,7 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
// Encryption tools
private MessageDigest digest = null;
private Cipher cipher = null;
//private Cipher cipher = null;
private Random random = new Random();
// Control attributes
private PDFEncryptionParams params;
@@ -160,20 +167,21 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
private String dictionary = null;

/**
* create a /Filter /Standard object.
* Create a /Filter /Standard object.
*
* @param number the object's number
* @param objnum the object's number
*/
public PDFEncryptionJCE(int number) {
public PDFEncryptionJCE(int objnum) {
/* generic creation of object */
super(number);
super();
setObjectNumber(objnum);
try {
digest = MessageDigest.getInstance("MD5");
cipher = Cipher.getInstance("RC4");
//cipher = Cipher.getInstance("RC4");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e.getMessage());
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException(e.getMessage());
/*} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException(e.getMessage());*/
}
}

@@ -227,17 +235,6 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
return obuffer;
}

private String toHex(byte[] value) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < value.length; i++) {
buffer.append(DIGITS[(value[i] >>> 4) & 0x0F]);
buffer.append(DIGITS[value[i] & 0x0F]);
}
return buffer.toString();
}
/**
* Returns the document file ID
* @return The file ID
@@ -258,38 +255,63 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
*/
public String getFileID(int index) {
if (index == 1) {
return toHex(getFileID());
return PDFText.toHex(getFileID());
}
byte[] id = new byte[16];
random.nextBytes(id);
return toHex(id);
return PDFText.toHex(id);
}
private byte[] encryptWithKey(byte[] data, byte[] key) {
try {
SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
cipher.init(Cipher.ENCRYPT_MODE, keyspec);
return cipher.doFinal(data);
final Cipher c = initCipher(key);
return c.doFinal(data);
} catch (IllegalBlockSizeException e) {
throw new IllegalStateException(e.getMessage());
} catch (BadPaddingException e) {
throw new IllegalStateException(e.getMessage());
}
}
private Cipher initCipher(byte[] key) {
try {
Cipher c = Cipher.getInstance("RC4");
SecretKeySpec keyspec = new SecretKeySpec(key, "RC4");
c.init(Cipher.ENCRYPT_MODE, keyspec);
return c;
} catch (InvalidKeyException e) {
throw new IllegalStateException(e.getMessage());
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e.getMessage());
} catch (NoSuchPaddingException e) {
throw new UnsupportedOperationException(e.getMessage());
}
}
private Cipher initCipher(int number, int generation) {
byte[] hash = calcHash(number, generation);
int size = hash.length;
hash = digest.digest(hash);
byte[] key = calcKey(hash, size);
return initCipher(key);
}
private byte[] encryptWithHash(byte[] data, byte[] hash, int size) {
hash = digest.digest(hash);

byte[] key = calcKey(hash, size);
return encryptWithKey(data, key);
}

private byte[] calcKey(byte[] hash, int size) {
byte[] key = new byte[size];

for (int i = 0; i < size; i++) {
key[i] = hash[i];
}
return encryptWithKey(data, key);
return key;
}

/**
@@ -344,14 +366,14 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
byte[] uValue = encryptWithKey(prepPassword(""), this.encryptionKey);
// Create the dictionary
this.dictionary = this.number + " " + this.generation
+ " obj\n<< /Filter /Standard\n"
this.dictionary = getObjectID()
+ "<< /Filter /Standard\n"
+ "/V 1\n"
+ "/R 2\n"
+ "/Length 40\n"
+ "/P " + permissions + "\n"
+ "/O <" + toHex(oValue) + ">\n"
+ "/U <" + toHex(uValue) + ">\n"
+ "/O " + PDFText.toHex(oValue) + "\n"
+ "/U " + PDFText.toHex(uValue) + "\n"
+ ">>\n"
+ "endobj\n";
}
@@ -367,11 +389,23 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
if (this.encryptionKey == null) {
throw new IllegalStateException("PDF Encryption has not been initialized");
}
//getDocument().getLogger().debug("encrypting with for "+number+" "+generation);

byte[] hash = calcHash(number, generation);
return encryptWithHash(data, hash, hash.length);
}

/**
* @see org.apache.fop.pdf.PDFEncryption#encrypt(byte[], PDFObject)
*/
public byte[] encrypt(byte[] data, PDFObject refObj) {
return encryptData(data, refObj.getObjectNumber(), refObj.getGeneration());
}

private byte[] calcHash(int number, int generation) {
byte[] hash = new byte[this.encryptionKey.length + 5];
int i = 0;
while (i < this.encryptionKey.length) {
hash[i] = this.encryptionKey[i]; i++;
}
@@ -381,8 +415,7 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
hash[i++] = (byte) (number >>> 16);
hash[i++] = (byte) (generation >>> 0);
hash[i++] = (byte) (generation >>> 8);;
return encryptWithHash(data, hash, hash.length);
return hash;
}

/**
@@ -399,8 +432,9 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
* Adds a PDFFilter to the PDFStream object
* @param stream the stream to add an encryption filter to
*/
public void applyFilter(PDFStream stream) {
stream.addFilter(this.makeFilter(stream.number, stream.generation));
public void applyFilter(AbstractPDFStream stream) {
stream.getFilterList().addFilter(
this.makeFilter(stream.getObjectNumber(), stream.getGeneration()));
}
/**
@@ -413,20 +447,15 @@ public class PDFEncryptionJCE extends PDFObject implements PDFEncryption {
throw new IllegalStateException("PDF Encryption has not been initialized");
}
try {
return this.dictionary.getBytes(PDFDocument.ENCODING);
} catch (UnsupportedEncodingException ue) {
return this.dictionary.getBytes();
}
return encode(this.dictionary);
}

/**
* @see org.apache.fop.pdf.PDFEncryption#getTrailerEntry()
*/
public String getTrailerEntry() {
return "/Encrypt " + number + " "
+ generation + " R\n"
+ "/ID[<" + getFileID(1) + "><"
+ getFileID(2) + ">]\n";
return "/Encrypt " + getObjectNumber() + " "
+ getGeneration() + " R\n"
+ "/ID[" + getFileID(1) + getFileID(2) + "]\n";
}
}

Loading…
Cancel
Save