C#에서 생성한 AES256 암호화 값을 java에서 복호화하여 처리해야 하는데, 아래 3가지 내용중 하나라도 다를 경우 복호화 정상으로 되지 않는다. 이참에 서로 다른 language간 AES256 암/복호화 방법을 정리해 둔다.
주요 점검 포인트
1.암호화 키 (나의 경우엔 32byte 키를 사용한다.)
2.IV initialvector (16byte를 사용함)
3.CipherMode (CBC, ECB, OFB, CFB, CTS 등이 있으나 CBC로 함)
4.PaddingMode (C#의 경우엔 PKCS7, JAVA는 PKCS5)
암/복호화 과정은 아래와 같은 순서대로 진행되는데, 아래 블로그에 잘 정리되어 있다.
- 암호화
plain text > plain bytes > encrypt > encrypted bytes > encrypted base64 text - 복호화
encrpyted base64 text > encrypted bytes > decrypt > plain bytes > plain text
https://velog.io/@bang9dev/AES-256-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC
한가지 추가하자면 http로 전송하므로 string -> hex string으로 변환하여 전송 한다.
Java 코드
package com.codepulse.AES256Chiper.Common;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
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# 코드
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace VisitlogAdminApp.Common
{
/// <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
}
}
java에서 암호화 한 데이터를 C#에서 복호화 하는데 성공했다. 다시 서비스를 만들기 시작..