Download presentation
Presentation is loading. Please wait.
1
자바 암호 프로그래밍 Java Cryptography Programming 2017. 3.
중부대학교 정보보호학과 이병천 교수
2
차례 1. 강의 개요 2. 자바프로그래밍 기초 3. 자바 네트워크 프로그래밍 4. 자바 GUI 프로그래밍
5. JCA/JCE 암호 프로그래밍 6. 해쉬함수, MAC, 패스워드 기반 키생성 7. 대칭키 암호, 파일 암호화/복호화 8. 공개키 암호 9. 전자서명 10. 인증서와 공개키기반구조(PKI) 11. 암호 알고리즘/프로토콜 구현
3
6. 해쉬함수, MAC, PBKDF2
4
1. 해쉬함수 소개 X 해쉬함수(hash function)란?
임의의 길이의 데이터를 입력받아서 고정된 길이의 특징값을 출력하는 함수 메시지 다이제스트(message digest)라고도 부름 암호키를 사용하지 않는 공개된 함수. 동일 한 입력값에 대해 항상 동일한 해쉬값을 출 력함 입력값으로부터 해쉬값을 계산하는 것은 쉽 지만 해쉬값으로부터 그것을 출력하는 입력 값을 찾는 것은 어려움 (일방향함수) Message M H X Message Digest D D = H(M)
5
해쉬함수
6
해쉬함수 해쉬함수의 요구조건 역상 저항성(Pre-image resistance): 주어진 출력에 대하여 입력 값을 구하는 것이 계산상 불가능하다. 제2 역상 저항성(Second pre-image resistance): 주어진 입력에 대하여 같은 출력을 내는 또 다른 입력을 찾아내는 것이 계산상 불가능하다. 충돌저항성(Collision resistance): 같은 출력을 내는 임의의 서로 다른 두 입력 메시지를 찾는 것이 계산상 불가능하다.
7
해쉬함수의 용도 메시지 다이제스트: 문서의 위조 방지 안전한 난수생성 패스워드 저장
해쉬값을 생성하여 함께 제공 전자서명과 함께 사용 안전한 난수생성 SecureRandom에서 난수생성을 위해 해쉬함수 이용 패스워드 저장 사용자의 패스워드를 서버에서는 그대로 저장하지 않고 salt를 이용한 해쉬값으로 저장 패스워드 기반 키생성 알고리즘 (PBKDF2) 사용자 입력 패스워드로부터 난수처럼 보이는 비밀키를 생성 체크섬 생성 및 검증 인터넷으로 배포되는 소프트웨어의 원본 보증
8
해쉬함수의 용도 패스워드 저장 체크섬 생성 및 검증
9
해쉬함수 알고리즘 해쉬 알고리즘 Md5 Sha1 Sha256 Sha384 Sha512
10
SHA-1 NIST FIPS 180-1 표준 해쉬함수 온라인 테스트 블록 사이즈 512 비트 출력 해쉬값 160비트
11
2. 해쉬함수 계산 MessageDigest 엔진 이용 객체 초기화 데이터 입력 해쉬계산 완료
public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException 데이터 입력 public void update(byte input) 해쉬계산 완료 public byte[] digest() MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update((byte) rnd); byte[] digest = md5.digest();
12
해쉬함수 계산 해쉬값의 비교 isEqual 메소드 이용: 두 개의 바이트 배열을 비교한다.
public static boolean isEqual(byte[] digesta, byte[] digestb) java.util.Array.equals()을 이용할 수도 있다.
13
해쉬함수의 종류 JCA/JCE에서 지원하는 해쉬함수들 MD2 (RFC 1319) MD5 (RFC 1321)
SHA-1 (NIST FIPS 180-1) SHA-256, SHA-384, SHA-512: SHA-1의 해쉬값의 길이를 증가 시켜 충돌회피성을 높이고자 제안된 해쉬함수
14
해쉬함수 계산 예제 java.security.MessageDigest 엔진을 사용 try{
MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(plaintext.getBytes()); byte[] digest01 = md5.digest(); for(byte b: digest01) System.out.printf("%02X ", b); System.out.println(); byte[] digest02 = md5.digest(); System.out.printf("Verified = %s%n", MessageDigest.isEqual(digest01, digest02)); } catch(NoSuchAlgorithmException e){ e.printStackTrace();
15
두 메시지의 해쉬값 비교 public class DigestTest {
public static void main(String[] args) { String plaintext01 = "This is a simple message."; String plaintext02 = "This is a simple message"; try{ MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(plaintext01.getBytes()); byte[] digest01 = md5.digest(); for(byte b: digest01) System.out.printf("%02X ", b); System.out.println(); md5.update(plaintext02.getBytes()); byte[] digest02 = md5.digest(); for(byte b: digest02) System.out.printf("%02X ", b); System.out.printf("Verified = %s%n", MessageDigest.isEqual(digest01, digest02)); } catch(NoSuchAlgorithmException e){ e.printStackTrace(); } // main } // DigestTest
16
두 메시지의 해쉬값 비교 import java.security.*; public class HashTest {
public static void main(String[] args) { String plaintext01 = "This is a simple message...";; String plaintext02 = "This is a simple message"; // 해쉬 알고리즘 선택: MD2 MD5 SHA-1 SHA-224 SHA-256 SHA-384 SHA-512 try{ System.out.println("MD5 Algorithm"); MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(plaintext01.getBytes()); System.out.println("Text1 : "+plaintext01); byte[] digest01 = md5.digest(); System.out.print("Hash1 : "); for(byte b: digest01) System.out.printf("%02X ", b); System.out.println(); md5.update(plaintext02.getBytes()); System.out.println("Text2 : "+plaintext02); byte[] digest02 = md5.digest(); System.out.print("Hash2 : "); for(byte b: digest02) System.out.printf("%02X ", b); System.out.print("\n해쉬 길이 : "+digest02.length+ " byte" ); System.out.printf("Verified = %s \n\n", MessageDigest.isEqual(digest01, digest02)); MD5 Algorithm Text1 : This is a simple message... Hash1 : 5C 30 7D EF FE BA F7 97 F1 67 A6 CD 91 EF 7D A1 Text2 : This is a simple message Hash2 : FB B2 5E F6 ED EB 7A 80 B D C2 36 해쉬 길이 : 16 byte Verified = false
17
3. 파일의 체크섬 생성/검증 인터넷으로 배포하는 파일의 진위성 확인을 위해 사용 (ChecksumExample.java)
1. 확인할 파일을 프로젝트 폴더 루트에 복사 httpd tar.gz 2. 인터넷에 게시된 해쉬값을 변수로 저장 httpd tar.gz.md cf0c58f14de2a5ce9426df2bc *httpd tar.gz httpd tar.gz.sha1 19e94b8c9e727cc16b c5b0e27ebc08d5 *httpd tar.gz Given MD5 : cf0c58f14de2a5ce9426df2bc Computed MD5 : cf0c58f14de2a5ce9426df2bc true Given SHA1 : 19e94b8c9e727cc16b c5b0e27ebc08d5 Computed SHA1 : 19e94b8c9e727cc16b c5b0e27ebc08d5
18
4. 인터넷으로 동전던지기 게임하기 인터넷으로 동전던지기 게임을 할 수 있을까? 공정성의 조건은?
상대방을 어떻게 신뢰? 공정성 문제 공정성의 조건은? 암호를 이용하는 동전던지기 게임 해쉬함수 이용 암호 알고리즘을 이용
19
해쉬함수를 이용한 동전던지기 Commitment Bidding Reveal Verify
20
CoinToss.java 해쉬함수를 이용한 동전던지기 게임 시뮬레이션 사용자 A: 동전을 던지는 사람
사용자 B: 홀짝을 맞추는 사람 1. A는 임의의 숫자를 입력하세요 >> A->B 해쉬값 전송: 0cc175b9c0f1b6a831c399e 2. B는 홀짝을 입력하세요 (홀/짝)>> 홀 A<-B 홀짝값 전송: 홀 3. 게임 결과 확인 A->B 원래의 숫자 전송: B의 해쉬값 확인: 0cc175b9c0f1b6a831c399e 해쉬값이 같습니다. 4. 동전던지기 승부 확인 가 홀인가요? B가 맞추었으므로 B가 이겼습니다.
21
통신을 이용한 동전던지기 게임 클라이언트/서버 모델의 동전던지기 서비스 서버: CoinTossServer.java
클라이언트: CoinToss.Client.java 인터넷 동전던지기 게임 방문을 환영합니다. 다음은 서버에서 보내온 어떤 난수 정수의 해쉬값입니다. 서버에서 보낸 해쉬값: 0fbd1776e1ad22c59a7080d35c7fd4db 홀/짝을 맞추어 보세요. 1)홀수, 2)짝수, 3)게임종료 1 서버에서 보낸 난수값: 97373 다시 계산한 해쉬값: 0fbd1776e1ad22c59a7080d35c7fd4db 해쉬값이 일치합니다. 결과: 사용자 승리
22
5. MAC (메시지인증코드) MAC(메시지인증코드)란? MAC: Message Authentication Code
해쉬함수와 송수신자간에 공유된 비밀키를 이용하여 송수신하는 메시지의 원본성을 인증하는데 사용 MAC은 대칭키 암호알고리즘처럼 송신자와 수신자 사이의 공유 된 비밀키가 필요 MAC 계산에는 공유된 비밀키를 이용하므로 송신자와 수신자만 계산하고 검증할 수 있음. 중간에서 공격자가 메시지를 변조하는 것을 방지 가능 송신자는 입력메시지와 비밀키를 이용하여 hmac 계산하여 전송. 수신자는 전송된 메시지와 비밀리를 이용하여 hmac 값이 맞는 지 검증.
23
MAC
24
Hash와 MAC의 비교 Hash MAC
25
MAC의 한계 제3자에게 MAC의 유효성 여부를 증명하지 못함 부인방지 불가
이것을 증명하려면 공유된 비밀키를 제3자에게 공개해야 함 부인방지 불가 송신자가 메시지를 보내지 않았다고 부인하는 경우 분쟁 해결이 어려움. 수신자도 동일한 MAC을 계산할 수 있기 때문 부인방지 기능을 제공하기 위해서는 전자서명을 사용
26
HMAC 알고리즘 From Wikipedia
27
HMAC 알고리즘 HMAC keyed-hash message authentication code RFC 2104
일방향 해시함수를 이용하여 메시지 인증코드를 구성하는 방법
28
MAC JCA/JCE에서의 MAC javax.crypto.Mac 엔진을 사용 기존의 비밀키를 MAC에 적합하도록 변환하여 사용
// 키 생성 – 송신자와 수신자가 공유 KeyGenerator kg = KeyGenerator.getInstance(“AES"); SecretKey key = kg.generateKey(); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(),"HmacSHA1"); // MAC 계산 – 송신자 계산, 수신자 검증 Mac mac = Mac.getInstance("HmacSHA1"); mac.init(keySpec); mac.update(plaintext01.getBytes()); byte[] digest01 = mac.doFinal();
29
MAC 제공되는 MAC 알고리즘 HmacMD5, HmacSHA1, HmacSHA256, HmacSHA384, HmacSHA512
30
MAC 사례 try{ KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey key = kg.generateKey(); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(keySpec); mac.update(plaintext.getBytes()); byte[] digest01 = mac.doFinal(); for(byte b: digest01) System.out.printf("%02X ", b); System.out.println(); byte[] digest02 = mac.doFinal(); System.out.printf("Verified = %s%n", MessageDigest.isEqual(digest01, digest02)); } catch(NoSuchAlgorithmException e){ e.printStackTrace();
31
MACTest.java String plaintext01 = "This is a simple message.";
KeyGenerator kg = KeyGenerator.getInstance("AES"); SecretKey key = kg.generateKey(); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(),"HmacSHA1"); byte[] keybyte = key.getEncoded(); System.out.println("키 : "+bytesToHex(keybyte)); System.out.println("키길이 : "+keybyte.length+ " byte" ); System.out.println(); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(keySpec); mac.update(plaintext01.getBytes()); byte[] digest01 = mac.doFinal(); mac.update(plaintext02.getBytes()); byte[] digest02 = mac.doFinal(); 키 : 6b45cc39dec61d684eac4ef0b1448abb 키길이 : 16 byte Text1 : This is a simple message. MAC1 : 2f1026b a429a29ef8d81e4c26bc425b8f MAC 길이 : 20 byte Text2 : This is a simple message.. MAC2 : d9fea50d90c3a9eb756721fdec82bf586db988b6
32
6. 패스워드 기반 암호화 패스워드와 비밀키 패스워드 기반 키생성함수 (PBKDF2)
사용자가 입력하는 패스워드를 직접 암호알고리즘의 비밀키로 사용하는 것은 추측 가능한 키를 사용하게 되므로 위험 무작위 대입공격, 사전공격 가능 난수화된 비밀키를 사용해야 함 패스워드 기반 키생성함수 (PBKDF2) Password-Based Key Derivation Function v2 (1)사용자 입력 패스워드, (2)랜덤한 salt값, (3)반복횟수(iteration) 값을 이용하여 난수처럼 보이는 비밀키를 생성하여 사용 salt값과 반복횟수 값은 공격자의 사전공격을 어렵게 하는 중요 한 요소
33
PBKDF2 테스트
34
PKCS#5 패스워드 기반 암호 표준 PBKDF2 (Password-Based Key Derivation Function 2)
35
PBKDF2 DK = PBKDF2(PRF, Password, Salt, c, dkLen)
36
PBKDF2 1. 사용자 입력 패스워드 2. 솔트(salt) 3. 반복횟수(iteration)
사용자가 입력하는 패스워드 기억할 수 있는 정보로서 엔트로피가 높지 않음 2. 솔트(salt) 난수로 생성하는 값 공격자의 사전공격(dictionary attack)을 방지하기 위한 정보 3. 반복횟수(iteration) 공격자의 공격비용을 증가시키기 위해 사용 DK = PBKDF2(PRF, Password, Salt, c, dkLen)
37
패스워드 기반 암호화 PBES1 (password-based encryption scheme 1)
PBKDF1을 이용하여 키생성 PBES2 (password-based encryption scheme 2) PBKDF2을 이용하여 키생성 PBKDF1 PBKDF2
38
패스워드 기반 암호화 사용 가능한 알고리즘 PBES1 PBES2
39
PBES1 PBE1Example.java 입력 패스워드: password salt: f76b8f30a582bea0
반복횟수: 1000 생성된 비밀키: f7264 생성된 파라메터: 평문: 오늘도 별이 바람에 스치운다. 암호문: b80f5ccca7019e e31b6cddbda8cc972e9a150e4f52f6afb6a6e8dc78b4808d791a55c8f3c050062f64838d81a 복호화: 오늘도 별이 바람에 스치운다.
40
PBES1 String pass = "password"; char[] password = pass.toCharArray();
System.out.println("입력 패스워드: "+pass); // salt 생성 byte[] salt = new byte[8]; SecureRandom random = new SecureRandom(); random.nextBytes(salt); System.out.println("salt: "+bytesToHex(salt)); int iterationCount = 1000; // 반복 횟수 System.out.println("반복횟수: "+iterationCount); // 패스워드를 사용한 SecretKey 생성 PBEKeySpec keySpec = new PBEKeySpec(password); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); SecretKey secretKey = keyFactory.generateSecret(keySpec); System.out.println("생성된 비밀키: "+bytesToHex(secretKey.getEncoded())); // salt, iteration count를 위한 파라미터 생성 PBEParameterSpec params = new PBEParameterSpec(salt, iterationCount); System.out.println("생성된 파라메터: "+params);
41
PBES2 PBE2Example.java 입력 패스워드: password salt: 3838b19d4d01d0fc
반복횟수: 1000 생성된 비밀키: 54bd3c9bb31155af23bab576a54a53de 입력 평문: 오늘도 별이 바람에 스치운다. 암호문: f66f518406f4f2d a053f6f4af0aee86e03ef5bfb6da83f561b63e3d0cfed1e2c4c8bb0bda28b5396e09ab06d 복호화: 오늘도 별이 바람에 스치운다.
42
PBES2 String pass = "password"; char[] password = pass.toCharArray();
System.out.println("입력 패스워드: "+pass); Charset charset = Charset.forName("UTF-8"); // salt 생성 byte[] salt = new byte[8]; SecureRandom random = new SecureRandom(); random.nextBytes(salt); System.out.println("salt: "+bytesToHex(salt)); int iterationCount = 1000; // 반복 횟수 System.out.println("반복횟수: "+iterationCount); // 패스워드를 이용한 SecretKey 생성 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, 128); SecretKey secretKey = new SecretKeySpec(keyFactory.generateSecret(keySpec).getEncoded(), "AES"); System.out.println("생성된 비밀키: "+bytesToHex(secretKey.getEncoded()));
43
패스워드 해시 저장 서버에 사용자 패스워드를 안전하게 저장하는 방법? 패스워드를 평문으로 저장 패스워드의 해시값을 저장
서버 관리자에게 패스워드 노출 서버를 해킹하는 공격자가 사용자 패스워드 획득 패스워드의 해시값을 저장 /etc/shadow
44
패스워드 해시 저장 /etc/passwd 파일 구조 root:x:0:0:root:/root:/bin/bash
① ②③④ ⑤ ⑥ ⑦ ① 필드 1 : 사용자명 ② 필드 2 : 패스워드(/etc/shadow 파일에 암호화되어 있음) ③ 필드 3 : 사용자 계정 uid ④ 필드 4 : 사용자 계정 gid ⑤ 필드 5 : 사용자 계정 이름(정보) ⑥ 필드 6 : 사용자 계정 홈 디렉토리 ⑦ 필드 7 : 사용자 계정 로그인 쉘
45
패스워드 해시 저장 $Hashid $Salt $Hash vlaue Hashid: 사용된 해쉬 알고리즘 (1, 5, 6)
/etc/shadow 파일 구조 root:$6$5H0QpwprRiJQR19Y$bXGOh7dIfOWpUb/Tuqr7yQVCqL3UkrJns9.7msfvMg4ZO/PsFC5Tbt32PXAw9qRFEBs1254aLimFeNM8YsYOv. : : 0 : : 7 : : : |-1–|———————————–2——————————————————————————————————————————————-|—3—|-4-|—5–|-6-|-7-|-8-| 1.Login Name : 사용자 계정각 항목별 구분은 :(콜론) 으로 구분되어있으며 콜론과 콜론 사이 각 필드에는 다음과 같은 구조로 구성되어 있습니다. 2.Encrypted : 패스워드를 암호화시킨 값 3.Last Changed : 1970년부터 1월 1일부터 패스워드가 수정된 날짜의 일수를 계산 4.Minimum : 패스워드가 변경되기 전 최소사용기간(일수) 5.Maximum : 패스워드 변경 전 최대사용기간(일수) 6.Warn : 패스워드 사용 만기일 전에 경고 메시지를 제공하는 일수 7.Inactive : 로그인 접속차단 일 수 8.Expire : 로그인 사용을 금지하는 일 수 (월/일/연도) 9.Reserved : 사용되지 않음 $Hashid $Salt $Hash vlaue Hashid: 사용된 해쉬 알고리즘 (1, 5, 6) Salt: 난수 솔트 Hash: 패스워드, 솔트를 입력으로 계산한 해쉬값
46
패스워드 해시 저장 PasswordHash.java 반복횟수 솔트 패스워드해시 패스워드 검증
Similar presentations