001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.crypto;
018    
019    import java.util.Collections;
020    import java.util.HashMap;
021    import java.util.Map;
022    import java.io.Serializable;
023    
024    /**
025     * A static class that uses registered Encryption instances to encypt and decrypt objects, typically strings.
026     * The encrypted strings are preceded by the name of the Encryption object, such as {Simple}, followed by the base64
027     * encoded encrypted bytes.
028     *
029     * Any number of Encryption instances can be registered but only the first to be explicitly registered will be used.
030     * However, when decrypting the Encryption instance is looked up from the name prefix so may be any registered Encryption instance.
031     * Furthermore, encrypt and decrypt are idempotent.  Calling encrypt on a string encrypted with a registered Encryption that is not
032     * the one in use will decrypt the string and re-encrypt it with the one in use.  This can be useful when changing Encryption.
033     *
034     * The default Encryption instance (that does not need to be registered) is SimpleEncryption which uses a fixed key hardcoded into
035     * the Encryption class itself.  Thus it is useful only to hide information from those who don't read code.  On the other hand
036     * you can't lose the key and make your server permanently unusable.
037     *
038     * n.b. calling these methods idempotent is a slight exageration as this would apply only if all arguments and return values are Strings.
039     *
040     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
041     */
042    public class EncryptionManager {
043    
044        private static final Map<String, Encryption> ENCRYPTORS = Collections.synchronizedMap(new HashMap<String, Encryption>());
045        private final static String SIMPLE_ENCRYPTION_PREFIX = "{Simple}";
046    
047        static {
048            ENCRYPTORS.put(SIMPLE_ENCRYPTION_PREFIX, SimpleEncryption.INSTANCE);
049            //login properties files used to have this
050            ENCRYPTORS.put("{Standard}", SimpleEncryption.INSTANCE);
051        }
052    
053        private static String encryptionPrefix = SIMPLE_ENCRYPTION_PREFIX;
054    
055        /**
056         * Encryption instances should call this to register themselves.
057         *
058         * @param prefix id in form {name} for the Encryption instance
059         * @param encryption Encryption instance to do the work.
060         */
061        public synchronized static void setEncryptionPrefix(String prefix, Encryption encryption) {
062            if (SIMPLE_ENCRYPTION_PREFIX.equals(encryptionPrefix)) {
063                encryptionPrefix = prefix;
064            }
065            ENCRYPTORS.put(prefix, encryption);
066        }
067    
068        /**
069         * Idempotent method that outputs string starting with the active registered encryption prefix followed by
070         * the output of the registered Encryption instance.  If called with a string encrypted by another
071         * registered Encryption it will re-encrypt with the active Encryption instance.
072         * @param source Serializable object to encrypt, usually a password string or an already encrypted string.
073         * @return the name of the registered Encryption followed by its output.
074         */
075        public static String encrypt(Serializable source) {
076            if (source instanceof String) {
077                String sourceString = (String) source;
078                if (sourceString.startsWith(encryptionPrefix)) {
079                    return (String) source;
080                } else if (sourceString.startsWith("{")) {
081                    source = decrypt(sourceString);
082                }
083            }
084            Encryption encryption = ENCRYPTORS.get(encryptionPrefix);
085            return encryptionPrefix + encryption.encrypt(source);
086        }
087    
088        /**
089         * Idempotent method that given a String starting with a registered Encryption name will remove the
090         * name prefix and return the result of applying the Encryption to the suffix.  If no registered Encryption
091         * name matches the start of the string the input will be returned.
092         * @param source String that is possibly the output of calling encrypt, consisting of a Encryption name followed by its encrypt output.
093         * @return the result of applying the Encryption.decrypt method to the input suffix after identifying the Encryption from the prefix, or the
094         * input if no Encryption name matches.
095         */
096        public static Serializable decrypt(String source) {
097            String prefix = null;
098            Encryption encryption = null;
099            synchronized (ENCRYPTORS) {
100                for (Map.Entry<String, Encryption> entry : ENCRYPTORS.entrySet()) {
101                    prefix = entry.getKey();
102                    if (source.startsWith(prefix)) {
103                        encryption = entry.getValue();
104                        break;
105                    }
106                }
107            }
108            if (encryption != null) {
109                return encryption.decrypt(source.substring(prefix.length()));
110            }
111            return source;
112        }
113    }