암호화
데이터 ->
암호화를 하고 원래데이터로 바꿀 수가 없는 : 단방향
암호화를 했다. 원래데로 복원할 수 있는 : 양방향
대칭키 ㅣ 대칭키 (비공개)
암호화를 할 떄 사용하는 키와 복호화를 할 떄 사용하는 키가 동일 : 대칭키
암호화키랑 복호화키가 동일하면 -> 속도가 빠름, 키 배송 위험성 이 존재하여 송신 측에서 수신측에 암호 키 전달하는 과정에서 노출 우려
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 )
댓글
댓글 쓰기