/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.shardingsphere.core.encrypt.impl;

import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shardingsphere.core.exception.ShardingConfigurationException;
import org.apache.shardingsphere.spi.algorithm.encrypt.ShardingEncryptor;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Arrays;
import java.util.Properties;

/**
 * AES sharding encryptor.
 *
 * @author panjuan
 */
@Getter
@Setter
public final class AESShardingEncryptor implements ShardingEncryptor {
    
    private static final String AES_KEY = "aes.key.value";
    
    private Properties properties = new Properties();
    
    @Override
    public String getType() {
        return "AES";
    }
    
    @Override
    @SneakyThrows
    public Object encrypt(final Object plaintext) {
        if (!hasSecretKey()) {
            throw new ShardingConfigurationException("No available secret key for `%s`.", AESShardingEncryptor.class.getName());
        }
        Cipher cipher = Cipher.getInstance(getType().toUpperCase());
        cipher.init(Cipher.ENCRYPT_MODE, generateSecretKey());
        byte[] result = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF8"));
        return Base64.encodeBase64String(result);
    }
    
    @Override
    @SneakyThrows
    public Object decrypt(final Object ciphertext) {
        if (!hasSecretKey()) {
            throw new ShardingConfigurationException("No available secret key for `%s`.", AESShardingEncryptor.class.getName());
        }
        Cipher cipher = Cipher.getInstance(getType().toUpperCase());
        cipher.init(Cipher.DECRYPT_MODE, generateSecretKey());
        byte[] result = Base64.decodeBase64(String.valueOf(ciphertext));
        return new String(cipher.doFinal(result));
    }
    
    private boolean hasSecretKey() {
        return null != properties.get(AES_KEY);
    }
    
    @SneakyThrows
    private Key generateSecretKey() {
        byte[] keyValue = String.valueOf(properties.get(AES_KEY)).getBytes("UTF8");
        return new SecretKeySpec(Arrays.copyOf(DigestUtils.sha1(keyValue), 16), getType());
    }
}

@@ -850,16 +850,61 @@
 .impl;%0A%0A
+import com.google.common.base.Preconditions;%0A
 import l
@@ -1050,21 +1050,21 @@
 dec.
-digest.Digest
+binary.String
 Util
@@ -1088,68 +1088,40 @@
 che.
-shardingsphere.core.exception.ShardingConfigurationException
+commons.codec.digest.DigestUtils
 ;%0Aim
@@ -1245,52 +1245,159 @@
 pto.
-spec.SecretKeySpec;%0Aimport java.security.Key
+NoSuchPaddingException;%0Aimport javax.crypto.spec.SecretKeySpec;%0Aimport java.security.InvalidKeyException;%0Aimport java.security.NoSuchAlgorithmException
 ;%0Aim
@@ -1894,340 +1894,87 @@
     
-if (!hasSecretKey()) %7B%0A            throw new ShardingConfigurationException(%22No available secret key for %60%25s%60.%22, AESShardingEncryptor.class.getName());%0A        %7D%0A        Cipher cipher = Cipher.getInstance(getType().toUpperCase());%0A        cipher.init(Cipher.ENCRYPT_MODE, generateSecretKey());%0A        byte%5B%5D result = cipher.doFinal
+byte%5B%5D result = getCipher(Cipher.ENCRYPT_MODE).doFinal(StringUtils.getBytesUtf8
 (Str
@@ -1995,32 +1995,16 @@
 aintext)
-.getBytes(%22UTF8%22
 ));%0A    
@@ -2157,84 +2157,359 @@
     
-if (!hasSecretKey()) %7B
+byte%5B%5D result = getCipher(Cipher.DECRYPT_MODE).doFinal(Base64.decodeBase64(String.valueOf(ciphertext)));%0A        return new String(result);
 %0A    
+%7D%0A
     
+%0A
     
-throw new ShardingConfigurationException(
+private Cipher getCipher(final int decryptMode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException %7B%0A        Preconditions.checkArgument(properties.containsKey(AES_KEY), 
 %22No 
@@ -2584,18 +2584,8 @@
 ));%0A
-        %7D%0A
     
@@ -2595,22 +2595,22 @@
  Cipher 
-cipher
+result
  = Ciphe
@@ -2636,548 +2636,249 @@
 pe()
-.toUpperCase());%0A        cipher.init(Cipher.DECRYPT_MODE, generateSecretKey());%0A        byte%5B%5D result = Base64.decodeBase64(String.valueOf(ciphertext));%0A        return new String(cipher.doFinal(result));%0A    %7D%0A    %0A    private boolean hasSecretKey() %7B%0A        return null != properties.get(AES_KEY);%0A    %7D%0A    %0A    @SneakyThrows%0A    private Key generateSecretKey() %7B%0A        byte%5B%5D keyValue = String.valueOf(properties.get(AES_KEY)).getBytes(%22UTF8%22);%0A        return new SecretKeySpec(Arrays.copyOf(DigestUtils.sha1(keyValue), 16), getType()
+);%0A        result.init(decryptMode, new SecretKeySpec(createSecretKey(), getType()));%0A        return result;%0A    %7D%0A    %0A    private byte%5B%5D createSecretKey() %7B%0A        return Arrays.copyOf(DigestUtils.sha1(properties.getProperty(AES_KEY)), 16
 );%0A 

refactor AESShardingEncryptor