1. RSA 암호
0️⃣ 필기


상민이가 a공개키라고 하고 자신의 공개키를 공개하면 이게 누구의 공개키인지 알 수 없음!
그래서 RSA로 대칭키를 전달해서 대칭키 전달의 문제 해결하고
정말 보내고자 하는 내용은 대칭키로 잠궈서 보냄!
1️⃣ 보안의 3요소 (CIA)
- 기밀성 (Confidentiality)
→ 허가된 사람만 메시지를 볼 수 있어야 함
- 무결성 (Integrity)
→ 메시지가 중간에 위·변조되지 않았음을 보장
- 가용성 (Availability)
→ 필요할 때 정상적으로 사용 가능
2️⃣ 기존 통신 환경의 문제
1) 상황
- 송신자 홍군 A (50명) ↔ 수신자 홍군 B (50명)
- 중간 공격자(청군) 약 70명
- 공격 방식
- 도청: 평문(Plaintext) 그대로 탈취
- 위조: 메시지를 바꿔치기 (무결성 깨짐)
2) 문제점
- 암호화를 안 하면 → 기밀성 X
- 암호화를 해도:
- “누가 보냈는지” 확인 불가 → 무결성 X
- 열쇠를 안전하게 전달할 방법이 없음
3️⃣ 대칭키 암호의 한계
- 암호화(C)와 복호화에 같은 키 사용
- 장점: 빠름
- 치명적 단점:
- 🔑 키를 안전하게 전달할 방법이 없음
- 키가 탈취되면 모든 보안 붕괴
4️⃣ 디피-헬만(Diffie-Hellman) 키 교환
1) 목적
- 기밀성 확보
- 공개된 채널에서도 공통 비밀키 생성
2) 개념 흐름
- 공개 정보
- 공통 소수 ggg
- 공통 모듈러 ppp
- 개인 비밀
- A의 비밀값 aaa
- B의 비밀값 bbb
- 계산
- A → gamod pg^a \mod pgamodp
- B → gbmod pg^b \mod pgbmodp
- 최종 공유 비밀
- (gb)a=(ga)b(g^b)^a = (g^a)^b(gb)a=(ga)b
3) 핵심
- 공개키 값은 커질수록 경우의 수 폭증
- 역으로 비밀값을 구하는 것이 매우 어려움
4) ⚠️ 한계
- 누가 보냈는지 증명 불가 → 무결성 해결 안 됨
5️⃣ RSA의 등장 배경
- Diffie-Hellman: 기밀성 O / 무결성 X
- RSA 목표:
- ✅ 기밀성
- ✅ 무결성
- 공개키 환경에서 안전한 통신
6️⃣ RSA 기본 개념
1) 키 구조 (2개)
- 공개키 (Public Key) → open
- 개인키 (Private Key) → close
2) 특징
- 공개키로 암호화한 메시지는
→ 오직 대응되는 개인키로만 복호화 가능
- 개인키는 절대 공개 ❌
7️⃣ RSA 통신 시나리오 (예시)
상황
- 길동 → 꺽정에게 메시지 전송
- 중간에 상민(공격자) 존재
① A의 개인키만 사용 (서명 X)
- A 개인키로 암호화 → 누구나 공개키로 열 수 있음
- ❌ 기밀성 없음
- ✔️ 무결성은 어느 정도 확보
② B의 공개키만 사용
- 기밀성 O
- ❌ 누가 보냈는지 알 수 없음
- 상민이 메시지 위조 가능
③ RSA 핵심 방식 (정답)
- B의 공개키로 암호화
→ 기밀성 확보
- A의 개인키로 서명
→ 무결성 확보
수신자 B는:
- A의 공개키로 서명 검증 (누가 보냈는지 확인)
- B의 개인키로 메시지 복호화
➡️ 기밀성 + 무결성 모두 충족
8️⃣ RSA 핵심 요약
- 🔐 공개키 암호 방식
- 🔑 키 전달 문제 해결
- ✍️ 전자서명으로 무결성 확보
- 📌 Diffie-Hellman의 단점을 보완
9️⃣ 한 줄 요약
RSA는 “공개키 암호 + 전자서명”을 통해 기밀성과 무결성을 동시에 만족시키는 암호 방식이다.
🔟 실습
implementation "com.nimbusds:nimbus-jose-jwt:9.37.3"import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
public class RSAUtil {
// RSA 키 생성 (개인키, 공개키)
public static RSAKey generateRSAKey(String keyId) throws JOSEException {
return new RSAKeyGenerator(2048).keyID(keyId).generate();
}
// 공개키로 암호화
public static String encrypt(String message, RSAPublicKey publicKey) throws JOSEException {
JWEObject encryptObject = new JWEObject(
new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM),
new Payload(message.getBytes(StandardCharsets.UTF_8))
);
encryptObject.encrypt(new RSAEncrypter(publicKey));
return encryptObject.serialize();
}
// 개인키로 서명
public static String sign(String payload, RSAPrivateKey privateKey) throws JOSEException {
JWSObject signObject = new JWSObject(
new JWSHeader(JWSAlgorithm.RS256),
new Payload(payload)
);
signObject.sign(new RSASSASigner(privateKey));
return signObject.serialize();
}
// 공개키로 서명검증
public static String verifySignature(String signedToken, RSAPublicKey publicKey) throws Exception {
JWSObject signObject = JWSObject.parse(signedToken);
if (signObject.verify(new RSASSAVerifier(publicKey))) {
return signObject.getPayload().toString();
}else{
throw new RuntimeException("공개키 서명 검증 실패");
}
}
// 개인키로 복호화
public static String decrypt(String encryptedToken, RSAPrivateKey privateKey) throws Exception {
JWEObject encryptObject = JWEObject.parse(encryptedToken);
encryptObject.decrypt(new RSADecrypter(privateKey));
return encryptObject.getPayload().toString();
}
}import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jose.jwk.RSAKey;
public class RSADemo {
public static void main(String[] args) throws Exception {
String msg = "나는 오늘 백운포에 송원 왕갈비를 먹으러 간다 비밀이다!!";
// 0. A(송신자), B(수신자) RSA 키 생성
RSAKey aKey = RSAUtil.generateRSAKey("A");
RSAKey bKey = RSAUtil.generateRSAKey("B");
RSAKey cKey = RSAUtil.generateRSAKey("C");
// ===== 송신 과정 =====
// 1. 수신자(B)의 공개키로 암호화
String encrypted = RSAUtil.encrypt(msg, bKey.toRSAPublicKey());
System.out.println("1. 암호화: " + encrypted.substring(0, 50) + "...");
// 2. 송신자(A)의 개인키로 서명
String signed = RSAUtil.sign(encrypted, aKey.toRSAPrivateKey());
System.out.println("2. 서명: " + signed.substring(0, 50) + "...");
System.out.println();
// ===== 수신 과정 =====
// 3. 송신자(A)의 공개키로 서명 검증
String verify = RSAUtil.verifySignature(signed, aKey.toRSAPublicKey());
System.out.println("3. 서명 검증: " + verify);
// 4. 수신자(B)의 개인키로 복호화
String decrypted = RSAUtil.decrypt(verify, bKey.toRSAPrivateKey());
System.out.println("4. 복호화: " + decrypted);
}
}Share article