데이터(문자, 파일 등)를 특정한 알고리즘을 사용하여 변환하여 데이터 보호하는 위한 보안 기술입니다.
암호화는 크게 대칭키(Symmetric Key) 암호화와 비대칭키(Asymmetric Key) 암호화 두 가지가 있습니다.
하나의 키(Key)로 암호화와 복호화를 모두 수행하는 방식입니다.
1️⃣ A(보내는 사람)가 **비밀 키(Secret Key)** 를 사용해 데이터를 암호화.
2️⃣ B(받는 사람)는 **같은 비밀 키**를 사용해 데이터를 복호화.
3️⃣ **문제점**: 키가 노출되면 누구나 데이터를 복호화할 수 있음.
알고리즘 | 키 길이 | 설명 |
---|---|---|
**AES** | 128/192/256비트 | 가장 많이 사용되는 강력한 암호화 알고리즘 |
**DES** | 56비트 | 더 이상 안전하지 않음 (취약) |
**3DES** | 168비트 | DES보다 보안이 강하지만 속도가 느림 |
**RC4** | 가변 길이 | 빠르지만 보안 문제로 사용 지양 |
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AESExample {
private static final String SECRET_KEY = "12345678901234567890123456789012"; // 32바이트 키
public static String encrypt(String data) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypted = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String encryptedData) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decrypted);
}
}
공개키(Public Key)와 비공개키(Private Key) 두 개의 키를 사용하는 방식입니다.
1️⃣ **공개키(Public Key)는 누구나 가질 수 있음**.
2️⃣ **비공개키(Private Key)는 소유자만 알고 있어야 함**.
3️⃣ A(보내는 사람)가 **B의 공개키로 데이터를 암호화**.
4️⃣ B(받는 사람)는 **자신의 비공개키로 복호화**.
5️⃣ **공개키로는 복호화할 수 없고, 반드시 비공개키가 필요**.
알고리즘 | 키 길이 | 설명 |
---|---|---|
**RSA** | 1024/2048/4096비트 | 가장 많이 사용되는 공개키 암호화 |
**ECC** | 256/384비트 | RSA보다 짧은 키 길이로 높은 보안 제공 |
**Diffie-Hellman** | 1024/2048비트 | 키 교환 프로토콜 |
import java.security.*;
import javax.crypto.Cipher;
import java.util.Base64;
public class RSAExample {
private static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
return keyGen.generateKeyPair();
}
public static String encrypt(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
}
public static String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedData)));
}
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateKeyPair();
String encrypted = encrypt("Hello, World!", keyPair.getPublic());
String decrypted = decrypt(encrypted, keyPair.getPrivate());
System.out.println("🔐 암호화된 데이터: " + encrypted);
System.out.println("🔑 복호화된 데이터: " + decrypted);
}
}
대칭키(AES)로 데이터를 암호화하고, 대칭키는 비대칭키(RSA)로 암호화하는 방식입니다.
이 방식이 HTTPS(SSL/TLS)에서도 사용됩니다.
대칭키의 속도와 비대칭키의 보안성을 모두 확보할 수 있는 방법입니다.
1️⃣ 서버는 **AES-256 대칭키를 생성**.
2️⃣ 서버는 **클라이언트의 공개키로 AES 키를 암호화**.
3️⃣ 클라이언트는 **RSA 비공개키로 AES 키를 복호화**.
4️⃣ 이후 데이터는 **AES-256을 사용하여 빠르게 암호화/복호화**.
클라이언트는 Web Crypto API를 사용하여 공개키, 비공개키를 생성합니다. 비공개키는 브라우저 내부 Web Crypto API에서만 접근 가능하며, 직접 유출될 위험이 줄어듭니다.
async function generateRSAKeyPair() {
const keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true, // 내보내기(export) 가능 여부
["encrypt", "decrypt"] // 공개키(암호화), 비공개키(복호화) 사용
);
return keyPair;
}
브라우저에서 생성된 공개키(Public Key)는 서버에 보내서 데이터 암・복호화에 사용될 비밀키를 암호화하는데 사용합니다.
async function sendPublicKeyToServer(publicKey) {
const exportedKey = await window.crypto.subtle.exportKey("spki", publicKey);
const publicKeyBase64 = btoa(String.fromCharCode(...new Uint8Array(exportedKey)));
await fetch("/api/storePublicKey", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ publicKey: publicKeyBase64 }),
});
}
데이터 암・복호화에 필요한 비밀키를 안전하게 전송하기 위해 클라이언트에서 받은 공개키로 비밀키를 암호화 합니다.
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import java.util.Base64;
public class RSAEncryptor {
//data가 AES 암호화에 사용되는 비밀키가 됨
public static String encryptData(String data, String publicKeyBase64) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(publicKeyBase64);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
}
서버에서 전달받은 암호화된 비밀키를 비공개키로 복화하여 데이터를 암호화하여 서버로 전달합니다.
async function decryptData(encryptedData, privateKey) {
const encryptedBytes = Uint8Array.from(atob(encryptedData), c => c.charCodeAt(0));
const decryptedData = await window.crypto.subtle.decrypt(
{ name: "RSA-OAEP" },
privateKey,
encryptedBytes
);
return new TextDecoder().decode(decryptedData);
}