자바 암호 프로그래밍 Java Cryptography Programming 2017. 3. 중부대학교 정보보호학과 이병천 교수
차례 1. 강의 개요 2. 암호와 정보보호 3. 자바프로그래밍 기초 4. 자바 네트워크 프로그래밍 5. JCA/JCE 암호 프로그래밍 6. 해쉬함수, MAC, 패스워드 기반 키생성 7. 대칭키 암호 8. 공개키 암호 9. 전자서명 10. 인증서와 공개키기반구조(PKI) 11. 암호 알고리즘/프로토콜 구현
7. 대칭키암호 1. 대칭키 암호 소개 2. 키생성 3. Cipher 클래스를 이용한 암호화/복호화 4. 파일 암호화/복호화 5. 비밀키를 파일로 저장하기/읽어오기
1. 대칭키 암호 소개 개요 대칭키 암호화(symmetric encryption)는 암호화에 사용하는 키 와 복호화에 사용하는 키가 동일 송신자와 수신자 모두 동일한 키를 공유해야 함 비밀키는 송신자와 수신자 이외에는 알지 못하도록 해야 함 대규모 데이터의 빠른 암호화에 활용
대칭키 암호 방식
대칭키 암호 방식 데이터를 변환하는 단위에 따라 블록 암호와 스트림 암 호로 분류 블록암호: 고정된 크기의 블록 단위로 암호화/복호화 스트림암호: 난수열을 생성하여 비트단위, 글자단위로 암호화/복 호화 블록암호 스트림암호
대칭키 암호 방식 블록 암호 vs. 스트림 암호
커크호프의 원리(Kerckhoffs' Principle) A cryptosystem should be secure even if everything about the system, except the key, is public knowledge. 키를 제외한 시스템의 다른 모든 내용이 알려지더라도 암호체계 는 안전해야 한다. 비밀 암호시스템은 오히려 안전하지 않을 수 있음 많은 전문가들의 공격시도를 견뎌야 안전성을 인정받음
블록 암호 알고리즘 DES 3DES (DESede) AES 1977년 NBS(미국 표준국)에서 미국 표준 알고리즘으로 채택 64비트 블록단위 암호화, 56비트 키길이 짧은 키길이로 인한 취약성으로 현재는 사용하지 않음 Feistel 구조 3DES (DESede) DES 암호를 암호화->복호화->암호화 순으로 3번 반복 블록 크기가 64비트, 느린 속도 AES 2001년 DES를 교체하기 위해 새롭게 제정한 암호 표준 128/192/256 비트의 키길이, 10/12/14 라운드 사용 SPN(Substitution Permutation Network) 구조
블록 암호 알고리즘 SEED ARIA 1999년 2월 국내에서 개발한 128비트 블록암호알고리즘 국내 표준, ISO/IEC 국제 표준 SEED128, SEED256 ARIA 경량 환경, 하드웨어 구현 최적화된 128비트 블록암호알고리즘 128/192/256비트의 키길이, 12/14/16 라운드
DES, AES DES AES
암호 알고리즘의 키길이와 안전성
블록암호의 운영모드 전자 코드북(electronic codebook, ECB) 모드 암호 블록 체인 (cipher-block chaining, CBC) 모드 암호 피드백(cipher feedback, CFB) 모드 출력 피드백(output feedback, OFB) 모드 카운터(Counter, CTR) 모드
ECB (Electronic Codebook) 모드 - 가장 단순한 모드로 블록단위로 순차적으로 암호화 하는 구조이다. - 한 개의 블록만 해독되면 나머지 블록도 해독이 되는 단점이 있다. - 각 블록이 독립적으로 동작하므로 한 블록에서 에러가 난다고 해도 다른 블록에 영향을 주지 않는다.
CBC (Cipher Block Chaining) 모드 - 블록 암호화 운영 모드 중 보안성이 제일 높은 암호화 방법으로 가장 많이 사용된다. - 평문의 각 블록은 XOR연산을 통해 이전 암호문과 연산되고 첫번째 암호문에 대해서는 IV(Initial Vector)가 암호문 대신 사용된다. 이 때, IV는 제 2의 키가 될 수 있다. - 암호화가 병렬처리가 아닌 순차적으로 수행되어야 한다.
CFB (Cipher Feedback) 모드 - 블록 암호화를 스트림 암호화처럼 구성해 평문과 암호문의 길이가 같다(패딩이 필요 없다) - 최초의 키생성 버퍼로 IV가 사용되며, 이때 IV는 제2의 키가 될수 있다. - 스트림의 기본단위를 Bit단위로 설정할 수 있으며, Bit단위에 따라 CFB8~CFB128로 쓰인다. - 암호화, 복호화 모두 암호화로만 처리할 수 있다. - CBC모드와 마찬가지로 암호화는 순차적이고, 복호화는 병렬적으로 처리할 수 있다.
OFB (Output Feedback) 모드 - 블록 암호화를 스트림 암호화처럼 구성해 평문과 암호문의 길이가 같다.(패딩이 필요없다) - 암호화 함수는 키 생성에만 사용되며, 암호화 방법과 복호화 방법이 동일해 암호문을 한번 더 암호화하면 평문이 나온다. (복호화시에 암호화) - 최초의 키생성 버퍼로 IV가 사용되며, 이 때 IV는 제2의 키가 될수 있다. - 스트림의 기본 단위를 Bit단위로 설정할 수 있으며, Bit단위에 따라 OFB8~OFB128로 쓰인다.
CTR (Counter) 모드 - 블록을 암호화할 때마다 1씩 증가해 가는 카운터를 암호화 해서 키스트림을 만든다. 즉 카운터를 암호화한 비트열과 평문블록과의 XOR를 취한 결과가 암호문 블록이 된다. - CTR모드는 OFB와 같은 스트림 암호의 일종이다. - CTR모드의 암/복호화는 완전히 같은 구조가 되므로 구현이 간단하다.(OFB와 같은 스트림 암호의 특징) - CTR모드에서는 블록의 순서를 임의로 암/복호화 할 수 있다.(블록번호로부터 카운터를 구할 수 있음) - 블록을 임의의 순서로 처리할 수 있다는 것은 처리를 병행할 수 있다는 것을 의미한다.(병렬처리 가능)
암호화와 인증을 결합한 블록암호 운영모드 Authenticated Encryption 암호화와 인증을 함께 제공 Encrypt-then-MAC (EtM) Encrypt-and-MAC (E&M) MAC-then-Encrypt (MtE) Encrypt-then-MAC (EtM) Encrypt-and-MAC (E&M) MAC-then-Encrypt (MtE)
암호화와 인증을 결합한 블록암호 운영모드 CCM (Counter with CBC-MAC) 암호화와 메시지 인증 기능을 동시에 제공하는 운영 모드 Authenticated Encryption: authenticate-then-encrypt 메시지 인증을 위해 CBC-MAC 모드를 사용하고, 메시지 암호화 를 위하여 CTR 모드를 사용 GCM (Galois/Counter Mode) CTR 모드와 GHASH 를 함께 사용하는 운영 모드 Authenticated Encryption 병렬처리 가능하여 효율성 높음
암호화와 인증을 결합한 블록암호 운영모드 GCM (Galois/Counter Mode)
패딩 (padding, 채우기) 블록암호는 정해진 길이의 블록단위로 암호화 수행 패딩된 데이터에서 원래 데이터를 복구할 수 있어야 함
2. 키생성 비밀키 생성 Key Spec 변환 비밀키를 파일로 저장하기/읽어오기
비밀키의 생성 KeyGenerator(javax.crypto.KeyGenerator) 클래스 이용 객체 생성: KeyGenerator kg = KeyGenerator.getInstance("AES"); 키생성 방법 지정: keyGenerator.init(128); 키 생성: SecretKey key = keyGenerator.generateKey(); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); SecretKey key = keyGenerator.generateKey();
비밀키의 생성 비밀키는 알고리즘에 따라 키 길이가 다르다. 자바에서는 사용자가 키 길이를 별도로 제시하지 않아 도 선택한 알고리즘에 필요한 비밀키를 생성하여 준다. 필요하면 특정한 알고리즘을 사용하여 키를 생성하도록 설정할 수도 있다. (init 메소드 이용) init(int keySize) init(SecureRandom random) init(int keySize, SecureRandom random)
키생성 알고리즘
KeySpec 변환 KeySpec 변환이 필요한 경우 javax.crypto.spec.SecretKeySpec 엔진을 사용 파일로 저장된 키를 불러와서 사용하는 경우 (비밀키를 파일로 저장할 경우에는 byte array 형태로 저장) 난수를 생성하여 비밀키로 사용하려고 하는 경우 javax.crypto.spec.SecretKeySpec 엔진을 사용 public SecretKeySpec(byte[] key, String algorithm) 주어진 byte array로부터 주어진 알고리즘에 맞는 비밀키를 구성 함. SecureRandom random = new SecureRandom(); byte[] keyData = new byte[16]; random.nextBytes(keyData); SecretKey secretKey = new SecretKeySpec(keyData, "AES");
비밀키를 파일로 저장하기/읽어오기 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyData = secretKey.getEncoded(); System.out.println("Algorithm : " + secretKey.getAlgorithm()); System.out.println("Format : " + secretKey.getFormat()); File keyFile = new File("secretKey.raw"); OutputStream out = new BufferedOutputStream(new FileOutputStream(keyFile)); try { out.write(keyData); } finally { out.close(); }
3. Cipher 클래스를 이용한 암호화/복호화 이 엔진을 사용하기 위해서는 먼저 사용할 알고리즘을 지정하여 야 한다. 이 때 알고리즘 이름 뿐만 아니라 암호화 모드, 채우기 방식을 함 께 지정할 수 있다. 암호화 모드와 채우기 방식을 지정하지 않으면 제공자에 의해 결 정된 모드와 채우기 방식으로 암호화가 이루어진다. Cipher DESCipher = Cipher.getInstance("DES"); Cipher DESCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
Cipher 엔진 객체의 초기화 Cipher 엔진의 동작모드 (opmode) public void init(int opmode, Key key) Cipher 엔진의 동작모드 (opmode) Cipher.ENCRYPT_MODE: 암호화 연산을 사용하고자 할 때 Cipher.DECRYPT_MODE: 복호화 연산을 사용하고자 할 때
채우기 (padding) 채우기 블록 암호 방식에서는 마지막 블록을 완전 블록으로 만들기 위해 채우기가 필요하다. 블록 암호 방식에서는 마지막 블록을 완전 블록으로 만들기 위해 채우기가 필요하다. JCE는 4가지 채우기 방법을 제공 none: 사용자가 완전 블록임을 보장해 주어야 한다. PKCS5Padding SSL3Padding (예약만 되어 있고, 실제 구현되어 있지 않음) OAEPWith<digest>And<mgf>Padding PKCS5Padding
채우기 (padding)
암호화 모드 (Mode of Operation)
암호화 방법1. doFinal() 메소드만 호출 암호화하고자 하는 평문의 크기가 작을 경우 방법2.일련의 update() 메소드를 호출한 후에 doFinal() 메소드 호출 암호화하고자 하는 평문의 크기가 크거나 나누어져 있을 경우 String plaintext = "This is a secret message!"; byte[] ciphertext = cipher.doFinal(plaintext.getBytes()); byte[] ciphertext = new byte[cipher.getOutputSize(input.length)]; int ctLength = cipher.update(myByteArray01, 0, myByteArray01.length, ciphertext, 0); ctLength += cipher.update(myByteArray02, 0, myByteArray02.length, ciphertext, ctLength); ctLength += cipher.doFinal(ciphertext, ctLength);
DES 암호화 예제 SymEncryption.java try{ KeyGenerator kg = KeyGenerator.getInstance("DES"); SecretKey key = kg.generateKey(); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "DES"); Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); String plaintext = "This is a secret message!"; byte[] ciphertext = cipher.doFinal(plaintext.getBytes()); for(int i=0; i<ciphertext.length; i++){ System.out.printf("%02X ", ciphertext[i]); } System.out.println(); cipher.init(Cipher.DECRYPT_MODE,keySpec); byte[] cleartext = cipher.doFinal(ciphertext); for(int i=0; i<cleartext.length; i++){ System.out.print((char)cleartext[i]); catch(){
AES 암호화 예제 AESTest.java AES 암호화 테스트 비밀키: 2b37e20db0df7bdee53fb26a846d7677 비밀키 길이: 128 bits 평문: 오늘도 별이 바람에 스치운다. 암호문: cb00d7e6d3dfc950896ea1aefbb1f1edfb9eac43da4cdc1790 135c09c31cd9343e15ba6a720394b033b8114de85199e4 복호문 :오늘도 별이 바람에 스치운다.
AES/CBC모드 예제 AESCBCTest.java 초기벡터 IV 생성 필요 // IV 생성 SecureRandom random = new SecureRandom(); byte[] ivData = new byte[16]; // 128 bit random.nextBytes(ivData); IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData); Charset charset = Charset.forName("UTF-8"); public static byte[] encrypt(SecretKey secretKey, IvParameterSpec ivParameterSpec, byte[] plainData) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec); byte[] encryptData = cipher.doFinal(plainData); return encryptData; }
4. 파일 암호화/복호화 대량의 데이터를 암/복호화하는 경우 CipherStream 클 래스를 이용 평문 파일의 암호화 평문 파일 입력: BufferedInputStream 이용 암호화 파일 출력: CipherOutputStream 이용 암호화 파일의 복호화 암호문 파일 입력: CipherInputStream 이용 복호화 평문 파일 출력: BufferedOutputStream 이용 예제: CipherStreamExample.java
파일 암호화/복호화 프로젝트 폴더에 암호화할 파일 준비 // 암호화할 텍스트 파일 File plainFile = new File("plain.txt"); // 암호화하여 저장할 파일 File encryptFile = new File("encrypt.txt"); // 복호화하여 저장할 파일 File decryptFile = new File("decrypt.txt");
파일 암호화/복호화 파일 암호화 try { input = new BufferedInputStream(new FileInputStream(plainFile)); output = new CipherOutputStream(new BufferedOutputStream(new FileOutputStream(encryptFile)), cipher); int read = 0; byte[] buffer = new byte[1024]; while ((read = input.read(buffer)) != -1) { output.write(buffer, 0, read); } } finally { if (output != null) try {output.close();} catch(IOException ie) {} if (input != null) try {input.close();} catch(IOException ie) {}
파일 암호화/복호화 파일 복호화 try { input = new CipherInputStream(new BufferedInputStream(new FileInputStream(encryptFile)), cipher); output = new BufferedOutputStream(new FileOutputStream(decryptFile)); int read = 0; byte[] buffer = new byte[1024]; while ((read = input.read(buffer)) != -1) { output.write(buffer, 0, read); } } finally { if (output != null) try {output.close();} catch(IOException ie) {} if (input != null) try {input.close();} catch(IOException ie) {}
5. 비밀키를 파일로 저장하기/읽어오기 비밀키를 파일로 저장, 파일에서 읽어오기 // 비밀키 생성 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyData = secretKey.getEncoded(); // 비밀키를 파일로 저장 String keyFile = "secretKey.raw"; FileOutputStream fos = new FileOutputStream(keyFile); fos.write(keyData); fos.close(); // 비밀키를 파일에서 읽어오기 FileInputStream fis = new FileInputStream(keyFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int curByte = 0; while((curByte = fis.read())!=-1){ baos.write(curByte); }
비밀키를 파일로 저장하기/읽어오기 예제: AESKeySave.java AES 암호화 테스트 - 비밀키 파일 저장 송신자 - 비밀키 생성, 암호화, 비밀키 저장, 비밀키 파일 전송, 암호문 전송 비밀키: a09e81375fd4f8141e24869e62be6429 비밀키 길이: 16 byte 평문: 오늘도 별이 바람에 스치운다. 암호문: ce8f6ac4c37543696907e9dd684435a0ef79a3987deea5e67dbf01b55124e340dcc360c6eb8a7adf513e995eaddf0fed 비밀키 파일저장 완료 수신자 - 암호문 수신, 비밀키 파일 수신, 복호화 비밀키를 파일에서 읽어옴 읽어온 비밀키: a09e81375fd4f8141e24869e62be6429 복호문 :오늘도 별이 바람에 스치운다.
The puzzle: given L, find R 실습과제 #3. 작업증명과 블록체인 작업증명(Proof of Work)이란? 대표적인 암호화폐인 비트코인에서는 작업증명을 이용하여 코인 을 생성함. 퀴즈를 푼 사람에게 대가로 코인을 제공. SolvePuzzle(L){ repeat{ R = my_name || i++ T = H(L,R) }while(T ≠ 0d) return R } L ∈ {0,1}* R ∈ {0,1}* H (a random function) T ∈ {0,1}d The puzzle: given L, find R such that T=0d
H H H 실습과제 #3. 작업증명과 블록체인 블록체인(Blockchain)이란? 앞의 작업증명 결과를 퀴즈의 입력으로 하여 이러한 작업증명을 계속 진행함. 체인을 구성하여 변조하기 어렵도록 함. x0 = Start! x1 =(P1, i1) x2=(P2, i1) x3=(P3, i3) H H H 000…000 000…000 000…000
실습과제 #3. 작업증명과 블록체인 문제 1. 작업증명 문제 2. 블록체인 해쉬함수 SHA-256을 이용하여 출력값의 0-bit의 길이 d를 10~30로 변경하면서 실제 작업증명을 수행하는 시간을 측정하 고 통계를 내보시오. 이러한 실험을 기반으로 작업증명의 수행시 간이 10분정도 시간에 가능한 수준의 d값을 결정하시오. 문제 2. 블록체인 위의 작업증명을 체인으로 연결하여 블록체인을 구성해 보시오.