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.core.util.crypt;
018
019import org.apache.wicket.core.random.ISecureRandomSupplier;
020import org.apache.wicket.util.crypt.ICrypt;
021import org.apache.wicket.util.lang.Args;
022
023import java.security.InvalidAlgorithmParameterException;
024import java.security.InvalidKeyException;
025import java.security.NoSuchAlgorithmException;
026
027import javax.crypto.BadPaddingException;
028import javax.crypto.Cipher;
029import javax.crypto.IllegalBlockSizeException;
030import javax.crypto.NoSuchPaddingException;
031import javax.crypto.SecretKey;
032import javax.crypto.spec.IvParameterSpec;
033
034/**
035 * AES based {@link ICrypt} encrypt and decrypt strings such as passwords or URL segments.
036 * Based on http://stackoverflow.com/a/992413
037 * 
038 * @see ICrypt
039 */
040public class AESCrypt extends AbstractJceCrypt
041{
042
043        private final SecretKey secretKey;
044        private final String algorithm;
045        private final ISecureRandomSupplier randomSupplier;
046
047        
048        /**
049         * Constructor
050         * 
051         * @param secretKey
052         *              The {@link SecretKey} to use to initialize the {@link Cipher}.
053         * @param randomSupplier
054         *              The {@link ISecureRandomSupplier} to use to generate random values.
055         */
056        public AESCrypt(SecretKey secretKey, ISecureRandomSupplier randomSupplier)
057        {
058                this(secretKey, "AES/CBC/PKCS5Padding", randomSupplier);
059        }
060
061        /**
062         * Constructor
063         * 
064         * @param secretKey
065         *              The {@link SecretKey} to use to initialize the {@link Cipher}.
066         * @param algorithm
067         *              The cipher algorithm to use, for example "AES/CBC/PKCS5Padding".
068         * @param randomSupplier
069         *              The {@link ISecureRandomSupplier} to use to generate random values.
070         */
071        public AESCrypt(SecretKey secretKey, String algorithm, 
072                ISecureRandomSupplier randomSupplier)
073        {
074                Args.notNull(secretKey, "secretKey");
075                Args.notNull(algorithm, "algorithm");
076                Args.notNull(randomSupplier, "randomSupplier");
077                
078                this.secretKey = secretKey;
079                this.algorithm = algorithm;
080                this.randomSupplier = randomSupplier;
081        }
082
083        @Override
084        protected byte[] decrypt(byte[] encrypted)
085        {
086                try
087                {
088                        Cipher cipher = Cipher.getInstance(algorithm);
089                        
090                        int ivSize = cipher.getBlockSize();
091                        byte[] iv = new byte[ivSize];
092                        byte[] ciphertext = new byte[encrypted.length - ivSize];
093                        
094                        System.arraycopy(encrypted, 0, iv, 0, ivSize);
095                        System.arraycopy(encrypted, ivSize, ciphertext, 0, ciphertext.length);
096
097                        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
098                        return cipher.doFinal(ciphertext);
099                }
100                catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException 
101                        | InvalidKeyException | InvalidAlgorithmParameterException e)
102                {
103                        throw new RuntimeException(e);
104                }
105        }
106
107        @Override
108        protected byte[] encrypt(byte[] plainBytes)
109        {
110                try
111                {
112                        Cipher cipher = Cipher.getInstance(algorithm);
113                        int ivSize = cipher.getBlockSize();
114                        byte[] iv = randomSupplier.getRandomBytes(ivSize);
115                        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
116
117                        byte[] ciphertext = cipher.doFinal(plainBytes);
118                        byte[] finalRes = new byte[ciphertext.length + ivSize];
119                        
120                        System.arraycopy(iv, 0, finalRes, 0, ivSize);
121                        System.arraycopy(ciphertext, 0, finalRes, ivSize, ciphertext.length);
122                        
123                        return finalRes;
124                }
125                catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException 
126                        | InvalidKeyException | InvalidAlgorithmParameterException e)
127                {
128                        throw new RuntimeException(e);
129                }
130
131        }
132}