1 키의 저장
파일 시스템 안에 키를 저장할 경우 이에 대한 보안이 필요
가) 키 래핑(Wrapping)과 언래핑(Unwrapping)
① wrap() 함수 : Cipher 클래스에 있는 함수로 키를 매개변수로 받아서 암호화된 키의 바이트 배열을 반환
cipher.init(Cipher.WRAP_MODE, passwordKey, paramSpec); byte[] encryptedKeyBytes = cipher.wrap(secretKey); |
② unwrap() : 암호화된 키의 바이트 배열을 Key로 반환
cipher.init(Cipher.UNWRAP_MODE, passwordKey, paramSpec); Key key = cipher.unwrap(encryptedKeyBytes, “Blowfish”, Cipher.SECRET_KEY); |
나) 래핑을 하지 않는 키 암호화
① wrap()과 unwrap()이 없다면 Key.getEncoded(), SecretKeySpec을 이용해 암호화
② java.security.Key : 암호화된 키의 바이트 배열을 반환
byte[] keyBytes = myKey.getBytes(); cipher.init(Cipher.ENCRYPT_MODE, passwordKey, paramSpec); byte[] encryptedKeyBytes = cipher.doFinal(keyBytes); |
③ SecretKeySpec : 키를 복호화
cipher.init(Cipher.DECRYPT_MODE, passwordKey, paramSpec); byte[] keyBytes = cipher.doFinal(encryptedKeyBytes); SecretKeySpec myKey = new SecretKeySpec(keyBytes, “Blowfish”); |
2 사이퍼 스트림(CipherStream)
가) 표준 입/출력 스트림을 암/복호화를 위한 래퍼 클래스를 제공
① CipherInputStream
② CipherOutputStream
나) 예제
FileInputStream input = new FileInputStream(“plaintext_filename”); FileOutputStream output = new FileOutputStream(“ciphertext_filename”); CipherOutputStram cipherOutput = new CipherOutputStream(output, cipher); int r = 0; while((r=input.read()) != null) cipherOutput.write(r); cipherOutput.close(); output.close(); input.close(); |
3 IV로 사이퍼 초기화
초기화 백터의 바이트를 생성하기 위해 java.security.SecureRandom을 이용
가) SecureRandom
① 예
byte[] randomBytes = new byte[8]; SecureRandom random = new SecureRandom(); random.nextBytes(randomBytes); |
나) IV 생성과 사용
① 생성
IVParameterSpec iv = new IVParameterSpec(randomBytes); |
② 사용
Cipher cipher = Cipher.getInstance(“Blowfish/CBC/PCS5PADDing”); cipher.init(Cipher.ENCRYPT_MODE, key, iv); |
다) IV 추적
① 복호화 할 때에도 동일한 IV가 사용되어야 함
② salt와 매우 비슷하며 숨길 필요 없이 암호문에 붙여서 사용함
4 사이퍼 스트림 예제 – 파일 인코더
가) 사이퍼 스트림을 이용해 파일을 암/복호화
나) 키는 패스워드 기반 암호화로 파일에 저장 및 복호화
다) 4개의 기능을 포함
① createKey() : AES 키를 생성 하고 패스워드를 이용하여 암호화하여 파일에 저장
/** * 1. 256bit AES(Rijndael)키를 생성 * 2. PBE를 이용하여 생성한 키를 암호화하여 파일에 저장 */ private static void createKey(char[] password) { System.out.println("Generating a RijnDael key..."); // AES 대칭 키 생성기 객체 생성 KeyGenerator keyGenerator = null; try { keyGenerator = KeyGenerator.getInstance("Rijndael"); } catch (NoSuchAlgorithmException e) {} // AES 키 생성 keyGenerator.init(256); Key key = keyGenerator.generateKey(); System.out.println("Done generating the key");
// PBE 시작 // salt 생성 byte[] salt = new byte[8]; SecureRandom random = new SecureRandom(); random.nextBytes(salt);
// password로 PBEKeySpec 생성 PBEKeySpec pbeKeySpec = new PBEKeySpec(password); SecretKeyFactory keyFactory = null;; SecretKey pbeKey = null; try { keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); pbeKey = keyFactory.generateSecret(pbeKeySpec); } catch (NoSuchAlgorithmException e) {} catch (InvalidKeySpecException e) {} // PBEParameterSpec 생성 PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, ITERATIONS); // Cipher 생성 및 초기화 Cipher cipher = null; byte[] encryptedKeyBytes = null; try { cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); cipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
// AES키 암호화 encryptedKeyBytes = cipher.doFinal(key.getEncoded()); } catch (NoSuchAlgorithmException e) {} catch (NoSuchPaddingException e) {} catch (InvalidKeyException e) {} catch (InvalidAlgorithmParameterException e) {} catch (IllegalBlockSizeException e) {} catch (BadPaddingException e) {}
FileOutputStream fos; try { fos = new FileOutputStream(KEY_FILENAME); //salt와 암호화된 키 바이트를 쓴다. fos.write(salt); fos.write(encryptedKeyBytes); fos.close(); } catch (FileNotFoundException e) {} catch (IOException e) {} } |
② loadKey() : 키가 저장된 파일로부터 읽어들여 복호화하여 키를 생성
private static Key loadKey(char[] password) throws Exception { // 파일로부터 읽어들임 FileInputStream fis = new FileInputStream(KEY_FILENAME); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int i = 0; while((i=fis.read()) != -1) baos.write(i); fis.close(); byte[] saltAndKeyBytes = baos.toByteArray(); baos.close(); // salt를 분리함. Base64 인코딩을 하지 않았기에 8byte 그대로 유지됨 byte[] salt = new byte[8]; System.arraycopy(saltAndKeyBytes, 0, salt, 0, 8);
// key를 분리함 int length = saltAndKeyBytes.length - 8; byte[] encryptedKeyBytes = new byte[length]; System.arraycopy(saltAndKeyBytes, 8, encryptedKeyBytes, 0, length);
// PBE 시작 PBEKeySpec pbeKeySpec = new PBEKeySpec(password); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC"); SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec); PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, ITERATIONS); // Cipher 생성 및 초기화 Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC"); cipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec); // 키 복호화 byte[] decryptedKeyBytes = cipher.doFinal(encryptedKeyBytes); SecretKeySpec key = new SecretKeySpec(decryptedKeyBytes, "Rijndael");
return key; } |
③ encrypt() : 사이퍼 스트림을 이용하여 암호화 수행
private static void encrypt(char[] passowrd, String fileInput, String fileOutput) throws Exception { // key 로드 System.out.println("Loading the key."); Key key = loadKey(passowrd); System.out.println("Loaded the key.");
// 사이퍼 생성 Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding"); System.out.println("Initializing SecureRandom..."); // SecureRandom 생성하여 IV 초기화 SecureRandom random = new SecureRandom(); byte[] iv = new byte[16]; random.nextBytes(iv); // 입력 파일과 출력 파일의 stream을 연다 FileInputStream fis = new FileInputStream(fileInput); FileOutputStream fos = new FileOutputStream(fileOutput);
// 먼저 출력파일에 iv를 쓴다. fos.write(iv); // IvParameterSpec를 생성하고 사이퍼를 생성 및 초기화한다. IvParameterSpec spec = new IvParameterSpec(iv); System.out.println("Initializing the cipher..."); cipher.init(Cipher.ENCRYPT_MODE, key, spec); // 출력 스트림과 사이퍼를 인자로 사이퍼 스트림을 생성한다. CipherOutputStream cos = new CipherOutputStream(fos, cipher);
System.out.println("Encrypting the file"); // 입력 파일로부터 읽어들여 사이퍼 스트림에다 쓴다. int theByte = 0; while((theByte = fis.read()) != -1) cos.write(theByte); // 스트림을 모두 닫아준다. fis.close(); cos.close(); } |
④ decrypt() : 사이퍼 스트림을 이용하여 복호화 수행
private static void decrypt(char[] password, String fileInput, String fileOutput) throws Exception { // key 로드 System.out.println("Loading the key"); Key key = loadKey(password); System.out.println("Loaded the key."); // 사이퍼 생성 Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding"); // 입력 파일과 출력 파일의 stream을 연다 FileInputStream fis = new FileInputStream(fileInput); FileOutputStream fos = new FileOutputStream(fileOutput); // 입력파일에서 초기화 벡터 16byte를 읽어들인다. // Base64인코딩을 하지 않았으므로 길이는 그대로다. byte[] iv = new byte[16]; fis.read(iv); // IvParameterSpec 생성 및 사이퍼 초기화 IvParameterSpec spec = new IvParameterSpec(iv); System.out.println("Initializing the cipher..."); cipher.init(Cipher.DECRYPT_MODE, key, spec); // 입력파일과 사이퍼를 인자로 사이퍼 스트림을 생성한다. CipherInputStream cis = new CipherInputStream(fis, cipher); System.out.println("Decrypting the file..."); // 입력파일로 부터 읽어들여 출력파일에 쓴다. int theByte = 0; while((theByte = cis.read()) != -1) fos.write(theByte); // 스트림을 모두 닫아준다. cis.close(); fos.close(); } |
5 봉인 객체
가) 사이퍼 또는 키로 객체를 암호화할 수 있는 방법을 제공
나) 암호화 객체 또는 봉인 객체라 불림
다) 예
import java.io.IOException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SealedObject;
public class SealedObjectExample {
public static void main(String[] args) { String creatiCard = "1234567890";
try { KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
Key key = keyGenerator.generateKey(); Cipher cipher = Cipher.getInstance("DESede"); cipher.init(Cipher.ENCRYPT_MODE, key);
System.out.println("Encrypting the Object"); SealedObject so = new SealedObject(creatiCard, cipher); System.out.println("Decrypting the Object"); //String decryptedCrediteCard = (String) so.getObject(key);
cipher.init(Cipher.DECRYPT_MODE, key); String decryptedCrediteCard = (String) so.getObject(cipher); System.out.println("Credit card Number : " + decryptedCrediteCard);
} catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchPaddingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvalidKeyException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalBlockSizeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadPaddingException e) { // TODO Auto-generated catch block e.printStackTrace(); }
}
} |
'UP! > Crypto' 카테고리의 다른 글
MessageDigest (0) | 2015.11.05 |
---|---|
Session key (0) | 2015.11.05 |
패스워드 기반 암호화 (0) | 2015.11.05 |
JCA, JCE (0) | 2015.11.05 |
인증, 전자서명 (0) | 2015.11.05 |