대칭키 암호화

UP!/Crypto 2015. 11. 5. 10:55

     키의 저장

파일 시스템 안에 키를 저장할 경우 이에 대한 보안이 필요

가)    키 래핑(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”);

 

     사이퍼 스트림(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();

 


     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개의 기능을 포함

     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();

       }

 

     봉인 객체

가)    사이퍼 또는 키로 객체를 암호화할 수 있는 방법을 제공

나)    암호화 객체 또는 봉인 객체라 불림

다)   

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
Posted by 으랏차
,