Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into GH-314.redaction
Browse files Browse the repository at this point in the history
# Conflicts:
#	core/core-awt/src/main/java/org/icepdf/core/pobjects/HexStringObject.java
#	core/core-awt/src/main/java/org/icepdf/core/pobjects/LiteralStringObject.java
#	core/core-awt/src/main/java/org/icepdf/core/pobjects/StringObject.java
#	core/core-awt/src/main/java/org/icepdf/core/util/updater/writeables/BaseWriter.java
#	core/core-awt/src/main/java/org/icepdf/core/util/updater/writeables/StreamWriter.java
  • Loading branch information
Patrick Corless committed Jan 30, 2024
2 parents a4899aa + 43c9403 commit 25c2140
Show file tree
Hide file tree
Showing 35 changed files with 287 additions and 348 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ ICEpdf is an open source project and is always looking for more contributors. T
<dependency>
<groupId>com.github.pcorless.icepdf</groupId>
<artifactId>icepdf-core</artifactId>
<version>7.1.3</version>
<version>7.1.4</version>
</dependency>
<dependency>
<groupId>com.github.pcorless.icepdf</groupId>
<artifactId>icepdf-viewer</artifactId>
<version>7.1.3</version>
<version>7.1.4</version>
</dependency>
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.icepdf.core.pobjects;

import org.icepdf.core.pobjects.security.SecurityManager;
import org.icepdf.core.util.Utils;

public abstract class AbstractStringObject implements StringObject {

// Reference is need for standard encryption
protected Reference reference;
// if isModified, string is always unencrypted, otherwise is the raw string data which can be
// encrypted or not.
protected StringBuilder stringData;

// modified string need to be encrypted when writing to file.
protected boolean isModified;

/**
* Gets the decrypted stringData value of the data using the key provided by the
* security manager.
*
* @param securityManager security manager associated with parent document.
*/
public String getDecryptedLiteralString(SecurityManager securityManager) {
if (!isModified) {
return encryption(getLiteralString(), reference, securityManager);
} else {
return getLiteralString();
}
}

/**
* Decrypts or encrypts a string.
*
* @param string string to encrypt or decrypt
* @param securityManager security manager for document.
* @return encrypted or decrypted string, depends on value of decrypt param.
*/
public String encryption(String string, Reference reference, SecurityManager securityManager) {
// get the security manager instance
if (securityManager != null && reference != null) {
// get the key
byte[] key = securityManager.getDecryptionKey();

// convert string to bytes.
byte[] textBytes = Utils.convertByteCharSequenceToByteArray(string);

// Decrypt/encrypt String
textBytes = securityManager.decrypt(reference, key, textBytes);

// convert back to a string
return Utils.convertByteArrayToByteString(textBytes);
}
return string;
}

/**
* Sets the parent PDF object's reference.
*
* @param reference parent object reference.
*/
public void setReference(Reference reference) {
this.reference = reference;
}

/**
* Indicates a string has been modified.
*
* @return string has been modified and may no longer be encrypted.
*/
@Override
public boolean isModified() {
return isModified;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,7 @@ public void setNew(boolean aNew) {
* @return string value of the newly set string which will always be decrypted.
*/
protected String setString(final Name key, String value) {
// make sure we store an encrypted documents string as encrypted
entries.put(key, new LiteralStringObject(value));
entries.put(key, new LiteralStringObject(value, getPObjectReference()));
return value;
}

Expand All @@ -237,8 +236,7 @@ protected String setString(final Name key, String value) {
* @return string value of the newly set string which will always be decrypted.
*/
protected String setHexString(final Name key, String value) {
// make sure we store an encrypted documents string as encrypted
entries.put(key, new HexStringObject(value));
entries.put(key, HexStringObject.createHexString(value));
return value;
}

Expand Down
43 changes: 23 additions & 20 deletions core/core-awt/src/main/java/org/icepdf/core/pobjects/Document.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ public static String getLibraryVersion() {
private static boolean isCachingEnabled;

private final Library library;
// todo put file channel input library?

private FileChannel documentFileChannel;
private RandomAccessFile randomAccessFile;
private ByteBuffer documentByteBuffer;
private CrossReferenceRoot crossReferenceRoot;

Expand Down Expand Up @@ -192,17 +193,24 @@ public void setFile(String filepath)
setDocumentOrigin(filepath);

File file = new File(filepath);
try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) {
documentFileChannel = randomAccessFile.getChannel();
ByteBuffer mappedFileByteBuffer = documentFileChannel.map(
FileChannel.MapMode.READ_ONLY, 0, documentFileChannel.size());
setInputStream(mappedFileByteBuffer);
try {
setInputStream(copyFileToByteBuffer(file));
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to set document file path", e);
throw e;
}
}

private ByteBuffer copyFileToByteBuffer(File file) throws IOException {
randomAccessFile = new RandomAccessFile(file, "r");
documentFileChannel = randomAccessFile.getChannel();
long fileSize = documentFileChannel.size();
ByteBuffer buffer = ByteBuffer.allocate((int) fileSize);
documentFileChannel.read(buffer);
buffer.flip();
return buffer;
}

/**
* Load a PDF file from the given URL and initiates the document's Catalog.
* If the system property org.icepdf.core.streamcache.enabled=true, the file
Expand Down Expand Up @@ -267,11 +275,8 @@ public void setInputStream(InputStream inputStream, String pathOrURL)

setDocumentCachedFilePath(tempFile.getAbsolutePath());

try (RandomAccessFile randomAccessFile = new RandomAccessFile(tempFile, "r")) {
documentFileChannel = randomAccessFile.getChannel();
ByteBuffer mappedFileByteBuffer = documentFileChannel.map(
FileChannel.MapMode.READ_ONLY, 0, documentFileChannel.size());
setInputStream(mappedFileByteBuffer);
try {
setInputStream(copyFileToByteBuffer(tempFile));
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to set document input stream", e);
throw e;
Expand Down Expand Up @@ -322,11 +327,8 @@ public void setByteArray(byte[] data, int offset, int length, String pathOrURL)

setDocumentCachedFilePath(tempFile.getAbsolutePath());

try (RandomAccessFile randomAccessFile = new RandomAccessFile(tempFile, "r")) {
documentFileChannel = randomAccessFile.getChannel();
ByteBuffer mappedFileByteBuffer = documentFileChannel.map(
FileChannel.MapMode.READ_ONLY, 0, documentFileChannel.size());
setInputStream(mappedFileByteBuffer);
try {
setInputStream(copyFileToByteBuffer(tempFile));
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to set document input stream", e);
throw e;
Expand Down Expand Up @@ -531,13 +533,14 @@ public void paintPage(int pageNumber, Graphics g, final int renderHintType,
* Dispose of Document, freeing up all used resources.
*/
public void dispose() {

if (documentFileChannel != null) {
// clean up file it will clean up any file channels and file descriptors too
if (randomAccessFile != null) {
try {
documentFileChannel.close();
randomAccessFile.close();
} catch (IOException e) {
logger.log(Level.FINE, "Error closing document input stream.", e);
logger.log(Level.FINE, "Error closing document random access file.", e);
}
randomAccessFile = null;
documentFileChannel = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import org.icepdf.core.pobjects.fonts.Font;
import org.icepdf.core.pobjects.fonts.FontFile;
import org.icepdf.core.pobjects.security.SecurityManager;
import org.icepdf.core.util.StringOffsetBuilder;
import org.icepdf.core.util.Utils;

import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -31,36 +29,19 @@
*
* @since 2.0
*/
public class HexStringObject implements StringObject {
public class HexStringObject extends AbstractStringObject {

private static final Logger logger =
Logger.getLogger(HexStringObject.class.toString());

// core data used to represent the literal string information
private StringBuilder stringData;

// Reference is need for standard encryption
Reference reference;

/**
* <p>Creates a new hexadecimal string object so that it represents the same
* sequence of bytes as in the bytes argument. In other words, the
* initial content of the hexadecimal string is the characters represented
* by the byte data.</p>
*
* @param bytes array of bytes which will be interpreted as hexadecimal
* data.
*/
public HexStringObject(byte[] bytes) {
this(new StringBuilder(bytes.length).append(new String(bytes)));
}

/**
* <p>Creates a new hexadecimal string object so that it represents the same
* sequence of character data specified by the argument. This constructor should
* only be used in the context of the parser which has leading and ending
* angled brackets which are removed by this method.</p>
*
* Old parser, just used for cmap parsing now.
*
* @param stringBuffer the initial contents of the hexadecimal string object
*/
public HexStringObject(StringBuilder stringBuffer) {
Expand All @@ -72,32 +53,19 @@ public HexStringObject(StringBuilder stringBuffer) {
stringData.append(normalizeHex(stringBuffer, 2).toString());
}

/**
* Content and object parser hex string creation
*
* @param string raw hex string
*/
public HexStringObject(String string) {
stringData = new StringBuilder(string.length());
stringData.append(normalizeHex(new StringBuilder(string), 2).toString());
}

/**
* <p>Creates a new literal string object so that it represents the same
* sequence of character data specified by the arguments. The string
* value is assumed to be unencrypted and will be encrypted. The
* method #LiteralStringObject(String string) should be used if the string
* is all ready encrypted. This method is used for creating new
* LiteralStringObject's that are created post document parse. </p>
*
* @param string the initial contents of the literal string object,
* unencrypted.
* @param reference of parent PObject
* @param securityManager security manager used ot encrypt the string.
*/
public HexStringObject(String string, Reference reference,
SecurityManager securityManager) {
// append string data
this.reference = reference;
// convert string data to hex encoded
stringData = encodeHexString(string);
// save and encrypt the hex value. TODO: encryption still not working correctly, likely a -> byte[] error.
stringData = new StringBuilder(encryption(stringData.toString(), false, securityManager));
public static HexStringObject createHexString(String literalstring) {
StringBuilder hexString = encodeHexString(literalstring);
return new HexStringObject(hexString.toString());
}

/**
Expand All @@ -107,9 +75,9 @@ public HexStringObject(String string, Reference reference,
* @param contents string to be encoded into hex format.
* @return original content stream with contents encoded in the hex string format.
*/
private StringBuilder encodeHexString(String contents) {
public static StringBuilder encodeHexString(String contents) {
StringBuilder hex = new StringBuilder();
if (contents != null && contents.length() > 0) {
if (contents != null && !contents.isEmpty()) {
char[] chars = contents.toCharArray();
hex.append("FEFF");
String hexCode;
Expand Down Expand Up @@ -399,85 +367,4 @@ public StringBuilder getRawHexToString() {
return sb;
}

/**
* Sets the parent PDF object's reference.
*
* @param reference parent object reference.
*/
public void setReference(Reference reference) {
this.reference = reference;
}

/**
* Sets the parent PDF object's reference.
*
* @return returns the reference used for encryption.
*/
public Reference getReference() {
return reference;
}

/**
* Gets the decrypted literal string value of the data using the key provided by the
* security manager.
*
* @param securityManager security manager associated with parent document.
*/
public String getDecryptedLiteralString(SecurityManager securityManager) {
// get the security manager instance
if (securityManager != null && reference != null) {
// get the key
byte[] key = securityManager.getDecryptionKey();

// convert string to bytes.
byte[] textBytes =
Utils.convertByteCharSequenceToByteArray(getLiteralString());

// Decrypt String
textBytes = securityManager.decrypt(reference,
key,
textBytes);

// convert back to a string
return Utils.convertByteArrayToByteString(textBytes);
}
return getLiteralString();
}

/**
* Decrypts or encrypts a string.
*
* @param string string to encrypt or decrypt
* @param decrypt true to decrypt string, false otherwise;
* @param securityManager security manager for document.
* @return encrypted or decrypted string, depends on value of decrypt param.
*/
public String encryption(String string, boolean decrypt,
SecurityManager securityManager) {
// get the security manager instance
if (securityManager != null && reference != null) {
// get the key
byte[] key = securityManager.getDecryptionKey();

// convert string to bytes.
byte[] textBytes =
Utils.convertByteCharSequenceToByteArray(string);

// Decrypt String
if (decrypt) {
textBytes = securityManager.decrypt(reference,
key,
textBytes);
} else {
textBytes = securityManager.encrypt(reference,
key,
textBytes);
}

// convert back to a string
return Utils.convertByteArrayToByteString(textBytes);
}
return string;
}

}
Loading

0 comments on commit 25c2140

Please sign in to comment.