AES256 암복호화 - JAVA & C#
C# Language 에서 AES256으로 encrypt 되어 있는 데이터를 Java Language 에서 복호화해서 처리해야 하는 경우가 생겼다.
서로 다른 Language 에서 암호화 키, IV initialvector, CipherMMode, PaddingMode 등 하나라도 어긋날 경우 암호화 값은 달라지게 된다.
AES 암호화 복호화에 필요한 네가지는 아래와 같다.
1. Secret Key
2. IV (Initialize Vector)
3. Chpher Mode
4. Padding Mode
보통 Base64 기반이니까, URL로 전달할거면 encodeURIComponent 로 인코딩할건지 Base64 URL Safe 핸들링(base64 문자열의 + 와 / 를 - 와 _ 로 치환) 할건지 정해야 한다.
JAVA 소스
package com.skhynix;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.apache.tomcat.util.codec.binary.Base64;
public class AES256Chiper {
private static volatile AES256Chiper Instance;
public static byte[] IV = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
public static AES256Chiper getInstance() {
if (Instance == null) {
synchronized (AES256Chiper.class) {
if (Instance == null) Instance = new AES256Chiper();
}
}
return Instance;
}
private AES256Chiper() {
}
//Encrypt plainText
public static String encrypt(String encKey, String plainText) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] keyData = encKey.getBytes("UTF-8");
SecretKey secretKey = new SecretKeySpec(keyData, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(IV));
byte[] encData = cipher.doFinal(plainText.getBytes("UTF-8"));
return ConvertStringToHex(new String(Base64.encodeBase64(encData)));
}
//Decrypt encText
public static String decrypt(String encKey, String encText) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] keyData = encKey.getBytes("UTF-8");
SecretKey secretKey = new SecretKeySpec(keyData, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(IV));
//byte[] decData = Base64.decodeBase64(encText.getBytes());
byte[] decData = Base64.decodeBase64(ConvertHexToString(encText).getBytes());
return new String(cipher.doFinal(decData), "UTF-8");
}
public static String ConvertStringToHex(String str) {
// display in lowercase, default
char[] chars = Hex.encodeHex(str.getBytes(StandardCharsets.UTF_8));
return String.valueOf(chars);
}
// Hex -> Decimal -> Char
public static String ConvertHexToString(String hex) {
StringBuilder result = new StringBuilder();
// split into two chars per loop, hex, 0A, 0B, 0C...
for (int i = 0; i < hex.length() - 1; i += 2) {
String tempInHex = hex.substring(i, (i + 2));
//convert hex to decimal
int decimal = Integer.parseInt(tempInHex, 16);
// convert the decimal to char
result.append((char) decimal);
}
return result.toString();
}
}
C# 소스
/// <summary>
/// AES256 암호화 Class
/// </summary>
public class AES256Chiper
{
public static string Encrypt(string plainText, string encKey)
{
string output = string.Empty;
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.UTF8.GetBytes(encKey);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
byte[] xXml = Encoding.UTF8.GetBytes(plainText);
cs.Write(xXml, 0, xXml.Length);
}
xBuff = ms.ToArray();
}
output = ConvertStringToHex(Convert.ToBase64String(xBuff));
xBuff = null;
}
catch (Exception ex)
{
Debug.WriteLine("Exception:{0}", ex.Message.ToString());
}
return output;
}
/// <summary>
/// AES256 복호화
/// </summary>
/// <returns></returns>
public static string Decrypt(string encText, string encKey)
{
string output = string.Empty;
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Encoding.UTF8.GetBytes(encKey);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var decrypt = aes.CreateDecryptor();
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
{
byte[] xXml = Convert.FromBase64String(ConvertHexToString(encText));
cs.Write(xXml, 0, xXml.Length);
}
xBuff = ms.ToArray();
}
output = Encoding.UTF8.GetString(xBuff);
xBuff = null;
}
catch (Exception ex)
{
Debug.WriteLine("Exception:{0}", ex.Message.ToString());
}
return output;
}
#region HexString
public static string ConvertStringToHex(string asciiString)
{
string hex = "";
foreach (char c in asciiString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
return hex;
}
public static string ConvertHexToString(string HexValue)
{
string StrValue = "";
while (HexValue.Length > 0)
{
StrValue += System.Convert.ToChar(System.Convert.ToUInt32(HexValue.Substring(0, 2), 16)).ToString();
HexValue = HexValue.Substring(2, HexValue.Length - 2);
}
return StrValue;
}
#endregion
}