AES 암호화

2023-05-01
ꞏ
유영진
ꞏ
show5116

AES

암호화를 하는 방식에는 단방향 암호화, 비밀키 암호화, 공개키 암호화가 있는데, 그중 AES는 비밀키 암호화 입니다.
많은 곳에서 AES를 사용하고 있습니다.

Padding

패딩(Padding)이란 데이터를 블럭으로 암호화할 때 평문이 항상 블럭크기의 갯수가 될 수 없습니다.
따라서 평문의 마지막 블록을 암호화 되기 전에 어떠한 데이터로 남은 공간을 채워주는 방식입니다.
AES에서는 PKCS 패딩을 사용하는데, 이 방식은 예를들어 16바이트의 평문 중 3바이트가 비어있다면 3바이트를 모두 05로 채우는 방식입니다.
PKCS#5와 PKCS#7 두가지가 있는데, 구현 되는 방식은 둘다 같고 다만 PKCS#5는 8바이트 고정, PKCS#7은 바이트가 고정되어있지 않습니다.

Mode

모드(Mode)는 블럭 암호화 순서 및 규칙에 대한 표준입니다.
모드에 따라서 IV(초기화 벡터)를 사용할 수도 있고, 사용하지 않을수도 있습니다.
일반적으론 CBC(Cipher-Block Chaining)모드가 가장 많이 사용됩니다.
이 모드는 최초 평문 1블럭과 IV를 XOR 연산한 다음 암호화하고, 다음 평문 1블럭은 앞에서 암호화된 결과 블럭과 XOR하여서 다시 암호화 하는 방식입니다.

PBKDF2

PBKDF는 Password-Based Key Derivation Function입니다.
만약에 키 값을 탈취당했을 때를 고려하여서 키 값을 늘려버리고 암호화의 횟수를 여러번으로 늘려서 움호화의 수행 시간을 오래 걸리게 해줍니다.
이 과정을 키 확장이라 하고, 외부 공격으로 부터 비교적 안전하게 해줍니다.
salt와 반복횟수를 지정해주어서 새로운 결과값을 만들어냅니다.
salt란 해시함수를 돌리기 전에 임의의 문자열ㅇㄹ 덧붙이는 것을 의미합니다.

cryptoJS

cryptoJS는 javascript의 표준 및 보안 암호화 알고리즘 구현 라이브러리입니다.
일반적인 사용법은 CryptoJS.AES.encrypt('변화할 데이터','비밀키', { mode: '모드', padding: '패딩', iv: '초기화벡터' })의 구조로 사용됩니다.

js
1const key = {
2 iv: '...', // 32바이트(크기)의 문자열 초기화 벡터
3 sa: '...', // 32바이트(크기)의 문자열 salt
4 pa: '...', // 32바이트(크기)의 문자열 비밀키
5};
6
7const encrypt = (value: string, use: boolean = true) => {
8 if(!use) return value;
9 return ((CryptoJS) =>{
10 return CryptoJS.AES.encrypt(
11 value,
12 // 비밀키에 PBKDF2를 적용하여 비밀키를 암호화합니다.
13 // CryptoJS.PBKDF2('암호키', 'salt', { keySize: 키사이즈, iterations: 반복횟수 })
14 CryptoJS.PBKDF2(key.pa, CryptoJS.enc.Hex.parse(key.sa), { keySize: 4, iterations: 1000 }),
15 // mode는 default로 'CBC', padding은 default로 'Pkcs7'이 됩니다.
16 {
17 iv: CryptoJS.enc.Hex.parse(key.iv)
18 }
19 ).toString();
20 })(window['CryptoJS']);
21};

in java

java에서는 위의 암호화 결과를 decrypt(해독)하고 있습니다.

java
1public static final String ENC_KEY = "..."; // cryptojs의 pa와 동일한 비밀키
2public static final String salt = "..."; // cryptojs의 sa와 동일한 salt
3public static final String iv = "...";// cryptojs의 iv와 동일한 초기화 벡터
4
5private static String decrypt(String salt, String iv, String passphrase, String ciphertext, int iterationCount, int keySize) throws Exception {
6 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
7 // 반복횟수와 키 사이즈를 설정하고 salt를 이용해 KeySpec을 만듭니다.
8 KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), Hex.decodeHx(salt.toCharArray()), iterationCount, keySize);
9 // KeySpec을 적용해 PBKDF2 방식으로 암호화된 비밀키를 만들어줍니다.
10 SecretKey = key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
11 // PKCS5Padding의 Instance를 불러오지만, PKCS#7과 로직이 일치하여 해당 인스턴스만 존재합니다.
12 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
13 // CBC모드로 IV를 적용하여서 해독합니다.
14 cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Hex.decodeHex(iv.toCharArray())));
15 byte[] decrypted = cipher.doFinal(Base64.decodeBase64(ciphertext));
16 return neew String(decrypted, "UTF-8");
17}