All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.owasp.esapi.crypto.CryptoToken Maven / Gradle / Ivy

/**
 * OWASP Enterprise Security API (ESAPI)
 * 
 * This file is part of the Open Web Application Security Project (OWASP)
 * Enterprise Security API (ESAPI) project. For details, please see
 * http://www.owasp.org/index.php/ESAPI.
 *
 * Copyright © 2010 - The OWASP Foundation
 * 
 * The ESAPI is published by OWASP under the BSD license. You should read and
 * accept the LICENSE before you use, modify, and/or redistribute this software.
 * 
 * @created 2010
 */
package org.owasp.esapi.crypto;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Encryptor;
import org.owasp.esapi.Logger;
import org.owasp.esapi.errors.EncodingException;
import org.owasp.esapi.errors.EncryptionException;
import org.owasp.esapi.errors.EncryptionRuntimeException;
import org.owasp.esapi.errors.ConfigurationException;
import org.owasp.esapi.errors.ValidationException;

///// IMPORTANT NOTE: Never print / log attribute *values* as they
/////                 may be sensitive. Also, do not log the CryptoToken
/////                 itself, because even it is encrypted, the token itself
/////                 is often used as an authentication token.

/**
 * Compute a cryptographically secure, encrypted token containing
 * optional name/value pairs. The cryptographic token is computed
 * like this:
 * 
 *     username;expiration_time;[<attr1>;<attr2>;...;<attrN>;]
 * 
* where * username is a user account name. Defaults to <anonymous> if * not set and it is always converted to lower case as per the rules of the * default locale. (Note this lower case conversion is consistent with the * default reference implementation of ESAPI's {@code User} interface.) *
* expiration_time is time (in milliseconds) after which the encrypted * token is considered invalid (i.e., expired). The time is stored as * milliseconds since midnight, January 1, 1970 UTC, and optional attributes *
*   <attr1>;<attr2>;...<attrN>; *
* are optional semicolon (';') separated name/value pairs, where each * name/value pair has the form: *
 *         name=[value]        (value may be empty, but not null)
 * 
* The attribute value may contain any value. However, values containing * either '=' or ';' will be quoted using '\'. Likewise, values containing '\' * will also be quoted using '\'. Hence if original name/value pair were * name=ab=xy\; * this would be represented as name=ab\=xy\\\;. * To ensure things are "safe" (from a security perspective), attribute * names must conform the the Java regular expression *
 *          [A-Za-z0-9_\.-]+
 * 
* The attribute value on the other hand, may be any valid string. (That * is, the value is not checked, so beware! When rendering these values, output * encoding may be required to prevent XSS. Use ESAPI's {@code Encoder} for that. *

* This entire semicolon-separated string is then encrypted via one of the * {@code Encryptor.encrypt()} methods and then base64-encoded, serialized * IV + ciphertext + MAC representation as determined by * {@code CipherTextasPortableSerializedByteArray()} is used as the * resulting cryptographic token. *

* The attributes are sorted by attribute name and the attribute names * must be unique. There are some restrictions on the attribute names. * (See the {@link #setAttribute(String, String)} method for details.) * * WARNING: You should never print / log attribute values as * they may be sensitive. Also, do not log the {@code CryptoToken} * itself, because even though it is encrypted, the token itself is * often used as an authentication token. * * @author [email protected] * @since 2.0 */ public class CryptoToken { /** Represents an anonymous user. */ public static final String ANONYMOUS_USER = ""; // Default expiration time private static final long DEFAULT_EXP_TIME = 5 * 60 * 1000; // 5 min == 300 milliseconds private static final String DELIM = ";"; // field delimiter private static final char DELIM_CHAR = ';'; // field delim as a char private static final char QUOTE_CHAR = '\\'; // char used to quote delimiters, '=' and itself. // OPEN ISSUE: Should we make these 2 regex's properties in ESAPI.properties??? private static final String ATTR_NAME_REGEX = "[A-Za-z0-9_.-]+"; // One or more alphanumeric, underscore, periods, or hyphens. private static final String USERNAME_REGEX = "[a-z][a-z0-9_.@-]*"; private static Logger logger = ESAPI.getLogger("CryptoToken"); private String username = ANONYMOUS_USER; // Default user name if not set. Always lower case. private long expirationTime = 0L; // This probably needed be sorted. A HashMap would do as well. // But this might make debugging a bit easier, so why not? private TreeMap attributes = new TreeMap(); private transient SecretKey secretKey = null; private Pattern attrNameRegex = Pattern.compile(ATTR_NAME_REGEX); private Pattern userNameRegex = Pattern.compile(USERNAME_REGEX); /** * Create a cryptographic token using default secret key from the * ESAPI.properties property Encryptor.MasterKey. */ public CryptoToken() { secretKey = getDefaultSecretKey( ESAPI.securityConfiguration().getEncryptionAlgorithm() ); long now = System.currentTimeMillis(); expirationTime = now + DEFAULT_EXP_TIME; } // Create using specified SecretKey /** * Create a cryptographic token using specified {@code SecretKey}. * * @param skey The specified {@code SecretKey} to use to encrypt the token. */ public CryptoToken(SecretKey skey) { if ( skey == null ) { throw new IllegalArgumentException("SecretKey may not be null."); } secretKey = skey; long now = System.currentTimeMillis(); expirationTime = now + DEFAULT_EXP_TIME; } /** * Create using previously encrypted token encrypted with default secret * key from ESAPI.properties. * @param token A previously encrypted token returned by one of the * {@code getToken()} or {@code updateToken()} methods. The * token must have been previously encrypted using the * using default secret key from the ESAPI.properties * property Encryptor.MasterKey. * @throws EncryptionException Thrown if they are any problems while decrypting * the token using the default secret key from * ESAPI.properties or if the decrypted * token is not properly formatted. */ public CryptoToken(String token) throws EncryptionException { secretKey = getDefaultSecretKey( ESAPI.securityConfiguration().getEncryptionAlgorithm() ); try { decryptToken(secretKey, token); } catch (EncodingException e) { throw new EncryptionException("Decryption of token failed. Token improperly encoded or encrypted with different key.", "Can't decrypt token because not correctly encoded or encrypted with different key.", e); } if ( username == null ) { throw new IllegalArgumentException("Programming error or malformed token: Decrypted token found username null."); } if ( expirationTime <= 0 ) { throw new IllegalArgumentException("Programming error or malformed token: Decrypted token found expirationTime <= 0."); } } /** * Create cryptographic token using previously encrypted token that was * encrypted with specified secret key. * * @param token A previously encrypted token returned by one of the * {@code getToken()} or {@code updateToken()} methods. * @throws EncryptionException Thrown if they are any problems while decrypting * the token using the default secret key from * ESAPI.properties or if the decrypted * token is not properly formatted. */ // token is a previously encrypted token (i.e., CryptoToken.getToken()) // with different SecretKey other than the one in ESAPI.properties public CryptoToken(SecretKey skey, String token) throws EncryptionException { if ( skey == null ) { throw new IllegalArgumentException("SecretKey may not be null."); } if ( token == null ) { throw new IllegalArgumentException("Token may not be null"); } secretKey = skey; try { decryptToken(secretKey, token); } catch (EncodingException e) { throw new EncryptionException("Decryption of token failed. Token improperly encoded.", "Can't decrypt token because not correctly encoded.", e); } if ( username == null ) { String exm = "Programming error???: Decrypted token found username null."; throw new EncryptionException(exm, exm); } if ( expirationTime <= 0 ) { String exm = "Programming error???: Decrypted token found expirationTime <= 0."; throw new EncryptionException(exm, exm); } } /** * Retrieve the user account name associated with this {@code CryptoToken} * object. * @return The user account name. The string represented by * {@link #ANONYMOUS_USER} is returned if * {@link #setUserAccountName(String)} was never called. */ public String getUserAccountName() { return ( (username != null) ? username : ANONYMOUS_USER ); } /** * Set the user account name associated with this cryptographic token * object. The user account name is converted to lower case. * @param userAccountName The user account name. * @throws ValidationException Thrown if user account name is not valid, i.e., * if it doesn't conform to the regular expression * given by "[a-z][a-z0-9_.@-]*". (Note that the * parameter {@code userAccountName} is first converted * to lower case before checked against the regular * expression.) */ public void setUserAccountName(String userAccountName) throws ValidationException { if ( userAccountName == null ) { throw new IllegalArgumentException("User account name may not be null."); } // Converting to lower case first allows a simpler regex. String userAcct = userAccountName.toLowerCase(); // Check to make sure that attribute name is valid as per our regex. Matcher userNameChecker = userNameRegex.matcher(userAcct); if ( userNameChecker.matches() ) { username = userAcct; } else { throw new ValidationException("Invalid user account name encountered.", "User account name " + userAccountName + " does not match regex " + USERNAME_REGEX + " after conversion to lowercase."); } } /** Check if token has expired yet. * @return True if token has expired; false otherwise. */ public boolean isExpired() { return System.currentTimeMillis() > expirationTime; } /** * Set expiration time to expire in 'interval' seconds (NOT milliseconds). * @param intervalSecs Number of seconds in the future from current date/time * to set expiration. Must be positive. */ public void setExpiration(int intervalSecs) throws IllegalArgumentException { int intervalMillis = intervalSecs * 1000; // Need to convert secs to millisec. // Don't want to use assertion here, because if they are disabled, // this would result in setting the expiration time prior to the // current time, hence it would already be expired. if ( intervalMillis <= 0) { throw new IllegalArgumentException("intervalSecs argument, converted to millisecs, must be > 0."); } // Check for arithmetic overflow here. In reality, this condition // should never happen, but we want to avoid it--even theoretically-- // since otherwise, it could have security implications. long now = System.currentTimeMillis(); preAdd(now, intervalMillis); expirationTime = now + intervalMillis; } /** * Set expiration time for a specific date/time. * @param expirationDate The date/time at which the token will fail. Must * be after the current date/time. * @throws IllegalArgumentException Thrown if the parameter is null. */ public void setExpiration(Date expirationDate) throws IllegalArgumentException { if ( expirationDate == null ) { throw new IllegalArgumentException("expirationDate may not be null."); } long curTime = System.currentTimeMillis(); long expTime = expirationDate.getTime(); if ( expTime <= curTime ) { throw new IllegalArgumentException("Expiration date must be after current date/time."); } expirationTime = expTime; } /** * Return the expiration time in milliseconds since epoch time (midnight, * January 1, 1970 UTC). * @return The current expiration time. */ public long getExpiration() { // Assertion here is safe as it is just a sanity check and this check is // make elsewhere. Plus this is a getter, not a setting, so if it's // messed up, it happened somewhere else. assert expirationTime > 0L : "Programming error: Expiration time <= 0"; return expirationTime; } /** * Return the expiration time as a {@code Date}. * @return The {@code Date} object representing the expiration time. */ public Date getExpirationDate() { return new Date( getExpiration() ); } /** * Set a name/value pair as an attribute. * @param name The attribute name * @param value The attribute value * @throws ValidationException Thrown if the attribute name is not properly * formed. That is, the attribute name does not * match the regular expression "[A-Za-z0-9_.-]+". */ public void setAttribute(String name, String value) throws ValidationException { if ( name == null || name.length() == 0 ) { throw new ValidationException("Null or empty attribute NAME encountered", "Attribute NAMES may not be null or empty string."); } if ( value == null ) { throw new ValidationException("Null attribute VALUE encountered for attr name " + name, "Attribute VALUE may not be null; attr name: " + name); } // NOTE: OTOH, it *is* VALID if the _value_ is empty! Null values cause too much trouble // to make it worth the effort of getting it to work consistently. // Check to make sure that attribute name is valid as per our regex. Matcher attrNameChecker = attrNameRegex.matcher(name); if ( attrNameChecker.matches() ) { attributes.put(name, value); } else { throw new ValidationException("Invalid attribute name encountered.", "Attribute name " + name + " does not match regex " + ATTR_NAME_REGEX); } } /** * Add the specified collection of attributes to the current attributes. * If there are duplicate attributes specified, they will replace any * existing ones. * * @param attrs Name/value pairs of attributes to add or replace the existing * attributes. Map must be non-null, but may be empty. * @throws ValidationException Thrown if one of the keys in the specified * parameter {@code attrs} is not a valid name. * That is, all attribute names must match the regular * expression ""[A-Za-z0-9_.-]+". * @see #setAttribute(String, String) */ public void addAttributes(final Map attrs) throws ValidationException { if ( attrs == null ) { throw new IllegalArgumentException("Attribute map may not be null."); } Set< Entry > keyValueSet = attrs.entrySet(); Iterator> it = keyValueSet.iterator(); while( it.hasNext() ) { Map.Entry entry = it.next(); String key = entry.getKey(); String value = entry.getValue(); setAttribute(key, value); } return; } /** * Retrieve the attribute with the specified name. * @param name The attribute name. * @return The value associated with the attribute name. If attribute is not * set, then {@code null} is returned. */ public String getAttribute(String name) { return attributes.get(name); } /** * Retrieve a {@code Map} that is a clone of all the attributes. A copy * is returned so that the attributes in {@code CrytpToken} are unaffected * by alterations made the returned {@code Map}. (Otherwise, multi-threaded code * could get trick. * * @return A {@code Map} of all the attributes. * @see #getAttribute(String) */ @SuppressWarnings("unchecked") public Map getAttributes() { // Unfortunately, this requires a cast, which requires us to supress warnings. return (Map) attributes.clone(); } /** * Removes all the attributes (if any) associated with this token. Note * that this does not clear / reset the user account name or expiration time. */ public void clearAttributes() { attributes.clear(); } /** * Return the new encrypted token as a base64-encoded string, encrypted with * the specified {@code SecretKey} which may be a different key than what the * token was originally encrypted with. E.g., *

     *   Alice:
     *      SecretKey aliceSecretKey = ...; // Shared with Bob
     *      CryptoToken cryptoToken = new CryptoToken(skey1);
     *      cryptoToken.setUserAccountName("kwwall");
     *      cryptoToken.setAttribute("role", "admin");
     *      cryptoToken.setAttribute("state", "Ohio");
     *      String token = cryptoToken.getToken(); // Encrypted with skey1
     *      // send token to Bob ...
     *  --------------------------------------------------------------------
     *  Bob:
     *      ...
     *      SecretKey aliceSecretKey = ...  // Shared with Alice
     *      SecretKey bobSecretKey = ...;   // Shared with Carol
     *      CryptoToken cryptoToken = new CryptoToken(aliceSecretKey, tokenFromAlice);
     *      
     *      // Re-encrypt for Carol using my (Bob's) key...
     *      String tokenForCarol = cryptoToken.getToken(bobSecretKey);
     *      // send tokenForCarol to Carol ...
     *      // use token ourselves
     *  --------------------------------------------------------------------
     *  Carol:
     *      ...
     *      SecretKey bobSecretKey = ...;   // Shared with Bob.
     *      CryptoToken cryptoToken = new CryptoToken(bobSecretKey, tokenFromBob);
     *      if ( ! cryptoToken.isExpired() ) {
     *          String userName = cryptoToken.getUserAccountName();
     *          String roleName = cryptoToken.getAttribute("role");
     *          if ( roleName != null && roleName.equalsIgnoreCase("admin") ) {
     *              // grant admin access...
     *              ...
     *          }
     *      }
     *      ...
     * 
* @param skey The specified key to (re)encrypt the token. * @return The newly encrypted token. */ public String getToken(SecretKey skey) throws EncryptionException { return createEncryptedToken(skey); } /** * Update the (current) expiration time by adding the specified number of * seconds to it and then re-encrypting with the current {@code SecretKey} * that was used to construct this object. * * @param additionalSecs The additional number of seconds to add to the * current expiration time. This number must be * >= 0 or otherwise an {@code IllegalArgumentException} * is thrown. * @return The re-encrypted token with the updated expiration time is returned. * @throws IllegalArgumentException Thrown if parameter {@code additionalSecs} * is less than 0. * @throws EncryptionException Thrown if the encryption fails. * @throws ValidationException Thrown if the token will have already expired * even after adding the specified number of * additional seconds. * @throws ArithmeticException If additional seconds is large enough such * that it would cause an arithmetic overflow * with a long (the current expiration time) * when added to the {@code additionalSecs} * parameter. */ public String updateToken(int additionalSecs) throws EncryptionException, ValidationException { if ( additionalSecs < 0) { throw new IllegalArgumentException("additionalSecs argument must be >= 0."); } // Avoid integer overflow. This could happen if one first calls // setExpiration(Date) with a date far into the future. We want // to avoid overflows as they might lead to security vulnerabilities. long curExpTime = getExpiration(); preAdd(curExpTime, additionalSecs * 1000); // Note: Can't use setExpiration(int) here was this needs a // 'long'. Could convert to Date first, and use // setExpiration(Date) but that hardly seems worth the trouble. expirationTime = curExpTime + (additionalSecs * 1000); if ( isExpired() ) { // Too bad there is no ProcrastinationException ;-) expirationTime = curExpTime; // Restore the original value (which still may // be expired. throw new ValidationException("Token timed out.", "Cryptographic token not increased to sufficient value to prevent timeout."); } // Don't change anything else (user acct name, attributes, skey, etc.) return getToken(); } /** * Return the new encrypted token as a base64-encoded string, encrypted with * the specified {@code SecretKey} with which this object was constructed. * * @return The newly encrypted token. * @see #getToken(SecretKey) */ public String getToken() throws EncryptionException { return createEncryptedToken(secretKey); } // Create the actual encrypted token based on the specified SecretKey. // This method will ensure that the decrypted token always ends with an // unquoted delimiter. private String createEncryptedToken(SecretKey skey) throws EncryptionException { StringBuilder sb = new StringBuilder( getUserAccountName() + DELIM); // CHECKME: Should we check here to see if token has already expired // and refuse to encrypt it (by throwing exception) if it has??? // If so, then updateToken() should also be revisited. sb.append( getExpiration() ).append( DELIM ); sb.append( getQuotedAttributes() ); Encryptor encryptor = ESAPI.encryptor(); CipherText ct = encryptor.encrypt(skey, new PlainText( sb.toString() ) ); String b64 = ESAPI.encoder().encodeForBase64(ct.asPortableSerializedByteArray(), false); return b64; } // Return a string of all the attributes, properly quoted. This is used in // creating the encrypted token. Note that this method ensures that the // quoted attribute string always ends with an (quoted) delimiter. private String getQuotedAttributes() { StringBuilder sb = new StringBuilder(); Set< Entry > keyValueSet = attributes.entrySet(); Iterator> it = keyValueSet.iterator(); while( it.hasNext() ) { Map.Entry entry = it.next(); String key = entry.getKey(); String value = entry.getValue(); // Because attribute values may be confidential, we don't want to log them! logger.debug(Logger.EVENT_UNSPECIFIED, " " + key + " -> "); sb.append(key + "=" + quoteAttributeValue( value ) + DELIM); } return sb.toString(); } // Do NOT define a toString() method as there may be sensitive // information contained in the attribute names. If we absolutely // need this, then only return the username and expiration time, and // _maybe_ the attribute names, but not there values. And obviously, // we NEVER want to include the SecretKey should we decide to do this. /* * public String toString() { return null; } */ // Quote any special characters in value. private static String quoteAttributeValue(String value) { if ( value == null ) { String exm = "Programming error???: Value should not be null."; // Empty string is OK. throw new EncryptionRuntimeException(exm, exm); } StringBuilder sb = new StringBuilder(); char[] charArray = value.toCharArray(); for( int i = 0; i < charArray.length; i++ ) { char c = charArray[i]; if ( c == QUOTE_CHAR || c == '=' || c == DELIM_CHAR ) { sb.append(QUOTE_CHAR).append(c); } else { sb.append(c); } } return sb.toString(); } // Parse the possibly quoted value and return the unquoted value. private static String parseQuotedValue(String quotedValue) { StringBuilder sb = new StringBuilder(); char[] charArray = quotedValue.toCharArray(); for( int i = 0; i < charArray.length; i++ ) { char c = charArray[i]; if ( c == QUOTE_CHAR ) { i++; // Skip past quote character. sb.append( charArray[i] ); } else { sb.append(c); } } return sb.toString(); } /* * Decrypt the encrypted token and parse it into the individual components. * The string should always end with a semicolon (;) even when there are * no attributes set. *

* Example of how quoted string might look: *

     *                            v              v  v            v     v
     *  kwwall;1291183520293;abc=x\=yx;xyz=;efg=a\;a\;;bbb=quotes\\tuff\;;
              |             |         |    |          |                  |
     *
     * 
*/ private void decryptToken(SecretKey skey, String b64token) throws EncryptionException, EncodingException { byte[] token = null; try { token = ESAPI.encoder().decodeFromBase64(b64token); } catch (IOException e) { // Ideally, we'd like to be able to include the actual (munged) token itself. // It's encrypted, but could be arbitrarily long, especially since it is not valid // encoding. In theory, it may help debugging as sometimes it may be a simple // case like someone failing to apply some other type of encoding // consistently (e.g., URL encoding), in which case logging this should // make this pretty obvious once a few of these are logged. // // OTOH, since tokens may be used as authentication tokens (see // "WARNING" in the class Javadoc) some insider could intentionally botch // a token (e.g., just remove the base64 padding characters) just to // get an otherwise valid token logged somewhere they can access it. // Therefore, I have decided NOT to include in in the logMessage // part of the exception. // // Note that prior to ESAPI 2.2.0.0, the token _was_ logged, but has // now been corrected. // throw new EncodingException("Invalid base64 encoding.", "Invalid base64 encoding for token [REDACTED]"); } CipherText ct = CipherText.fromPortableSerializedBytes(token); Encryptor encryptor = ESAPI.encryptor(); PlainText pt = encryptor.decrypt(skey, ct); String str = pt.toString(); if ( ! str.endsWith(DELIM) ) { String exm = "Programming error???: Expecting decrypted token to end with delim char, " + DELIM_CHAR; throw new EncryptionException(exm, exm); } char[] charArray = str.toCharArray(); int prevPos = -1; // Position of previous unquoted delimiter. int fieldNo = 0; ArrayList fields = new ArrayList(); int lastPos = charArray.length; for ( int curPos = 0; curPos < lastPos; curPos++ ) { boolean quoted = false; char curChar = charArray[curPos]; if ( curChar == QUOTE_CHAR ) { // Found a case where we have quoted character. We need to skip // over this and set the current character to the next character. curPos++; if ( curChar != lastPos ) { curChar = charArray[ curPos + 1 ]; quoted = true; } else { // Last position will always be a delimiter character that // should be treated as unquoted. curChar = DELIM_CHAR; } } if ( curChar == DELIM_CHAR && !quoted ) { // We found an actual (unquoted) field delimiter. String record = str.substring(prevPos + 1, curPos); fields.add( record ); fieldNo++; prevPos = curPos; } } Object[] objArray = fields.toArray(); if ( fieldNo != objArray.length ) { String exm = "Programming error???: Mismatch of delimited field count."; throw new EncryptionException(exm, exm); } logger.debug(Logger.EVENT_UNSPECIFIED, "Found " + objArray.length + " fields."); if ( objArray.length < 2 ) { String exm = "Missing mandatory fields from decrypted token (username &/or expiration time)."; throw new EncryptionException(exm, exm); } username = ((String)(objArray[0])).toLowerCase(); String expTime = (String)objArray[1]; expirationTime = Long.parseLong(expTime); for( int i = 2; i < objArray.length; i++ ) { String nvpair = (String)objArray[i]; int equalsAt = nvpair.indexOf("="); if ( equalsAt == -1 ) { throw new EncryptionException("Invalid attribute encountered in decrypted token.", "Malformed attribute name/value pair (" + nvpair + ") found in decrypted token."); } String name = nvpair.substring(0, equalsAt); String quotedValue = nvpair.substring(equalsAt + 1); String value = parseQuotedValue( quotedValue ); // Because attribute values may be confidential, we don't want to log them! logger.debug(Logger.EVENT_UNSPECIFIED, "Attribute[" + i + "]: name=" + name + ", value="); // Check to make sure that attribute name is valid as per our regex. Matcher attrNameChecker = attrNameRegex.matcher(name); if ( attrNameChecker.matches() ) { attributes.put(name, value); } else { throw new EncryptionException("Invalid attribute name encountered in decrypted token.", "Invalid attribute name encountered in decrypted token; " + "attribute name " + name + " does not match regex " + ATTR_NAME_REGEX); } attributes.put(name, value); } return; } private SecretKey getDefaultSecretKey(String encryptAlgorithm) { if ( encryptAlgorithm == null ) { throw new IllegalArgumentException("Encryption algorithm cannot be null"); } byte[] skey = ESAPI.securityConfiguration().getMasterKey(); if ( skey == null ) { throw new IllegalArgumentException("Can't obtain master key, Encryptor.MasterKey"); } if ( skey.length < 7 ) { throw new ConfigurationException( "Encryptor.MasterKey must be at least 7 bytes. " + "Length is: " + skey.length + " bytes."); } // Set up secretKeySpec for use for symmetric encryption and decryption, // and set up the public/private keys for asymmetric encryption / // decryption. return new SecretKeySpec(skey, encryptAlgorithm ); } // Check precondition to see if addition of two operands will result in // arithmetic overflow. Note that the operands are of two different // integral types. I.e., check to see if: // long result = leftLongValue + rightIntValue // would cause arithmetic overflow. // Note: We know that as we use it here, leftLongValue will always be > 0, // so arithmetic underflow should never be possible, but we check for // it anyhow. // Package level access to allow this to be used by other classes in this package. static final void preAdd(long leftLongValue, int rightIntValue) throws ArithmeticException { if ( rightIntValue > 0 && ( (leftLongValue + rightIntValue) < leftLongValue) ) { throw new ArithmeticException("Arithmetic overflow for addition."); } if ( rightIntValue < 0 && ( (leftLongValue + rightIntValue) > leftLongValue) ) { throw new ArithmeticException("Arithmetic underflow for addition."); } } }