JAVA 암복호화 (Cipher 클래스 AES, RSA 암호화, 복호화)

오성민·2021년 2월 4일
0

JAVA Cipher 클래스 (Java로 AES, RSA 암호화, 복호화)

  1. RAS
    RSA 암호는 공개키 암호시스템의 하나로, 암호화뿐만 아니라 전자서명이 가능한 최초의 알고리즘으로 알려져 있다. RSA가 갖는 전자서명 기능은 인증을 요구하는 전자 상거래 등에 RSA의 광범위한 활용을 가능하게 하였다.

RSA는 두 개의 키를 사용한다. 여기서 키란 메시지를 열고 잠그는 상수(constant)를 의미한다. 일반적으로 많은 공개키 알고리즘의 공개키(public key)는 모두에게 알려져 있으며 메시지를 암호화(encrypt)하는데 쓰이며, 암호화된 메시지는 개인키(private key)를 가진 자만이 복호화(decrypt)하여 열어볼 수 있다.

공개키 알고리즘은 누구나 어떤 메시지를 암호화할 수 있지만, 그것을 해독하여 열람할 수 있는 사람은 개인키를 지닌 단 한 사람만이 존재한다는 점에서 대칭키 알고리즘과 차이를 가진다.

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import javax.crypto.Cipher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class RsaUtil {
	
	static Logger Log = LoggerFactory.getLogger(RsaUtil.class);

	private KeyPairGenerator generator;
	private KeyFactory keyFactory;
	private KeyPair keypair;
	private Cipher cipher;
	
    // 1024비트 RSA 키쌍을 생성
	public RsaUtil() {
		try{
			generator = KeyPairGenerator.getInstance("RSA");
			generator.initialize(1024);
			keyFactory = KeyFactory.getInstance("RSA");
			cipher = Cipher.getInstance("RSA");
		}catch (Exception e){
			Log.debug("RsaUtil Create Failed", e);
		}
	}
	
	public HashMap<String, Object> createRSA() {
		HashMap<String, Object> rsa = new HashMap<String, Object>();
		try{
			keypair = generator.generateKeyPair();
			PublicKey publicKey = keypair.getPublic();
			PrivateKey privateKey = keypair.getPrivate();
			
			RSAPublicKeySpec publicSpec = keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
			String modulus = publicSpec.getModulus().toString(16);
			String exponent = publicSpec.getPublicExponent().toString(16);
			rsa.put("privateKey", privateKey);
			rsa.put("modulus", modulus);
			rsa.put("exponent", exponent);
			
		}catch (Exception e){
			Log.debug("RsaUtil.createRSA()", e);
		}
		
		return rsa;
	}
	
    // Key로 RSA 복호화를 수행
	public String getDecryptText(PrivateKey privateKey, String ecryptText) throws Exception {
		cipher.init(cipher.DECRYPT_MODE, privateKey);
		byte[] decryptedBytes = cipher.doFinal(hexToByteArray(ecryptText));
		
		return new String(decryptedBytes, "UTF-8");
	}
    
    // Key로 RSA 암호화를 수행
	public String setEncryptText(PrivateKey publicKey, String encryptText) throws Exception {
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(encryptText.getBytes());
        
        return new String(encryptedBytes, "UTF-8");
    }
	
	private byte[] hexToByteArray(String hex){
		if(hex == null || hex.length() % 2 != 0){
			return new byte[]{};
		}
		
		byte[] bytes = new byte[hex.length() / 2];
		for(int i = 0; i < hex.length(); i += 2){
			byte value = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
			bytes[(int) Math.floor(i / 2)] = value;
		}
		
		return bytes;
	}
}
  1. AES
    AES는 미국 정부가 채택한 이후 전 세계적으로 널리 사용되고 있다. 암호화와 복호화 과정에서 동일한 키를 사용하는 대칭 키 알고리즘이다.

AES256(256비트 블록암호화) 입니다. 주로 평문 데이터를 암호화하는데 많이 쓰이며, 비밀키 하나로 데이터를 암호화 하고 복호화를 하며 사용하게 됩니다. 장점으로는 AES128과 비교하였을때는 당연히 보안성과 안전성이 높다고 말할수 있으며, 더 멀리 나아가 비대칭키(공개키 암호화시스템)보다 속도가 빠르다는 이점을 가지고 있습니다. 비대칭키와 대칭키에 대한 개념설명은 따로 포스팅 하겠습니다. 단점으로는 키 한개로 암호화복호화를 하기 때문에 유출이 된다면, 암호화는 의미가 없어지겠죠.

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

//양방향 암호화 알고리즘인 AES256 암호화를 지원하는 클래스
public class AES256Util {
	private String iv;
	private Key keySpec;

	/**
	 * 16자리의 키값을 입력하여 객체를 생성
	 * 
	 * @param key 암/복호화를 위한 키값         
	 * @throws UnsupportedEncodingException 키값의 길이가 16이하일 경우 발생
	 */
	final static String key = "비밀키입력하는곳";

	public AES256Util() throws UnsupportedEncodingException {
		this.iv = key.substring(0, 16);
		byte[] keyBytes = new byte[16];
		byte[] b = key.getBytes("UTF-8");
		int len = b.length;
		if (len > keyBytes.length) {
			len = keyBytes.length;
		}
		System.arraycopy(b, 0, keyBytes, 0, len);
		SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

		this.keySpec = keySpec;
	}

	/**
	 * AES256 으로 암호화
	 * 
	 * @param str 암호화할 문자열   
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws GeneralSecurityException
	 * @throws UnsupportedEncodingException
	 */
	public String encrypt(String str) throws NoSuchAlgorithmException,
			GeneralSecurityException, UnsupportedEncodingException {
		Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
		c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
		byte[] encrypted = c.doFinal(str.getBytes("UTF-8"));
		String enStr = new String(Base64.encodeBase64(encrypted));
		return enStr;
	}

	/**
	 * AES256으로 암호화된 txt를 복호화
	 * 
	 * @param str 복호화할 문자열    
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws GeneralSecurityException
	 * @throws UnsupportedEncodingException
	 */
	public String decrypt(String str) throws NoSuchAlgorithmException,
			GeneralSecurityException, UnsupportedEncodingException {
		Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
		c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
		byte[] byteStr = Base64.decodeBase64(str.getBytes());
		return new String(c.doFinal(byteStr), "UTF-8");
	}
}

패딩(padding) 이란?
아래 코드에도 나오겠지만, 위에서 블록암호화를 진행하기 위해서는 패딩기법이 필요합니다. 데이터를 특정크기로 맞추기 위해서, 특정크기보다 부족한 부분의 공간을 의미없는 문자들로 채워서 비트수를 맞추는 것입니다. 암호화시에는 반드시 필요한 방법입니다.

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 다음과 같이 쓰일수 있습니다.

profile
끊임없이 공부하고 배우자!

0개의 댓글