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 */
017package org.apache.wicket.util.crypt;
018
019import java.io.UnsupportedEncodingException;
020import java.security.GeneralSecurityException;
021import java.util.Base64;
022import java.util.UUID;
023
024import javax.crypto.Cipher;
025
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029
030/**
031 * Abstract base class for JCE based ICrypt implementations.
032 * 
033 * @author Juergen Donnerstag
034 */
035public abstract class AbstractCrypt implements ICrypt
036{
037        /** Encoding used to convert java String from and to byte[] */
038        private static final String CHARACTER_ENCODING = "UTF-8";
039
040        /** Log. */
041        private static final Logger log = LoggerFactory.getLogger(AbstractCrypt.class);
042
043        /** Key used to de-/encrypt the data */
044        private String encryptionKey;
045
046        /**
047         * Constructor
048         */
049        public AbstractCrypt()
050        {
051                this.encryptionKey = UUID.randomUUID().toString();
052        }
053
054        /**
055         * Decrypts a string into a string.
056         * 
057         * @param text
058         *            text to decrypt
059         * @return the decrypted text
060         */
061        @Override
062        public final String decryptUrlSafe(final String text)
063        {
064                try
065                {
066                        byte[] decoded = java.util.Base64.getUrlDecoder().decode(text);
067                        return new String(decryptByteArray(decoded), CHARACTER_ENCODING);
068                }
069                catch (Exception ex)
070                {
071                        log.debug("Error decoding text: " + text, ex);
072                        return null;
073                }
074        }
075
076        /**
077         * Encrypt a string into a string using URL safe Base64 encoding.
078         * 
079         * @param plainText
080         *            text to encrypt
081         * @return encrypted string
082         */
083        @Override
084        public final String encryptUrlSafe(final String plainText)
085        {
086                try
087                {
088                        byte[] encrypted = encryptStringToByteArray(plainText);
089                        Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
090                        byte[] encoded = encoder.encode(encrypted);
091                        return new String(encoded, CHARACTER_ENCODING);
092                }
093                catch (GeneralSecurityException e)
094                {
095                        log.error("Unable to encrypt text '" + plainText + "'", e);
096                        return null;
097                }
098                catch (UnsupportedEncodingException e)
099                {
100                        log.error("Unable to encrypt text '" + plainText + "'", e);
101                        return null;
102                }
103        }
104
105        /**
106         * Get encryption private key
107         * 
108         * @return encryption private key
109         */
110        public String getKey()
111        {
112                return encryptionKey;
113        }
114
115        /**
116         * Set encryption private key
117         * 
118         * @param key
119         *            private key to make de-/encryption unique
120         */
121        @Override
122        public void setKey(final String key)
123        {
124                encryptionKey = key;
125        }
126
127        /**
128         * Crypts the given byte array
129         * 
130         * @param input
131         *            byte array to be crypted
132         * @param mode
133         *            crypt mode
134         * @return the input crypted. Null in case of an error
135         * @throws GeneralSecurityException
136         */
137        protected abstract byte[] crypt(final byte[] input, final int mode)
138                throws GeneralSecurityException;
139
140        /**
141         * Decrypts an encrypted, but Base64 decoded byte array into a byte array.
142         * 
143         * @param encrypted
144         *            byte array to decrypt
145         * @return the decrypted text
146         */
147        private byte[] decryptByteArray(final byte[] encrypted)
148        {
149                try
150                {
151                        return crypt(encrypted, Cipher.DECRYPT_MODE);
152                }
153                catch (GeneralSecurityException e)
154                {
155                        throw new RuntimeException(
156                                "Unable to decrypt the text '" + new String(encrypted) + "'", e);
157                }
158        }
159
160        /**
161         * Encrypts the given text into a byte array.
162         * 
163         * @param plainText
164         *            text to encrypt
165         * @return the string encrypted
166         * @throws GeneralSecurityException
167         */
168        private byte[] encryptStringToByteArray(final String plainText)
169                throws GeneralSecurityException
170        {
171                try
172                {
173                        return crypt(plainText.getBytes(CHARACTER_ENCODING), Cipher.ENCRYPT_MODE);
174                }
175                catch (UnsupportedEncodingException ex)
176                {
177                        throw new RuntimeException(ex.getMessage());
178                }
179        }
180}