JAVA Cipher 클래스 (Java로 AES, 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;
}
}
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"); 다음과 같이 쓰일수 있습니다.