/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icepdf.core.pobjects.security;
import org.icepdf.core.exceptions.PDFSecurityException;
import org.icepdf.core.pobjects.Reference;
import org.icepdf.core.util.Defs;
import org.icepdf.core.util.Library;
import java.io.InputStream;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>The Security Manager class manages the encryption of encrypted
* PDF documents. The class is initiated by the Document class if a
* Crypt key is found in the document's trailer. The singleton pattern
* is implemented so that it can be called from anywhere with the PDF
* object structure.</p>
* <br>
* <p>There is currently only support for Adobe Standard encryption which is
* supported by the StandardSecurityHandler. Additional support for custom
* security handlers, public-key handlers and crypt filters is currently under
* development.</p>
* <br>
* <p>The Security Manager needs tobe compliant with Sun Java JCE 1.2.1 implementation.
* The security manager assumes that
* org.bouncycastle.jce.provider.BouncyCastleProvider can be found on the class
* path and will try to load the class accordingly. However, if you have another
* crypto API that you would like to use, the system property
* org.icepdf.core.pobjects.security.provider can be set to the provider's class path.</p>
*
* @since 1.1
*/
public class SecurityManager {
private static final Logger logger =
Logger.getLogger(SecurityManager.class.toString());
// Default Encryption dictionary, which also contians keys need for
// standard, crypt and public security handlers.
private EncryptionDictionary encryptDictionary = null;
// Pointer to class which implements the SecurityHandler interface
private SecurityHandler securityHandler = null;
// flag for detecting JCE
private static boolean foundJCE = false;
// key caches, fairly expensive calculation
private byte[] encryptionKey;
private byte[] decryptionKey;
// Add security provider of choice before Sun RSA provider (if any)
static {
// Load security handler from system property if possible
String defaultSecurityProvider =
"org.bouncycastle.jce.provider.BouncyCastleProvider";
// check system property security provider
String customSecurityProvider =
Defs.sysProperty("org.icepdf.core.security.jceProvider");
// if no custom security provider load default security provider
if (customSecurityProvider != null) {
defaultSecurityProvider = customSecurityProvider;
}
try {
// try and create a new provider
Object provider = Class.forName(defaultSecurityProvider).newInstance();
Security.addProvider((Provider) provider);
} catch (ClassNotFoundException e) {
logger.log(Level.FINE, "Optional BouncyCastle security provider not found");
} catch (InstantiationException e) {
logger.log(Level.FINE, "Optional BouncyCastle security provider could not be instantiated");
} catch (IllegalAccessException e) {
logger.log(Level.FINE, "Optional BouncyCastle security provider could not be created");
}
try {
Class.forName("javax.crypto.Cipher");
foundJCE = true;
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, "Sun JCE Support Not Found");
}
}
/**
* Disposes of the security handler instance.
*/
public void dispose() {
}
/**
* Creates new instance of SecurityManager object.
*
* @param library library of documents PDF objects
* @param encryptionDictionary encryption dictionary key values
* @param fileID fileID of PDF document
* @throws PDFSecurityException if the security provider could not be found
*/
public SecurityManager(Library library, HashMap<Object, Object> encryptionDictionary,
List fileID)
throws PDFSecurityException {
// Check to make sure that if run under JDK 1.3 that the JCE libraries
// are installed as extra packages
if (!foundJCE) {
logger.log(Level.SEVERE, "Sun JCE support was not found on classpath");
throw new PDFSecurityException("Sun JCE Support Not Found");
}
// create dictionary for document
encryptDictionary =
new EncryptionDictionary(library, encryptionDictionary, fileID);
// create security Handler based on dictionary entries.
if (encryptDictionary.getPreferredSecurityHandlerName().getName().
equalsIgnoreCase("Standard")) {
securityHandler = new StandardSecurityHandler(encryptDictionary);
// initiate the handler
securityHandler.init();
} else {
throw new PDFSecurityException("Security Provider Not Found.");
}
}
/**
* Gets the permission associated with the document's encryption handler.
*
* @return permission object
*/
public Permissions getPermissions() {
return securityHandler.getPermissions();
}
/**
* Gets the SecurityHandler associated with this Security Manager.
*
* @return security handler object.
*/
public SecurityHandler getSecurityHandler() {
return securityHandler;
}
/**
* Gets the encryption dictionary associated with the document encryption
* handler.
*
* @return encryption dictionary
*/
public EncryptionDictionary getEncryptionDictionary() {
return encryptDictionary;
}
/**
* Gets the encryption key used by the security handler when encrypting data.
*
* @return encryption key used to encrypt the data
*/
public byte[] getEncryptionKey() {
if (encryptionKey == null) {
encryptionKey = securityHandler.getEncryptionKey();
}
return encryptionKey;
}
/**
* Gets the decrypt key used by the security handler when decrypting data.
*
* @return decryption key used to encrypt the data
*/
public byte[] getDecryptionKey() {
if (decryptionKey == null) {
decryptionKey = securityHandler.getDecryptionKey();
}
return decryptionKey;
}
/**
* Encrypt the <code>data</code> using the <code>encryptionKey</code> and
* <code>objectReference</code> of the PDF stream or String object.
*
* @param objectReference PDF objects number and revision number
* @param encryptionKey encryption key used to encrypt the data
* @param data byte data of a PDF Stream or String object
* @return encrypted data
*/
public byte[] encrypt(Reference objectReference,
byte[] encryptionKey,
byte[] data) {
return securityHandler.encrypt(objectReference, encryptionKey, data);
}
/**
* Decrypt the <code>data</code> using the <code>encryptionKey</code> and
* <code>objectReference</code> of the PDF stream or String object.
*
* @param objectReference PDF objects number and revision number
* @param encryptionKey encryption key used to decrypt the data
* @param data byte data of a PDF Stream or String object
* @return decrypted data
*/
public byte[] decrypt(Reference objectReference,
byte[] encryptionKey,
byte[] data) {
return securityHandler.decrypt(objectReference, encryptionKey, data);
}
/**
* Return a new InputStream, from which read operations will return
* data, read and decrypt from the InputStream parameter
* <code>objectReference</code> of the PDF stream or String object.
*
* @param objectReference PDF objects number and revision number
* @param encryptionKey encryption key used to decrypt the data
* @param input InputStream giving access to encrypted data
* @param decodeParams crypt filter optional parameters, can be null.
* @param returnInputIfNullResult If results end up being null, then return input instead of null
* @return InputStream giving access to decrypted data
*/
public InputStream decryptInputStream(
Reference objectReference,
byte[] encryptionKey,
HashMap decodeParams,
InputStream input,
boolean returnInputIfNullResult) {
InputStream result = securityHandler.decryptInputStream(
objectReference, encryptionKey, decodeParams, input);
if (returnInputIfNullResult && result == null)
result = input;
return result;
}
/**
* Return a new InputStream, from which read operations will return
* data, read and decrypt from the InputStream parameter
* <code>objectReference</code> of the PDF stream or String object.
*
* @param objectReference PDF objects number and revision number
* @param encryptionKey encryption key used to decrypt the data
* @param input InputStream giving access to encrypted data
* @param decodeParams crypt filter optional parameters, can be null.
* @param returnInputIfNullResult If results end up being null, then return input instead of null
* @return InputStream giving access to decrypted data
*/
public InputStream encryptInputStream(
Reference objectReference,
byte[] encryptionKey,
HashMap decodeParams,
InputStream input,
boolean returnInputIfNullResult) {
InputStream result = securityHandler.encryptInputStream(
objectReference, encryptionKey, decodeParams, input);
if (returnInputIfNullResult && result == null)
result = input;
return result;
}
/**
* Determines whether the supplied password is authorized to view the
* PDF document. If a password is rejected, the user should be restricted
* from viewing the document.
*
* @param password password to authorize
* @return true, if the password was authorized successfully; false, otherwise.
*/
public boolean isAuthorized(String password) {
return securityHandler.isAuthorized(password);
}
}