/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java
Java | 293 lines | 149 code | 36 blank | 108 comment | 17 complexity | f632ff80a5faf024100beded7734bebe MD5 | raw file
Possible License(s): BSD-3-Clause, CC-BY-SA-3.0
- /**
- * 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
- * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
- *
- * Copyright (c) 2007 - 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.
- *
- * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
- * @created 2007
- */
- package org.owasp.esapi.reference.crypto;
- import java.io.BufferedReader;
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintStream;
- import java.io.PrintWriter;
- import java.io.Reader;
- import java.util.Collection;
- import java.util.Enumeration;
- import java.util.Properties;
- import java.util.Set;
- import org.owasp.esapi.ESAPI;
- import org.owasp.esapi.EncryptedProperties;
- import org.owasp.esapi.Logger;
- import org.owasp.esapi.crypto.CipherText;
- import org.owasp.esapi.crypto.PlainText;
- import org.owasp.esapi.errors.EncryptionRuntimeException;
- /**
- * Reference implementation of the {@code EncryptedProperties} interface. This
- * implementation wraps a normal properties file, and creates surrogates for the
- * {@code getProperty} and {@code setProperty} methods that perform encryption
- * and decryption based on {@code Encryptor}.
- * <p>
- * This implementation differs from {@code DefaultEncryptedProperties} in that
- * it actually extends from {@code java.util.Properties} for applications that need an
- * instance of that class. In order to do so, the {@code getProperty} and
- * {@code setProperty} methods were modified to throw {@code EncryptionRuntimeException}
- * instead of {@code EncryptionException}.
- *
- * @author August Detlefsen (augustd at codemagi dot com)
- * <a href="http://www.codemagi.com">CodeMagi, Inc.</a>
- * @author kevin.w.wall@gmail.com
- * @since October 8, 2010
- * @see org.owasp.esapi.EncryptedProperties
- * @see org.owasp.esapi.reference.crypto.DefaultEncryptedProperties
- */
- public class ReferenceEncryptedProperties extends java.util.Properties implements EncryptedProperties {
- /**
- * serverVersionUID; use format of YYYYMMDD.
- */
- private static final long serialVersionUID = 20120718L;
- /** The logger. */
- private final Logger logger = ESAPI.getLogger(this.getClass());
- private static final String[] GET_ERROR_MESSAGES = new String[]{
- ": failed decoding from base64",
- ": failed to deserialize properly",
- ": failed to decrypt properly"
- };
- private static final String[] SET_ERROR_MESSAGES = new String[]{
- ": failed to encrypt properly",
- ": failed to serialize correctly",
- ": failed to base64-encode properly",
- ": failed to set base64-encoded value as property. Illegal key name?"
- };
- /**
- * Instantiates a new encrypted properties.
- */
- public ReferenceEncryptedProperties() {
- super();
- }
- public ReferenceEncryptedProperties(Properties defaults) {
- super();
- for (Object oKey : defaults.keySet()) {
- String key = (oKey instanceof String) ? (String)oKey : oKey.toString();
- String value = defaults.getProperty(key);
- this.setProperty(key, value);
- }
- }
- /**
- * {@inheritDoc}
- *
- * @throws EncryptionRuntimeException Thrown if decryption fails.
- */
- @Override
- public synchronized String getProperty(String key) throws EncryptionRuntimeException {
- int progressMark = 0;
- try {
- String encryptedValue = super.getProperty(key);
- if(encryptedValue==null)
- return null;
- progressMark = 0;
- byte[] serializedCiphertext = ESAPI.encoder().decodeFromBase64(encryptedValue);
- progressMark++;
- CipherText restoredCipherText = CipherText.fromPortableSerializedBytes(serializedCiphertext);
- progressMark++;
- PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText);
- return plaintext.toString();
- } catch (Exception e) {
- throw new EncryptionRuntimeException("Property retrieval failure",
- "Couldn't retrieve encrypted property for property " + key +
- GET_ERROR_MESSAGES[progressMark], e);
- }
- }
- /**
- * {@inheritDoc}
- *
- * @throws EncryptionRuntimeException Thrown if decryption fails.
- */
- @Override
- public String getProperty(String key, String defaultValue) throws EncryptionRuntimeException {
- String value = getProperty(key);
- if (value == null) return defaultValue;
- return value;
- }
- /**
- * {@inheritDoc}
- *
- * @throws EncryptionRuntimeException Thrown if encryption fails.
- */
- @Override
- public synchronized String setProperty(String key, String value) throws EncryptionRuntimeException {
- int progressMark = 0;
- try {
- if ( key == null ) {
- throw new NullPointerException("Property name may not be null.");
- }
- if ( value == null ) {
- throw new NullPointerException("Property value may not be null.");
- }
- // NOTE: Not backward compatible w/ ESAPI 1.4.
- PlainText pt = new PlainText(value);
- CipherText ct = ESAPI.encryptor().encrypt(pt);
- progressMark++;
- byte[] serializedCiphertext = ct.asPortableSerializedByteArray();
- progressMark++;
- String b64str = ESAPI.encoder().encodeForBase64(serializedCiphertext, false);
- progressMark++;
- return (String)super.put(key, b64str);
- } catch (Exception e) {
- throw new EncryptionRuntimeException("Property setting failure",
- "Couldn't set encrypted property " + key +
- SET_ERROR_MESSAGES[progressMark], e);
- }
- }
- /**
- * {@inheritDoc}
- * @throws IOException Thrown if input stream invalid or does not
- * correspond to Java properties file format.
- */
- @Override
- public void load(InputStream in) throws IOException {
- super.load(in);
- logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
- }
- /**
- * {@inheritDoc}
- *
- * For JDK 1.5 compatibility, this method has been overridden convert the Reader
- * into an InputStream and call the superclass constructor.
- *
- * @throws IOException Thrown if {@code Reader} input stream invalid or does not
- * correspond to Java properties file format.
- */
- public void load(Reader in) throws IOException {
- if (in == null) return;
- //read from the reader into a StringBuffer
- char[] cbuf = new char[65536];
- BufferedReader buff = new BufferedReader(in);
- StringBuilder contents = new StringBuilder();
- int read_this_time = 0;
- while (read_this_time != -1) {
- read_this_time = buff.read(cbuf, 0, 65536);
- if (read_this_time > 0) contents.append(cbuf, 0, read_this_time);
- }
- //create a new InputStream from the StringBuffer
- InputStream is = new ByteArrayInputStream(contents.toString().getBytes());
- super.load(is);
- logger.trace(Logger.SECURITY_SUCCESS, "Encrypted properties loaded successfully");
- }
- /**
- * This method has been overridden to throw an {@code UnsupportedOperationException}
- */
- @Override
- public void list(PrintStream out) {
- throw new UnsupportedOperationException("This method has been removed for security.");
- }
- /**
- * This method has been overridden to throw an {@code UnsupportedOperationException}
- */
- @Override
- public void list(PrintWriter out) {
- throw new UnsupportedOperationException("This method has been removed for security.");
- }
- /**
- * This method has been overridden to throw an {@code UnsupportedOperationException}
- */
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @Override
- public Collection values() {
- throw new UnsupportedOperationException("This method has been removed for security.");
- }
- /**
- * This method has been overridden to throw an {@code UnsupportedOperationException}
- */
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @Override
- public Set entrySet() {
- throw new UnsupportedOperationException("This method has been removed for security.");
- }
- /**
- * This method has been overridden to throw an {@code UnsupportedOperationException}
- */
- @SuppressWarnings({ "unchecked", "rawtypes" })
- @Override
- public Enumeration elements() {
- throw new UnsupportedOperationException("This method has been removed for security.");
- }
- /**
- * This method has been overridden to only accept Strings for key and value, and to encrypt
- * those Strings before storing them. Outside classes should always use {@code setProperty}
- * to add values to the Properties map. If an outside class does erroneously call this method
- * with non-String parameters an {@code IllegalArgumentException} will be thrown.
- *
- * @param key A String key to add
- * @param value A String value to add
- * @return The old value associated with the specified key, or {@code null}
- * if the key did not exist.
- */
- @Override
- public synchronized Object put(Object key, Object value) {
- //if java.util.Properties is calling this method, just forward to the implementation in
- //the superclass (java.util.Hashtable)
- Throwable t = new Throwable();
- for (StackTraceElement trace : t.getStackTrace()) {
- if ("java.util.Properties".equals(trace.getClassName()) ) return super.put(key, value);
- }
- //otherwise, if both arguments are Strings, encrypt and store them
- if (key instanceof String && value instanceof String) return setProperty((String)key, (String)value);
- //other Object types are not allowed
- throw new IllegalArgumentException("This method has been overridden to only accept Strings for key and value.");
- }
- /**
- * This method has been overridden to not print out the keys and values stored in this properties file.
- *
- * @return The minimal String representation of this class, as per java.lang.Object.
- */
- @Override
- public String toString() {
- return getClass().getName() + "@" + Integer.toHexString(hashCode());
- }
- }