IT

인증(Authentication)

 

메시지 다이제스트(Message Digest)

     기능

가)    해쉬 라고도 함

나)    메시지가 부당하게 변경되지 않았다는 것을 증명

다)    한 방향 함수

라)    패스워드 인증에도 사용됨

     방법

가)    파일과 다이제스트를 받음

나)    파일에 대해 메시지 다이제스트를 적용한 결과를 다이제스트와 비교함

     종류

가)    MD5

     RSA에서 개발

     128bit

나)    SHA, SHA-1

     Secure Hash Algorithm

     일반적으로 SHA SHA-1을 말함

     160bit

 

메시지 인증 코드(MAC:Message Authentication codes)

     기능

가)    키와 함께 생성된 메시지 다이제스트

나)    ·수신 측 모두 비밀 키가 있어야 함

다)    동일한 메시지와 키는 언제나 동일한 메시지 인증 코드를 가짐

     사용

가)    보안 연결이 이미 설치된 곳에 보통 사용됨

나)    SSL은 받은 데이터를 검사하기 위해 세션을 시작할 때 교환한 비밀 키를 가지고 MAC을 사용함.

     종류

가)    HMAC(Hasing Message Authentication Codes)

     MD5 SHA-1과 같은 해시 알고리즘을 사용함

나)    HmacMD5

     키와 함께 MD5 다이제트스 알고리즘 사용

다)    HmacSHA1

     SHA-1 알고리즘 사용

 

전자서명

     기능

가)    데이터 원형에 대한 보증

나)    데이터가 변조 방지 보증

     방법

가)    서명해야 할 메시지에 대해 메시지 다이제스트 생성

나)    생성된 메시지 다이제스트를 송신자의 개인키로 암호화

다)    수신자는 전달받은 메시지에 대해 메시지 다이제스트 생성

라)    전자서명을 송신자의 공개키로 복호화한 후 다)에서 생성한 다이제스트와 비교

 

전자 인증서(Digital Certificates)

     문제점

가)    인증에 공개 키를 사용하는 것이 문제

나)    공개 키가 어떤 사람의 것인지 알 수가 없음

다)    전자서명은 수진자의 공개 키를 이용해서 서명해야 함

     기능

가)    승인된 기관의 개인 키로 서명된 개인의 공개 키와 신원을 확인하는 정보가 포함

나)    승인된 기관의 공개 키로 검증

다)    SSL 인증서는 URL을 포함하고 있어 접속 시 URL이 틀릴 경우 에러 메시지 발생

Posted by sinpk

Padding

데이터를 블럭으로 암호화 할때 평문이 항상 블럭 크기(일반적으로 64비트 또는 128비트)의 배수가 되지 않는다. 패딩은 어떻게 평문의 마지막 블록이 암호화 되기 전에 데이터로 채워지는가를 확실히 지정하는 방법이다. 복호화 과정에서는 패딩을 제거하고 평문의 실제 길이를 지정하게 된다.

 

PKCS#5
PKCS#5는 공개키 암호화의 표준이며, RSA 회사에 의해 발표되 독립적으로 규정된 표준이다.
패딩기법은 블럭의 나머지 부분을 바이트(정확히 otect) 수로 채운다. 예를 들어 128비트의 블록에서, 만약 블럭에 남는 다섯 개의 남은 평문 바이트가 있다면, 11의 값을 가진 11 바이트가 블록에 패딩된다. 블럭에 채워진 데이터는 16바이트(128비트)로 나누어지게 된다. 이 방법은 패딩이 복호화 뒤에 명백하게 제거되도록 하고, 평문의 원래 크기가 다시 저장 되도록 한다. 추가적으로 64비트의 블럭인 경우는 다섯개의 남은 평문이 있다면, 3의 값을 가진 3바이트가 블럭에 패딩된다. 블럭에 채워진 데이터는 8바이트(64비트)로 나누어지게 된다.
만약 평문이 블럭(64 또는 128비트 외 기타)으로 나누어지면, 패딩을 추가하지 않아도 되는 것 처럼 보이지만, 암호화를 수행 할때는 패딩이 항상 추가 된다.

 

 


Mode

암호 모드는 평문의 블럭이 암호문의 블럭으로 암호화되는 방법을 결정하며, 그 역 또한 성립한다.
아래는 내용은 DES 암호 알고리즘의 이용하여 64비트(8바이트) 블럭암호를 했을 때 나타 낸것이다. 만약 128비트 암호(16바이트)를 사용 한다면 크기만 변경해 주면된다.

 

ECB Mode (Electronic Code Book)
가장 일반적인 형태이며 암호문 각각의 8바이트 블럭이 독립적이라는 것이다. 만약 암호문 한 블럭 에러가 발생했다면 에러가 발생한 블럭을 제외한 나머지 암호문 블럭은 복호화 할 수 있다. 아래그림에서 보면 만약 Block C2가 에러가 났더라도 Block C1과 그 외 Block Cn은 복호화 할 수 있다는 것이다. 단점은 보통 정형화 된 형식의 평문(인사말, 전송되는 데이터의 형식의 헤더 정보 등...) 또는 반복되는 평문이 있을 경우 암호문에서 반복되는 형식이 출력된다. 이것은 악의적이 누군가에 정보를 추측 할 수 있도록 하게 해준다. 

 

CBC Mode (Cipher Block Chaining)
평문의 첫번째 블럭(P1)이 초기화 벡터(IV: Initalization Vector)와 XOR을 수행하하고 나서 암호화를 수행하게 된다. 암호문(C1)은 평문의 다음 블럭(P2)의 XOR를 수행하게 되며 이것이 반복된다. 그리고 마지막 블럭에서는 패딩이 추가되어져서 수행된다. 복호화는 역으로 수행하면 된다. CBC 특징은 현재 생성된 암호문 블럭이 다음 생성된 암호문 브럭에 영향을 미치기 때문에 특정 암호문 블럭에 에러가 발생하면 그 다음 암호문에도 그 영향을 받게 된다. IV는 송신자가 값을 가지고 암호화를 수행하면 복호화하는 수신자도 IV값을 알고 있어야한다. 깨진 암호문의 해당블록과 다음블록의 평문까지 영향을 받는다.

문서를 전송하는데 적합하지만, 한번에 모든 블록을 전송해야 한다.(보통 8캐릭터). 전제 메시지에는 좋지만, 한번에 한 글자씩 보내는 방식에는 적합하지 않다.



 

PCBC Mode (Propagating Cipher Block Chaining)
PCBC는 CBC 모드와 유사하다. 아래 그림에서 보면 평문 블럭(P2)이 암호화 할 때, 이전의 평문 블럭(P1)과 암호문 블럭(C1)이 동시에 XOR 연산을 수행하고 암호화를 수행하게 된다. 그리고 마지막 블럭에서는 패딩이 추가되어져서 수행된다. 복호화는 역으로 수행하면 된다.


 

CFB Mode (Cipher FeedBack)
CFB는 블록암호가 스트림 암호처럼 한 바이트(8비트)를 암호화 하거나 복호화하는데 CFB8이라고 한다.

스트림 암호화처럼 동작하기 때문에 평문과 암호문의 길이가 같아져 패딩이 필요 없다.

스트림의 기본단위를 Bit단위로 설정할 수 있으며 Bit 단위에 따라 CFB8~CFB128로 쓰인다.

한번에 한 바이트 데이타를 암호화 해서 보내는 방식에 적합하다.
다음은 아래 그림처럼 작동 방법을 설명하고 있다.
Step 1. 8bytes 초기화 벡터(IV)에 값을 채우고, IV값을 암호화를 수행하여 Shift register값을 출력한다.
Step 2. 평문의 첫 8비트(Byte P1)와 shift register의 최좌단 8비트와 XOR를 수행하여 첫번째 암호문(Byte C1)을 출력한다.
Step 3. Shift register값을 왼쪽으로 8비트 이동시키고, 암호문의 8비트를 shift register에 값의 오른쪽에 채운다.
Step 4. shift register는 암호화를 수행한다.
Step 5. 평문의 다음 8비트(Byte P2)와 shift register의 최좌단 8비트를 XOR를 수행하여 다음 암호문(Byte C2)을 출력한다.
Step 6. 모든 평문의 길이만큼 Step 3~6까지 반복 수행하여 처리한다.

 

 

또 다른 그림

 

OFB Mode (Output FeedBack)
OFB는 암호문이 shift register값에 피드백 되지 않는 것을 제외하고는 CFB와 유사하다. shift register 출력 값 그 자체가 피드백 된다. 이것은 평문도 암호문도 암호화 되지 않는다. 단지 암호문은 shift register의 출력 값과 평문이 XOR 수행하여 출력 된다는 것이다.

스트림 암호화처럼 동작하기 때문에 평문과 암호문의 길이가 같아져 패딩이 필요 없다.

스트림의 기본단위를 Bit단위로 설정할 수 있으며 Bit 단위에 따라 CFB8~CFB128로 쓰인다.

 


또 다른 그림

 

 

 CRT Mode (CounTeR)
블록을 암호화할 때마다 1씩 증가해 가는 카운터를 암호화 해서 키 스트림을 만든다. 즉 카운터를 암호화환 비트열과 평문블록과의 XOR을 취한 결과가 암호문 블록이 된다. CRT는 OFB와 같은 스트림 암호의 일종이며 암복호화는 완전히 같은 구조를 가지므로 구현이 간단하다. 비표와 블록번호로부터 카운터를 구할수 있기 때문에 블록의 순서를 임의로 암/복호화 할 수 있다.

 

 

Posted by sinpk

RSA 파일 암호화

 

     RSA 키 쌍 생성

 

     공개 키로 파일을 암호화

가)    RSA 키의 인코딩

     X.509를 사용하여 인코딩 사용(공개 키와 인증서를 전달할 때 사용하는 공개 형식)

     개인 키는 PKCS#8로 인코딩

     공개/개인 키든 getEncoded()를 호출하면 자동으로 인코딩 됨

나)    RSA 키의 디코딩

     인코딩된 키의 스펙 객체를 생성

     KeyFactory를 사용하여 적절한 키 타입의 인스턴스를 활성화

     공개 키 디코딩

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

KeyFactory keyFactory = KeyFactory.getInstance(“RSA”);

PublicKey publicKey = keyFactory.generatePublic(keySpec);

 

     개인 키의 디코딩

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

KeyFactory keyFactory = KeyFactory.getInstance(“RSA”);

PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

 

다)    암호화된 파일 형식

     포맷

키의 길이

암호화된 키

초기화백터(16바이트)

암호문서

 

라)    예제

private static void createKey(String password) throws Exception {

             // RSA 생성

             KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

             keyPairGenerator.initialize(1024);

             KeyPair keyPair = keyPairGenerator.genKeyPair();

            

             //공개키를 파일에 쓴다.

             byte[] publicKeyBytes = keyPair.getPublic().getEncoded();

             FileOutputStream fos = new FileOutputStream("c:\\publicKey");

             fos.write(publicKeyBytes);

             fos.close();

            

             // 개인 키를 암호화한 후에 파일에 쓴다.

             byte[] privateKeyBytes = passwordEncrypt(password.toCharArray(), keyPair.getPrivate().getEncoded());

             fos = new FileOutputStream("c:\\privateKey");

             fos.write(publicKeyBytes);

             fos.close();

       }

      

       private static byte[] passwordEncrypt(char[] password, byte[] plaintext) throws Exception {

             // salt 생성

             byte[] salt = new byte[9];

             Random random = new Random();

             random.nextBytes(salt);

            

             // PBE 키와 사이퍼 생성

             PBEKeySpec keySpec = new PBEKeySpec(password);

             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC");

             SecretKey key = keyFactory.generateSecret(keySpec);

             PBEParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);

            

             Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC");

             cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);

            

             byte[] cipherText = cipher.doFinal(plaintext);

            

             ByteArrayOutputStream baos = new ByteArrayOutputStream();

             baos.write(salt);

             baos.write(cipherText);

            

            

             return baos.toByteArray();

       }

      

       private static void encrypt(String fileInput) throws Exception {

             String publicKeyFileName = "c:\\publicKey";

             // 공개 키가 저장된 파일로부터 keyByte 바이트 배열을 생성한다.

             FileInputStream fis = new FileInputStream(publicKeyFileName);

             ByteArrayOutputStream baos = new ByteArrayOutputStream();

            

             int theByte = 0;

             while((theByte = fis.read()) != -1)

                    baos.writeTo(baos);

             fis.close();

            

             byte[] keyBytes = baos.toByteArray();

             baos.close();

            

             // 인코딩된 키를 RSA 공개 키의 인스턴스로 바꾼다.

             X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

             KeyFactory keyFactory = KeyFactory.getInstance("RSA");

             PublicKey publicKey = keyFactory.generatePublic(keySpec);

            

             String fileOutput = fileInput + ENCRYPTED_FILENAME_SUFFIX;

             DataOutputStream output = new DataOutputStream(new FileOutputStream(fileOutput));

            

             // RSA 공개 키를 이용하여 세션 키를 암호화할 사이퍼를 생성한다.

             Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

             rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);

            

             // 세션 생성

             KeyGenerator rijndaelKeyGenerator = KeyGenerator.getInstance("Rijndael");

             rijndaelKeyGenerator.init(256);

             Key rijndaelKey = rijndaelKeyGenerator.generateKey();

            

             // RSA 사이퍼를 이용하여 세션 키를 암호화 하고 파일에 저장한다.

             // 키의 길이, 인코딩된 세션 형식이다.

             byte[] encodedKeyBytes = rsaCipher.doFinal(rijndaelKey.getEncoded());

             output.writeInt(encodedKeyBytes.length);

             output.write(encodedKeyBytes);

            

             // 초기화 벡터

             SecureRandom random = new SecureRandom();

             byte[] iv = new byte[16];

             random.nextBytes(iv);

            

             //IV 파일에 쓴다

             output.write(iv);

            

             //IV 생성한 세션 키를 이용하여 파일의 내용을 암호화한다.

             IvParameterSpec spec = new IvParameterSpec(iv);

             Cipher symmetricCipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding");

             symmetricCipher.init(Cipher.ENCRYPT_MODE, rijndaelKey, spec);

             CipherOutputStream cos = new CipherOutputStream(output, symmetricCipher);

            

             FileInputStream input = new FileInputStream(fileInput);

             theByte = 0;

             while((theByte = input.read()) != -1)

                    cos.write(theByte);

            

             input.close();

             cos.close();

             return;

       }

 

     개인 키로 복호화

가)    예제

private static byte[] passwordDecrypt(char[] password, byte[] ciphertext) throws Exception {

             // salt 읽는다. 개인키는 8byte salt 사용했다.

             byte[] salt = new byte[8];

             ByteArrayInputStream bais = new ByteArrayInputStream(ciphertext);

             bais.read(salt, 0 ,8);

            

             byte[] remainingCiphertext = new byte[ciphertext.length-8];

             bais.read(remainingCiphertext, 0, ciphertext.length-8);

            

             //PBE 사이퍼를 생성하여 세션 키를 복원한다.

             PBEKeySpec keySpec = new PBEKeySpec(password);

             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC");

             SecretKey key = keyFactory.generateSecret(keySpec);

             PBEParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);

             Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwofish-CBC");

            

             // 복호화

             cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

             return cipher.doFinal(remainingCiphertext);

       }

      

       private static void decrypt(String password, String fileInput) throws Exception {

             String privateKeyFilename = "c:\\privateKey";

             // 파일로부터 개인 키를 읽어들인다.

             FileInputStream fis = new FileInputStream(privateKeyFilename);

             ByteArrayOutputStream baos = new ByteArrayOutputStream();

            

             int theByte = 0;

             while((theByte = fis.read()) != -1)

                    baos.write(theByte);

             fis.close();

             byte[] keyByte = baos.toByteArray();

             baos.close();

            

              // 암호화된 개인 바이트를 복원한다.

             keyByte = passwordDecrypt(password.toCharArray(), keyByte);

            

             // RSA 개인 키를 복원한다.

             PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyByte);

             KeyFactory keyFactory = KeyFactory.getInstance("RSA");

             PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

            

             // 개인 키를 이용하여 사이퍼를 생성하고 세션 키를 복호화한다

             Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

             DataInputStream dis = new DataInputStream(new FileInputStream(fileInput));

             byte[] encryptedKeyBytes = new byte[dis.readInt()];

             dis.readFully(encryptedKeyBytes);

            

             rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);

             byte[] rijndaelKeyByte = rsaCipher.doFinal(encryptedKeyBytes);

            

             SecretKey rijndaelKey = new SecretKeySpec(rijndaelKeyByte, "Rijndael");

            

             byte[] iv = new byte[16];

             dis.readFully(iv);

             IvParameterSpec spec = new IvParameterSpec(iv);

            

             Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5padding");

             cipher.init(Cipher.DECRYPT_MODE, rijndaelKey, spec);

             CipherInputStream cis = new CipherInputStream(dis, cipher);

            

             FileOutputStream fos = new FileOutputStream(fileInput + DECRYPTED_FILENAME_SUFFIX);

            

             theByte = 0;

             while((theByte = cis.read()) != -1)

                    fos.write(theByte);

            

             cis.close();

             fos.close();

            

             return;     

       }

 

나)    복호화 과정

 

     세션 키 암호화를 사용해야 하는 이유

가)    해킹에 대비해, 개인 키를 저장할 때 암호화할 필요가 있다.

나)    세션 키를 사용하면 속도가 훨씬 빨라진다.

다)    RSA는 특정 메시지에 대해서 매우 취약하다.

Posted by sinpk

     애플리케이션

가)    javax.crypto.Cipher : 암호화하고 복호화하는 기본적인 엔진

     getInstance()

       - Cipher.getInstance(“DESede/ECB/PKCS5Padding”);

       - 알고리즘의 이름을 매개변수로 해서 팩토리 메소드 이용

     Init() : Cipher의 인스턴스를 얻었다면 init()로 초기화해야 함

       - cipher.init(Cipher.ENCRYPTODE, myKey);

       - ENCRYPTODE / DECRYPT_MODE : 작동 방식

       - 비대칭 암호화에서 사용

     update() :

     doFinal() : 암호화된 바이트 배열

       - byte[] ciphertext = cipher.doFinal();

 

나)    javax.crypto.KeyGenerator : 대칭 암호화에 쓸 키를 생성

     getInstance()

       - KeyGenerator.getInstance(“DESede”);

     Init() : KeyGenerator의 인스턴스를 초기화할 때 키의 크기를 결정

       - keyGenerator.init(168);

     generateKey() : Key 객체를 실제로 만들어 냄

       - Key key = keyGenerator.generateKey();

 

다)    간단한 암호화 예제 : DESede

 import java.io.UnsupportedEncodingException;
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;

public class SimpleExample {

 /**
  * @param args
  */
 public static void main(String[] args) {
  System.out.println("Generating a DESede TripleDES key...");
  
  try {
   KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
   keyGenerator.init(168);
   Key key = keyGenerator.generateKey();
   
   System.out.println("Done generating the key.");
   
   Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
   cipher.init(Cipher.ENCRYPT_MODE, key);
   
   byte[] plaintext = "love is love.".getBytes("UTF8");
   
   System.out.println("Plaintext: ");
   for(int i = 0; i < plaintext.length; i++)
    System.out.print(plaintext[i] + " ");
   
   byte[] ciphertext = cipher.doFinal(plaintext);

   System.out.println("\nCiphertext: ");
   for(int i = 0; i < ciphertext.length; i++)
    System.out.print(ciphertext[i] + " ");
   
   cipher.init(Cipher.DECRYPT_MODE, key);
   
   byte[] decryptedText = cipher.doFinal(ciphertext);
   System.out.println("\nDecrypted text: " + new String(decryptedText, "UTF8"));
   
   
  } 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 (UnsupportedEncodingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalBlockSizeException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (BadPaddingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }

}

 

 

라)    간단한 암호화 예제 : Blowfish : DESede 예제에서 2군데만 달라진다.

 KeyGenerator keyGenerator = KeyGenerator.getInstance("Blowfish");

Cipher cipher = Cipher.getInstance("Blowfish /ECB/PKCS5Padding");

 

 

Posted by sinpk

패스워드 기반 암호화(PBE : Password-Based Encryption)

 

1  설명

   가) 패스워드를 키로 사용하는 방법

   나) 키 스페이스가 TripleDES나 Blowfish보다 훨씬 작아 안전하지 못함

   다) 사전공격에 취약함

   라) 해쉬와 일반적인 대칭 암호화를 사용함

   마) 미리 패스워드 해쉬 목록을 만들어 놓고 복호화 할 수 있음

   바) salting와 iteration counts를 도입하여 보안성 강화

 

2  방법

   가) 패스워를 MD5로 해쉬를 구한 다음 이 값을 암호화 키 값으로 사용한다. 

 

3  Salt

   가) 패스워드를 해쉬하기 전에 랜덤값을 추가하여 키 스페이스를 늘린다.

   나) salt는 암호문과 함께 저장된다.

   다) 복호화할 때는 암호화된 데이터로부터 salt를 분리해야 한다.

   라) salt는 매번 다르게 생성되며 동일한 문장에 대해서도 다른 값이 생성된다.

 

4  Iteration count

   가) 해쉬의 횟수를 의미함

   나) Iteration count가 1000이면 1000번을 해쉬한다는 의미

 

5  암호화/복호화 방법

   가) 패스워드와 salt를 생성하여 보통문서를 암호화하고 생성된salt를 암호문서 앞에 붙인다.

 

   나) 암호화된 데이타를 salt와 암호문서로 나누어 패스워드와 salt로 암호 문서를 복호화 한다.

 

 

6  주요 클래스 사용법

   가) PBEKeySpec : SecretKeyFactory의 인스턴스를 이용해서 패스워드기반 키를 생성한다.

 char[] password = “iloveyou”.toCharArry();

PBEKeySpec keySpec = new PBEKeySpec(password)

 

   나) SecretKeyFactory : PBEKeySpec을 실제 키로 사용하기 위해 generateSecret()를 실행시켜 줘야 한다.

 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(“PBEWithShAAndTwoFish-CBC”);

SecretKey theKey = keyFactory.generateSecret(keySpec);

 

   다) PBEParameterSpec : salt와 interation count를 위한 레퍼런스

 PBEParameterSpec paramSpec = new PBEParameterSpec(salt, iterations);

Cipher cipher = Cipher.getInstance(“PBEWithShAAndTwoFish-CBC”);

Cipher.init(Cipher.ENCRYPT_MODE, theKey, paramSpec);

 

7  PBE 알고리즘

   가) PBEWithMD5AndDES 

   나) PBEWithSHAAndBlowfish

   다) PBEWithSHAAnd128BitRC4

   라) PBEWithSHAAndDEA-CBC

   마) PBEWithSHAAnd3-KeyTripleDES-CBC

 

8  PBE 예제

 

 import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import com.Ostermiller.util.Base64;

public class PBE {

 private static int ITERATIONS = 1000;
 
 public static void main(String[] args) {
  char[] password = "iloveyou".toCharArray();
  String text = "i love you! but you don't. i sad b.b";
  String output = null;
  
  System.out.println("평문 : " + text);
  try {
   output = encrypt(password, text);
   System.out.println("암호문 : " + output);
   System.out.println("복호문 : " + decrypt(password, output));
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 private static String encrypt(char[] password, String plaintext) throws Exception {
  // 1. salt를 8byte 랜덤 생성
  byte[] salt = new byte[8];
  Random random = new Random();
  random.nextBytes(salt);
  
  // 2. 패스워드를 이용하여 PBEKeySpec 생성
  PBEKeySpec keySpec = new PBEKeySpec(password);
  SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwoFish-CBC");
  
  // 3. 비밀키 생성
  SecretKey key = keyFactory.generateSecret(keySpec);
  
  // 4. salt, iteration count를 위한 PBEParameterSpec 생성
  PBEParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);
  
  // 5. Cipher 생성 및 초기화
  Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwoFish-CBC");
  cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
  
  // 6. 암호문 생성
  byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
  
  // 7. salt와 암호문을 Base64 인코딩 후 결합하여 최종 결과물 생성
  String saltString = Base64.encodeToString(salt);
  String ciphertextString = Base64.encodeToString(ciphertext);
  
  return saltString+ciphertextString;
  
 }
 
 private static String decrypt(char[] password, String text) throws Exception {
  
  // 1. 8byte를 Base64인코딩하면 12byte가 생성된다.
  String salt = text.substring(0, 12);
  String ciphertext = text.substring(12, text.length());
  
  // 2. salt와 암호문을 Base64 디코딩하여 원래 상태로 되돌린다.
  byte[] saltArry = Base64.decodeToBytes(salt);
  byte[] ciphertextArry = Base64.decodeToBytes(ciphertext);
  
  // 3. 패스워드를 이용하여 PBEKeySpec 생성
  PBEKeySpec keySpec = new PBEKeySpec(password);
  SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHAAndTwoFish-CBC");
  
  // 4. 비밀 키 생성
  SecretKey key = keyFactory.generateSecret(keySpec);
  
  // 5. salt, iteration count를 위한 PBEParameterSpec 생성
  PBEParameterSpec paramSpec = new PBEParameterSpec(saltArry, ITERATIONS);
  
  // 6. Cipher 생성 및 초기화
  Cipher cipher = Cipher.getInstance("PBEWithSHAAndTwoFish-CBC");
  cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
  
  // 7. 평문생성
  byte[] plaintextArry = cipher.doFinal(ciphertextArry);
  
  return new String(plaintextArry);
 }

}

Posted by sinpk