1. ๋ฌธ์ ์ํฉ
๋ด๊ฐ ๋ง๋ ์ฌ์ดํธ์์๋ ์๋ฌด๋๋ ์ฐ๋ฆฌ ํ๊ต ๋ํ์์ ์๋๋ก ํ๊ธฐ ๋๋ฌธ์ ํ์๊ฐ์
์ ํ๋ฒ, ํจ์ค์๋, ์ด๋ฉ์ผ์ ์
๋ ฅํ๋ค.
์ด ์ ๋ณด๋ ๊ฐ์ธ์ ๋ณด ์ด๋ฏ๋ก ์ํธํ ํ ํ์๊ฐ ์๋ค.
ํจ์ค์๋ ๊ฐ์ ๊ฒฝ์ฐ๋ ๋น๊ต๋ง ํ๋ฉด ๋๋ฏ๋ก ๋จ๋ฐฉํฅ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ์ด์ฉํ๋ PasswordEncoder์ ๋์์ ํตํด ์ฝ๊ฒ ์ํธํ ํ์๋๋ฐ,
ํ๋ฒ์ด๋ ์ด๋ฉ์ผ์ ์ ๋ณด๋ก ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋ณตํธํํ ์ ์์ด์ผ ํ๋ฏ๋ก ์๋ฐฉํฅ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํด์ผ ํ๋ค.
์ด๋ป๊ฒ ๋์ํ๋์ง๋ฅผ ๋จผ์ ํ์ ํ๊ธฐ ์ํด ๋จผ์ AES256์ ๋ํด์ ๊ณต๋ถํ๊ณ ์ฝ๋๋ฅผ ๊ตฌ์ฑํ์๋ค.
2. ๋ฌธ์ ํด๊ฒฐ
โ๏ธ AES-256 ๋์นญํค ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์
AES(Advanced Encryption Standard)
AES ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ ์ํธํ ๋ฐ ๋ณตํธํ์ ๋์ผํ ํค๋ฅผ ์ฌ์ฉํ๋ ๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
DES ์ํธํ ์๊ณ ๋ฆฌ์ฆ ๋ฐฉ์๋ ์์ง๋ง ์ด๋ 64 ๋นํธ์ ๊ธธ์ด๋ฅผ ๊ฐ์ง ํค๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ๊ณต๊ฒฉ์ ์ทจ์ฝํ๋ค๋ ๋จ์ ์ด ์์ด ์ฌ์ฉํ์ง ์๋๋ค๊ณ ํ๋ค.
๋์นญํค ์๊ณ ๋ฆฌ์ฆ์ ๋ํด์๋ ์ ๊ฒ์๋ฌผ์์ HTTPS์ ๋ํด์ ์ค๋ช
ํ๋ฉด์ ๋งํ์ง๋ง
๋์นญํค๊ฐ ๋
ธ์ถ๋์์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๊ฐ ๊ทธ๋๋ก ๋
ธ์ถ๋ ์ ์๋ค๋ ๋จ์ ์ด ์์ง๋ง ์ฅ์ ์ผ๋ก๋ ๋์ ์์ ์ฑ๊ณผ ํ๋ก์ธ์ฑ ์๋๊ฐ ๋น ๋ฅด๋ค๋ ๊ฒ์ด๋ค.
AES ์ํธํ์๋ Key, IV(Initialize Vector), Cipher Mode(EBC/CBC ๋ฑ), Padding Mode(PKCS5/PKCS7 ๋ฑ) 4๊ฐ์ง๊ฐ ํ์ํ๋ค.
โ๏ธ Secret Key์ ๋ํด์ ์์๋ณด์
AES-128, AES-192, AES-256 ๋ฑ์ ์ข
๋ฅ๊ฐ ์์ผ๋ฉฐ ์ซ์๋ ํค์ ๊ธธ์ด(bit)๋ฅผ ์๋ฏธํ๋ค.
๋ด๊ฐ ์ฌ์ฉํ AES-256 ๋ฐฉ์์ ํค๊ฐ 256bits=32byte ์ธ ๊ฒ์ด๋ค.
โ๏ธ Padding Mode์ ๋ํด์ ์์๋ณด์
Block Cipher์ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ๋ธ๋ก ํฌ๊ธฐ์ ์ ์๋ฐฐ๊ฐ ๋์ง ์์ ๊ฒฝ์ฐ์ padding์ด ํ์ํ๋ค.
๋๋ถ๋ถ์ ๋์นญํ ์๊ณ ๋ฆฌ์ฆ์ 1. No Padding 2. PKCS#5 Padding ๋ ํจ๋ฉ ์ ํ ์ค ํ๋๋ฅผ ์ฌ์ฉํ๋ค.
PKCS(Public Key Cryptography Standard)#5
- ์ถ๊ฐ๋ ๋ฐ์ดํธ ์๋ก ์ฑ์ฐ๋ ๋ฐฉ๋ฒ
ex) 8๋ฐ์ดํธ ๋ธ๋ญ์ธ ๊ฒฝ์ฐ
๋ฐ์ดํฐ ํฌ๊ธฐ๊ฐ 3: ddd55555
๋ฐ์ดํฐ ํฌ๊ธฐ๊ฐ 5: ddddd333
โ๏ธ Cipher Mode์ IV ์ด๊ธฐํ ๋ฒกํฐ์ ๋ํด์ ์์๋ณด์
1. ECB(Electric CodeBook) Mode
- ํ๋ฌธ ๋ธ๋ก์ ์ํธํํ ๊ฒ์ด ๊ทธ๋๋ก ์ํธ๋ฌธ ๋ธ๋ก์ด ๋๋ ๋ฐฉ๋ฒ.
- ๋์ผํ ๋ด์ฉ์ ๊ฐ๋ ํ๋ฌธ ๋ธ๋ก์ ์ด์ ๋์๋๋ ๋์ผํ ์ํธ๋ฌธ ๋ธ๋ก์ผ๋ก ๋ณํ๋๋ค.
- ๊ฐ์ฅ ๊ฐ๋จํ ๋ชจ๋์ด์ ๊ฐ์ฅ ๊ธฐ๋ฐ์ฑ์ด ๋ฎ์ ๋ชจ๋์ด๋ค.
- ํ๋ฌธ ๋ธ๋ก๊ณผ ์ํธ๋ฌธ ๋ธ๋ก์ด ํญ์ 1๋1 ๊ด๊ณ๋ฅผ ์ ์งํ๋ค.
- ์ํธ๋ฌธ์ ์ดํด๋ณด๋ ๊ฒ๋ง์ผ๋ก๋ ํ๋ฌธ ์์ ํจํด์ ๋ฐ๋ณต์ด ์๋ค๋ ๊ฒ์ ์๊ฒ ๋๋ฉฐ, ์ด ํจํด์ ํตํด ์ํธํด๋
์ ํ ์ ์๋ค.
=> ์ด ๋ชจ๋๋ ์์ ํ์ง ์๋ค.
2. CBC(Cipher Block Chaining) Mode
- ์ํธ๋ฌธ ๋ธ๋ก์ ๋ง์น Chain ์ฒ๋ผ ์ฐ๊ฒฐํ๋ ๋ฐฉ๋ฒ.
- 1๋จ๊ณ ์์ ์ํธ๋ฌธ ๋ธ๋ก์ ํ๋ฌธ ๋ธ๋ก์ XOR ํ๊ณ ๋์ ์ํธํ๋ฅผ ์ํํ๋ค.
- ๋ฐ๋ผ์ ์์ฑ๋๋ ๊ฐ๊ฐ์ ์ํธ๋ฌธ ๋ธ๋ก์ ํ์ฌ ํ๋ฌธ ๋ธ๋ก ๋ฟ๋ง ์๋๋ผ ๊ทธ ์ด์ ์ ํ๋ฌธ ๋ธ๋ก๋ค์ ์ํฅ๋ ๋ฐ๊ฒ ๋๋ค.
๋ฐ๋ผ์ ํ๋ฌธ ๋ธ๋ก์ ์ํธํํ ๋๋ <1๋จ๊ณ ์์ ์ํธ๋ฌธ ๋ธ๋ก>์ด ์กด์ฌํ์ง ์์ผ๋ฏ๋ก <1๋จ๊ณ ์์ ์ํธ๋ฌธ ๋ธ๋ก>์ ๋์ ํ ๋นํธ์ด์ธ ํ ๊ฐ์ ๋ธ๋ก์ ์ค๋นํ ํ์๊ฐ ์๋ค.
์ด ๋นํธ์ด์ ์ด๊ธฐํ ๋ฒกํฐ IV(Initialization Vector)๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ด๊ธฐํ ๋ฒกํฐ๋ ๋น๋ฐํค์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ก์ ์์ ์์ ์๊ฐ์ ๋ฏธ๋ฆฌ ์ฝ์๋์ด ์์ด์ผ ํ์ง๋ง ๊ณต๊ฐ๋ ๊ฐ์ ์ฌ์ฉํด๋ ๋ฌด๋ฐฉํ๋ค.
โ๏ธ ์์ธํ ์ค๋ช ์ ์ ์ฐ์ ์ฝ๋๋ฅผ ๋ณด์
@Component
public class AES256 {
private String keyDir;
private String ivDir;
private static String alg = "AES/CBC/PKCS5Padding";
private byte[] iv;
private byte[] key;
public AES256(@Value("${fileDir.aeskey}") String keyDir,
@Value("${fileDir.iv}") String ivDir) throws Exception {
this.ivDir = ivDir;
this.keyDir = keyDir;
this.key = getKey();
this.iv = generateIv();
}
public String encrypt(String text) {
byte[] encrypted = null;
try{
Cipher cipher = Cipher.getInstance(alg);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParamSpec);
encrypted = cipher.doFinal(text.getBytes("UTF-8"));
} catch (Exception ex) {
ex.printStackTrace();
}
return Base64.getEncoder().encodeToString(encrypted);
}
public String decrypt(String cipherText) throws Exception {
byte[] decrypted = null;
try {
Cipher cipher = Cipher.getInstance(alg);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParamSpec);
byte[] decodedBytes = Base64.getDecoder().decode(cipherText);
decrypted = cipher.doFinal(decodedBytes);
} catch (Exception ex) {
ex.printStackTrace();
}
return new String(decrypted, "UTF-8");
}
private byte[] generateIv() throws Exception {
File keyFile = new File(ivDir);
FileInputStream fis = new FileInputStream(keyFile);
byte[] keyBytes = new byte[(int) keyFile.length()];
fis.read(keyBytes);
fis.close();
return keyBytes;
}
private byte[] getKey() throws Exception {
File keyFile = new File(keyDir);
FileInputStream fis = new FileInputStream(keyFile);
byte[] keyBytes = new byte[(int) keyFile.length()];
fis.read(keyBytes);
fis.close();
return keyBytes;
}
}
โ๏ธ ๋ค์ ์ค๋ช ์ฌ๊ฒ
๋์ถฉ ํ๋ฆ์ ํ์
ํ์ ๊ฒ์ด๋ค.
์ํธํ๋ plain text -> plain bytes -> encrypt -> encrypted bytes -> encrypted Base64 Text
๋ณตํธํ๋ ๋น์ฐํ ๊ทธ ๋ฐ๋๋ก ํ๋ฌ๊ฐ๊ฒ ๋๋ค.
์ฐ์ ์์์ ์ฌ์ฉํ Cipher, SecretKeySpec, IvParameterSpec๊ณผ ๊ฐ์ ํด๋์ค๋ค์ Java์ ๊ธฐ๋ณธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ค.
๋๋ Secret key์ iv๋ฅผ ํ์ผ๋ก ๋ถ๋ฆฌํ์๋ค.
์๋ ์ฝ๋๋ฅผ ๋จผ์ ์ดํด๋ณด์.
Cipher ํด๋์ค๋ ์ํธํ, ๋ณตํธํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ฉฐ Java Cryptography Extension๋ฅผ ๊ตฌ์ฑํ๋๋ฐ ํต์ฌ์ ์ธ ์์์ด๋ค.
CBC๋ชจ๋๋ฅผ ์ฌ์ฉํ๋ฉฐ, PKCS5 Padding Scheme๋ก ์ด๊ธฐํ๋ Cipher ์ธ์คํด์ค๋ฅผ ์ป๊ฒ๋๋ค.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
๊ทธ๋ค์ SecretKeySpec ์ธ์คํด์ค๋ฅผ ๋น๋ฐํค๋ฅผ ํตํด ์์ฑํ๊ณ , IvParameterSpec ์ธ์คํด์ค๋ฅผ iv ๊ฐ์ ํตํด ์์ฑํ๋ค.
โ๏ธ Secret Key์ IV ์์ฑ ๋ฐฉ๋ฒ
package org.example;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.FileOutputStream;
import java.security.SecureRandom;
import java.util.Base64;
public class KeyGenerator {
public static void main(String[] args) throws Exception {
// AES 256-bit key ์์ฑ
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey secretKey = keyGen.generateKey();
byte[] keyBytes = secretKey.getEncoded();
System.out.println("Generated AES key (Base64 encoded): " + Base64.getEncoder().encodeToString(keyBytes));
try (FileOutputStream fos = new FileOutputStream("/example/kiosek/key.txt")) {
fos.write(keyBytes);
}
// iv ๊ฐ ์์ฑ
SecureRandom secureRandom = new SecureRandom();
byte[] keyBytes = new byte[16];
secureRandom.nextBytes(keyBytes);
System.out.println("Generated iv key: " + Base64.getEncoder().encodeToString(keyBytes));
try (FileOutputStream fos = new FileOutputStream("/example/kiosek/iv.txt")) {
fos.write(keyBytes);
}
}
}