org.owasp.esapi.crypto.SecurityProviderLoader Maven / Gradle / Ivy
package org.owasp.esapi.crypto;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.util.Hashtable;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Logger;
/**
* This class provides a generic static method that loads a
* {@code java.security.Provider} either by some generic name
* (i.e., {@code Provider.getName()}) or by a fully-qualified class name.
* It is intended to be called dynamically by an application to add a
* specific JCE provider at runtime.
*
* If the {@code ESAPI.properties} file has a the property
* {@code ESAPI.PreferredJCEProvider} defined to either a recognized
* JCE provider (see below for list) or a fully qualified path name of
* that JCE provider's {@code Provider} class, then the reference implementation
* of ESAPI cryptography ({@code org.owasp.esapi.reference.crypto.JavaEncryptor})
* tries to load this specified JCE provider via
* {@link SecurityProviderLoader#insertProviderAt(String,int)}.
*
*/
public class SecurityProviderLoader {
private static Logger logger = ESAPI.getLogger("SecurityProviderLoader");
private static Hashtable jceProviders;
static {
//
// Load the table with known providers. We load the (short) JCE name
// and the corresponding provider class. We don't 'new' the actual
// class name here because that would mean we would have to have all
// these jars. Instead we use reflection and do it dynamically only
// when SecurityProviderLoader.insertProviderAt() is called because
// presumably they will have the jar in their classpath for the
// provider they wish to use.
//
jceProviders = new Hashtable();
// SunJCE is installed by default and should always be available
// with Sun-based JREs. As of JDK 1.3 and later, it is part of the
// standard JRE install.
jceProviders.put("SunJCE", "com.sun.crypto.provider.SunJCE");
// IBMJCE is default for WebSphere and is used by IBM JDKs. They
// also have IBMJCEFIPS, but not sure if this is *always* provided
// with WebSphere or just an add-on, hence not including it. IBMJCEFIPS
// is a FIPS 140-2 compliant JCE provider from IBM.
jceProviders.put("IBMJCE", "com.ibm.crypto.provider.IBMJCE");
// jceProviders.put("IBMJCEFIPS", "com.ibm.crypto.fips.provider.IBMJCEFIPS");
// GnuCrypto is JCE provider for GNU Compiler for Java (GCJ)
jceProviders.put("GnuCrypto", "gnu.crypto.jce.GnuCrypto");
// Bouncy Castle -- http://www.bouncycastle.org/ - FOSS, maintained.
jceProviders.put("BC",
"org.bouncycastle.jce.provider.BouncyCastleProvider");
// IAIK -- http://jce.iaik.tugraz.at/ -- No longer free.
jceProviders.put("IAIK", "iaik.security.provider.IAIK");
// IBM FIPS 140-2 compliant provider -- Commercial
// See above comments.
// jceProviders.put("IBMJCEFIPS", "com.ibm.crypto.fips.provider.IBMJCEFIPS");
// RSA FIPS 142-2 compliant provider -- Commercial
// jceProviders.put("RSA", "com.rsa.jsafe.crypto.CryptoJ");
// Cryptix -- http://www.cryptix.org/ - FOSS, not maintained.
// Cryptix JCE code signing cert expired 2009/08/29 and was not
// renewed.
jceProviders.put("CryptixCrypto", "cryptix.jce.provider.CryptixCrypto");
jceProviders.put("Cryptix", "cryptix.jce.provider.CryptixCrypto");
// ABA - FOSS, not maintained - Google for it, or maybe search for
// old copy at http://www.archive.org/
jceProviders.put("ABA", "au.net.aba.crypto.provider.ABAProvider");
}
/**
* This methods adds a provider to the {@code SecurityManager}
* either by some generic name or by the class name.
*
* The following generic JCE provider names are built-in:
*
* - SunJCE
* - IBMJCE [for WebSphere]
* - GnuCrypto [for use with GNU Compiler for Java, i.e., gcj]
* - BC [i.e., Bouncy Castle]
* - IAIK
* - CryptixCrypto (or Cryptix)
*
- ABA
*
* Note that neither Cryptix or ABA are actively maintained so
* it is recommended that you do not start using them for ESAPI
* unless your application already has a dependency on them. Furthermore,
* the Cryptix JCE jars likely will not work as the Cryptix code signing
* certificate has expired as of August 28, 2009. (This likely is true
* for ABA, but I can't even find a copy to download!). Lastly, the IAIK
* provider is no longer offered as free, open source. It is not a
* commercial product. See {@link "http://jce.iaik.tugraz.at/"} for
* details. While some older versions were offered free, it is not clear
* whether the accompanying license still allows you to use it, and if
* it does, whether or not the code signing certificate used to sign
* their JCE jar(s) has expired are not. Therefore, if you are looking
* for a FOSS alternative to SunJCE, Bouncy Castle
* ({@link "http://www.bouncycastle.org/"} is probably your best bet. The
* BC provider does support many the "combined cipher modes" that provide
* both confidentiality and authenticity. (See the {@code ESAPI.properties}
* property {@code Encryptor.cipher_modes.combined_modes} for details.)
*
* For those working in the U.S. federal government, it should be noted
* that none of the providers listed here are considered validated
* by NIST's Cryptographic Module Validation Program and are therefore
* not considered FIPS 140-2 compliant. There are a few approved
* JCE compatible Java libraries that are on NIST's CMVP list, but this
* list changes constantly so they are not listed here. For further details
* on NIST's CMVP, see
* {@link "http://csrc.nist.gov/groups/STM/cmvp/index.html"}.
*
* Finally, if you wish to use some other JCE provider not recognized above,
* you must specify the provider's fully-qualified class name (which in
* turn must have a public, no argument constructor).
*
* The application must be given the {@code SecurityPermission} with a
* value of {@code insertProvider.<provider_name>} (where
* <provider_name> is the name of the algorithm provider if
* a security manager is installed.
*
*
* @param algProvider Name of the JCE algorithm provider. If the name
* contains a ".", this is interpreted as the name
* of a {@code java.security.Provider} class name.
* @param pos The preference position (starting at 1) that the
* caller would like for this provider. If you wish
* for it to be installed as the last provider
* (as of the time of this call), set {@code pos} to -1.
* @return The actual preference position at which the provider was added,
* or -1 if the provider was not added because it is already
* installed.
* @exception NoSuchProviderException - thrown if the provider class
* could not be loaded or added to the {@code SecurityManager} or
* any other reason for failure.
*/
public static int insertProviderAt(String algProvider, int pos)
throws NoSuchProviderException
{
// We assume that if the algorithm provider contains a ".", then
// we interpret this as a crypto provider class name and dynamically
// add the provider. If it's one of the special ones we know about,
// we also dynamically create it. Otherwise, we assume the provider
// is in the "java.security" file.
Class providerClass = null;
String clzName = null;
Provider cryptoProvider = null;
// Yes; I'm aware of DeMorgan's Law, but this is easier to grok.
if ( ! ( (pos == -1 || pos >= 1) ) ) {
throw new IllegalArgumentException("Position pos must be -1 or integer >= 1");
}
try {
// Does algProvider look like a class name?
if (algProvider.indexOf('.') != -1) {
clzName = algProvider;
} else if ( jceProviders.containsKey(algProvider) ) {
// One of the special cases we know about.
clzName = jceProviders.get(algProvider);
} else {
throw new NoSuchProviderException("Unable to locate Provider class for " +
"provider " + algProvider + ". Try using fully qualified class name " +
"or check provider name for typos. Builtin provider names are: " +
jceProviders.toString());
}
providerClass = Class.forName(clzName);
cryptoProvider = (Provider)providerClass.newInstance();
// Found from above. Note that Security.insertProviderAt() can
// throw a SecurityException if a Java SecurityManager is
// installed and application doesn't have appropriate
// permissions in policy file.
//
// However, since SecurityException is a RuntimeException it
// doesn't need to be explicitly declared on the throws clause.
// The application must be given the SecurityPermission with
// a value of "insertProvider." (where
// is the name of the algorithm provider) if
// a SecurityManager is installed.
int ret;
if ( pos == -1 ) { // Special case: Means place _last_.
ret = Security.addProvider(cryptoProvider);
} else {
ret = Security.insertProviderAt(cryptoProvider, pos);
}
if ( ret == -1 ) {
// log INFO that provider was already loaded.
String msg = "JCE provider '" + algProvider + "' already loaded";
if (pos == -1) {
// The just wanted it available (loaded last) and it is, so
// this is not critical.
logger.always(Logger.SECURITY_SUCCESS, msg);
} else {
// In this case, it's a warning because it may have already
// been loaded, but *after* the position they requested.
// For example, if they were trying to load a FIPS 140-2
// compliant JCE provider at the first position and it was
// already loaded at position 3, then this is not FIPS 140-2
// compliant. Therefore, we make it a warning and a failure.
// Also log separately using 'always' in case warnings suppressed
// as per NSA suggestion.
logger.warning(Logger.SECURITY_FAILURE, msg);
logger.always(Logger.SECURITY_FAILURE, "(audit) " + msg);
}
} else {
// As per NSA suggestion.
logger.always(Logger.SECURITY_AUDIT,
"Successfully loaded preferred JCE provider " +
algProvider + " at position " + pos);
}
return ret;
} catch(SecurityException ex) {
// CHECKME: Log security event here too? This is a RuntimeException.
// It would only be thrown if a SecurityManager is installed that
// prohibits Security.addProvider() or Security.insertProviderAt()
// by the current user of this thread. Will log it here. Can always
// be ignored.
logger.always(Logger.SECURITY_FAILURE, "Failed to load preferred JCE provider " +
algProvider + " at position " + pos, ex);
throw ex;
} catch(Exception ex) {
// Possibilities include: ClassNotFoundException,
// InstantiationException, and others???
//
// Log an error & re-throw original message as NoSuchProviderException,
// since that what it probably really implied here. This probably a configuration
// error (e.g., classpath problem, etc.) so we use EVENT_FAILURE rather than
// SECURITY_FAILURE here.
logger.error(Logger.EVENT_FAILURE, "Failed to insert failed crypto " +
" provider " + algProvider + " at position " + pos, ex);
throw new NoSuchProviderException("Failed to insert crypto " +
" provider for " + algProvider +
"; exception msg: " + ex.toString());
}
}
/**
* Load the preferred JCE provider for ESAPI based on the ESAPI.properties
* property {@code Encryptor.PreferredJCEProvider}. If this property is null
* (i.e., unset) or set to an empty string, then no JCE provider is inserted
* at the "preferred" position and thus the Java VM continues to use whatever
* the default it was using for this (generally specified in the file
* {@code $JAVA_HOME/jre/security/java.security}).
* @return The actual preference position at which the provider was added,
* (which is expected to be 1) or -1 if the provider was not added
* because it is already installed at some other position. -1 is also
* returned if the {@code Encryptor.PreferredJCEProvider} was not set
* or set to an empty string, i.e., if the application has no
* preferred JCE provider.
* @exception NoSuchProviderException - thrown if the provider class
* could not be loaded or added to the {@code SecurityManager} or
* any other reason for failure.
* @see
* ESAPI 2.0 Symmetric Encryption User Guide
*/
public static int loadESAPIPreferredJCEProvider() throws NoSuchProviderException
{
String prefJCEProvider =
ESAPI.securityConfiguration().getPreferredJCEProvider();
try {
// If unset or set to empty string, then don't try to change it.
if ( prefJCEProvider == null || prefJCEProvider.trim().length() == 0) {
// Always log, per NSA suggestion.
logger.always(Logger.SECURITY_AUDIT, "No Encryptor.PreferredJCEProvider specified.");
return -1; // Unchanged; it is, whatever it is.
} else {
return insertProviderAt(prefJCEProvider, 1);
}
} catch (NoSuchProviderException ex) {
// Will already have logged with exception msg.
String msg = "failed to load *preferred* " + "JCE crypto provider, " + prefJCEProvider;
logger.always(Logger.SECURITY_AUDIT, msg); // Per NSA suggestion.
logger.error(Logger.SECURITY_FAILURE, msg);
throw ex;
}
}
}