2020.11.23 양방향 암호화 단방향 암호화 고급자바 수업노트

 암호화 


데이터 -> 


암호화를 하고 원래데이터로 바꿀 수가 없는 : 단방향 


암호화를 했다. 원래데로 복원할 수 있는 : 양방향 




대칭키 ㅣ 대칭키 (비공개) 


암호화를 할 떄 사용하는 키와 복호화를 할 떄 사용하는 키가 동일 : 대칭키 


암호화키랑 복호화키가 동일하면 -> 속도가 빠름, 키 배송 위험성 이 존재하여 송신 측에서 수신측에 암호 키 전달하는 과정에서 노출 우려 

des, aes 



비 대칭키 : (공개키) 



암호화할때 키와 복호화할때 키가 다르다. 

복호화키를 공개를 하면 -> 공개된 키로 -> 암호화하면 상대방은 2개중 나머지 1개를 가지고 있으니까 나머지키로 복호화가능 

키가 1 개 있을때에 비해 안전 





sha-256 

256 바이트 : 대체로 뒤에있는 숫자는 암호의 숫자의 길이라고 생각할 수 있다.


-----------------------------------------------------------------------------------------------------



양방향 암호화 알고리즘인 aes 256 암호화 방식 

package kr.or.ddit.basic;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;



public class AES256Util {

//양방향 암호화 알고리즘인 aes256 암호화 방식을 지원하는 클래스 
//초기화 벡터(Initial Vector , IV ) ==> IV는 암호문이 패턴화 되지 않도록 사용하는 데이터를 말한다. 
//   => 첫 블록을 암호화할 때 사용되는 값 ==> CBC모드에서 사용된다. : iv  
// => 암호화를 할 때 다른 랜덤 비트열을 이용하는 것이 보통이다. 
//iv 는 초기화 벡터값이 저장될 변수 : 암호문이 패턴화되는것을 막아주는 것 : 초기화 백터값 


private String iv; // 초기화 벡터값 
private Key keySpec; 



private static final String key = "a1b2c3d4e5f6g7h8i";


//암호화 키값(16글자 이상) 
//한글 한 글자가 2바이트씩 차지하고 알파벳 숫자는 1 바이트씩 차지하기 때문에 한글같은거를 16바이트로 고정하기 위해서 substring 을 밑에 써준다. 



// 생성자 

public AES256Util() throws UnsupportedEncodingException {

//key 값이 한글이면 바이트가 바이트니까 더 크기 때문에 substring 을 해줘서 16자리만 가져옴 


    this.iv = key.substring(0, 16); 

    //16자리로 고정된 keyBytes 배열을 미리 만들어줌 
    byte[] keyBytes = new byte[16];


    //key 의 길이가 얼마나 되든 그 길이 전체를 b 배열에 넣어줌 

    
    byte[] b = key.getBytes("utf-8");

    // 전체 배열( 큰길이 ) 의 길이를 구함

    int len = b.length;
    
    //전체길이(큰길이) 가 16자리보다 더 크면

    if(len > keyBytes.length ) {
    //len 의 길이를 16자리로 만들어줌 

        len = keyBytes.length; 
    }


    //앞에 있는 데이터 b 의 0 번째부터 복사하여서 len 만큼 복사해서 keyBytes 의 0 번째 
    부터 넣어라
    System.arraycopy(b, 0 , keyBytes, 0 , len);




    //비밀키 생성
    
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

    this.keySpec = keySpec; 얘가 비밀키.!!!!!

    
    


//AES256 방식으로 암호화하는 메서드 

    //str 은 암호화할 문자열
    //반환값은 암호화된 문자열 

    
public String encrypt(String str) throws NoSuchAlgorithmException,                                                                        NoSuchPaddingException, InvalidKeyException,                                                       InvalidAlgorithmParameterException,                                                                    IllegalBlockSizeException, BadPaddingException,                                                    UnsupportedEncodingException {

    
//Cipher 생성 및 초기화하기 
//     AES/CBC/PKCS5Padding   => 알고리즘 / 모드 / 패딩 
//    CBC : Cipher Block Chaining Mode ==> 동일한 평문 블록과 암호문 블록 쌍이 발생
//하지 않도록 이전 단계의 암복호화 한 결과가 현 단계에 영향을 주는 운영 모드를 말한다.

//수정 : 암호화를 안전하게 하기 위해 여러블록으로 나눠서 암호처리를 하는데 앞에서 사용했던 블럭을 계속 사용하겠다는 것 
//블록 암호화 운영모드 중 보완성이 가장 높은 암호화 방법으로 가장 많이 사용한다. 
암호화가 병렬적으로 처리되는 것이 아니라 순차적으로 수행되어야 한다. 


//패딩 : Padding ==> 마지막 블록이 블록의 길이와 항상 딱 맞아 떨어지지 않을 수 있기 때문에 부족한 길이만큼을 0 으로 채우거나 임의의 비트들로 채워넣는 것을 의미한다. 




    //AES 알고리즘 CBC모드 Padding 
    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 




    //16길이의 바이트를 만든다 ( 기준이 되는것 )

    byte[] ivBytes = new byte[16];
 
    System.arraycopy(iv.getBytes(), 0, ivBytes, 0 , ivBytes.length); 

    
    //옵션 종류 : ENCRYPT_MODE(암호화모드), DECRYPT_MODE(복호화모드) 
    //            WRAP_MODE(암호화키를 캡슐화) , UNWRAP_MODE(암호키 캡슐화 해제)


    //암호를 옵션 종류에 맞게 초기화한다. 
    // IvParameterSpec 는 초기화 백터  Cipher.ENCRYPT_MODE 암호화모드

    c.init(Cipher.ENCRYPT_MODE,keySpec, new IvParameterSpec(ivBytes));
    
    //암호문생성
    byte[] encrypted = c.doFinal(str.getBytes("utf-8"));
    String enStr = Base64.getEncoder().encodeToString(encrypted);


    return enStr;

}





//////////////////////////////////////////////////////////////////////////////////////////////

AES256 방식으로 암호화된 문자열을 복호화하는 메서드 str : 복호화할 암호화된 문자열
반환값 : 복호화된 문자열 

public String decrypt(String str) throws NoSuchAlgorithmException,                                                                                 NoSuchPaddingException,                                                                                 InvalidKeyException,                                                                                             InvalidAlgorithmParameterException,                                                                     UnsupportedEncodingException,                                                                      IllegalBlockSizeException, BadPaddingException {


    Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] ivBytes = new byte[16];
    System.arraycopy( iv.getBytes() , 0, ivBytes, 0 ,  ivBytes.length);
    c.init(Cipher.DECRYPT_MODE, keySpec , new IvParameterSpec(ivBytes));


    //암호화된 데이터를 원래의 BYTE 배열로 변환 
    byte[] byteStr = Base64.getDecoder().decode(str); 

    return new String(c.doFinal(byteStr), "utf-8");
}
}




----------------------------------------------------------------------------------------------------




//단방향 암호화  ( 위에 클래스사용해서 ( Util ) 아래 암호화 복호화 한 데이터 뽑아내보기 


package kr.or.ddit.basic;

import java.security.NoSuchAlgorithmException;

import kr.or.ddit.util.CryptoUtil;


//단방향 암호화 
public class CryptoTest {

    //문자열을 MD5 로 암호화한다 
    

    public static void main(String[] args) throws Exception {
        
        CryptoUtil crypto = new CryptoUtil();
        
        String plainText = "안녕하세요, 반갑습니다.";
    
        //Hello world 를 암호화했을때 나오는 방식이 다름 
        //사용자가 암호를 잊어버렸을때 그 암호를 사이트에서 아는 것이아니라 사이트에서
        //임의적으로 암호를 만들어서 알려준다. // 단방향 . 관리자도 비밀번호를 모른다. 
        //복원이 가능하지 않음,   복원을 할 필요가 없음 


        System.out.println("MD5 : " + crypto.md5(plainText));
System.out.println("SHA-256 : " + crypto.sha256(plainText));
System.out.println("SHA-512 : " + crypto.sha512(plainText));
System.out.println("---------------------------------------------");
    

        AES256Util aes256 = new AES256Util();

        String str = aes256.encrypt(plainText); 
        
        //str 은 암호화된 긴 어떤 것 

        System.out.println("원래의 데이터 : " + plainText);
System.out.println("AES256 암호화 : " + str);
System.out.println("암호화 문자열 길이 : " + str.length());
System.out.println("AES256 복호화 : " + aes256.decrypt(str));


        System.out.println("---------------------------------------------");
String tmp = ""; 

for(int i = 0 ; i <= 9 ; i++) {
for(int j= 0 ; j<= 9 ; j++) {
tmp += j; 

                        //tmp 를 암호화 
str = aes256.encrypt(tmp);
System.out.println( i + "-" + "tmp => " + tmp);
System.out.println("암호화 => " + str );
System.out.println("암호화 길이 => " + str.length());
String deStr = aes256.decrypt(str); 
System.out.println("복호화 => " + deStr);
System.out.println();
}
}
}

}





-----------------------------------------------------------------------------------------------------



package kr.or.ddit.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class CryptoUtil {


//문자열을 MD5 방식으로 암호화한다. ( 자리수 : 32BYTE_ 

    public String md5(String msg) throwt NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");

                md.update(msg.getBytes()); // 암호화 하기 
                
                //암호화된 데이터를 가져와서 16진수로 변환해서 반환 
                return byteToHexString(md.digest());

                //암호화된 데이터를 꺼내는 명령어 : digest() 
                
                 }


                //문자열을 SHA-256 방식으로 암호화한다. ( 자리수ㅡ 보통 64BYTE) 

     // 문자열을 SHA-256 방식으로 암호화한다. (자리수 보통 : 64byte) 
public String sha256(String msg) throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 
sha256.update(msg.getBytes());
return byteToHexString(sha256.digest());
}
// 문자열을 SHA-512방식으로 암호화한다. (자리수 보통 : 128byte) 
public String sha512(String msg) throws NoSuchAlgorithmException {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); 
sha512.update(msg.getBytes());
return byteToHexString(sha512.digest());
}


            

//byte 배열의 데이터를 6진수 값으로 변환하는 메서드 
public String byteToHexString(byte[] data ) {
StringBuilder sb = new StringBuilder();
for(byte b : data) {
//(b $ 0xff) + 0x100) ==> 16진수 2자리만들기 
// 0xa & 0xff ==> 0x0a ==> 0xa + 0x100 => 0x10a ==> "10a" ==>                             "0a" ( substring)
sb.append(Integer.toHexString((b & 0xff) + 0x100 ).substring(1));
//toHexString // & 는 bite ( 0 or 1 ) and 연산자 0xff 는 
                // 1111 1111 1 bite 는 8 개의 비트 // // 
                // 15 - > 16진수 // 2 진수 : 한 자리당 0 ~ 15 까지
                // 5 4 6 10 // 16 세제곱 16제곱 16 1 // a , b , c , d , e , f -> 16진수로 표기                     할때 10 부터 15까지 // 15 -> f // 16진수 한자리는 2 진수 4자리와 같다. 
                // 8진수 한자리는 2진수 3자리와 같다. // 0x숫자 -> 16진수 표기법 
                // 0숫자 -> 8진수 표기법 // 그냥 숫자 -> 10진수 // 0xff 는 15 15 1111 1111 :                 1 바이트다 (8개의 비트) }

               //16진수로 많이 출력을 해준다. 
return sb.toString(); 
}

}

   

-----------------------------------------------------------------------------------------------------

문과생의 16진수 공부하기 


2진법 0 , 1 
10 진법 0 ~ 9 
8진법 0 ~ 7 
16진법 0 ~ 15 

16진법에서 10 , 11 , 12 , 13 , 14 , 15 는 2자리 숫자라서 
10 = A
11 = B 
12 = C
13 = D
14 = E
15 = F 라고 해준다. 


16진수에 10 진수로의 변환 방법
맨 뒤에서 16의 0 제곱 , 1제곱, 2제곱, 3제곱 .... N 제곱을 해주면 된다. 


EX) 1A3F(16) = (15 X 1) + (3 X 16) + (10 X 16^2) + (1 X 16^3 )  
















댓글