같은 언어끼리 암/복호화를 하는 경우는 몇번 경험을 해보았지만, 언어가 다른 환경에서 암/복호화 하는것은 많은 경험이 없어 이것에 대해 글을 남겨보려고 한다.
0. 환경
- Java 1.8
- Python 3.7
- pycrypto==2.6.1
- 암호화방식: AES128
1. Python에서의 암/복호화
## aes128_crypto.py
import base64
from Crypto import Random
from Crypto.Cipher import AES
class AES128Crypto:
def __init__(self, encrypt_key):
self.BS = AES.block_size
##암호화 키중 16자리만 잘라서 쓴다.
self.encrypt_key = encrypt_key[:16].encode(encoding='utf-8', errors='strict')
self.pad = lambda s: bytes(s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS), 'utf-8')
self.unpad = lambda s: s[0:-ord(s[-1:])]
def encrypt(self, raw):
raw = self.pad(raw)
# initialization vector 를 매번 랜덤으로 생성 한다.
iv = Random.new().read(self.BS)
cipher = AES.new(self.encrypt_key, AES.MODE_CBC, iv)
# 암호화시 앞에 iv와 암화화 값을 붙여 인코딩 한다.
# 디코딩시 앞에서 BS(block_size) 만금 잘라서 iv를 구하고, 이를통해 복호화 한다.
return base64.b64encode(iv + cipher.encrypt(raw)).decode("utf-8")
def decrypt(self, enc):
enc = base64.b64decode(enc)
# encrypt 에서 작업한 것처럼 첫 16바이트(block_size=BS) 를 잘라 iv를 만들고, 그 뒤를 복호화 하고자 하는 메세지로 잘라 만든다.
iv = enc[:self.BS]
encrypted_msg = enc[self.BS:]
cipher = AES.new(self.encrypt_key, AES.MODE_CBC, iv)
return self.unpad(cipher.decrypt(encrypted_msg)).decode('utf-8')
Python 모듈 사용방법
from cipher.aes128_crypto import AES128Crypto
if __name__ == '__main__':
encrypt_key = "1234567890123456"
plain_text = "서울특별시"
cipher = AES128Crypto(encrypt_key)
enc_text = cipher.encrypt(plain_text)
dec_text = cipher.decrypt(enc_text)
print(enc_text)
print(dec_text)
print(plain_text == dec_text)
2. Java에서의 암/복호화
package com.tistory.louisdev;
public interface Crypto {
String encrypt(String message) throws Exception;
String decrypt(String encryptedMessage) throws Exception;
}
package com.tistory.louisdev;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
public class AES128Crypto implements Crypto{
//Initialization Vector Block Size
private static final int BLOCK_SIZE = 16;
//암호화 키 사이즈
private static final int MINIMUM_ENCRYPT_KEY_SIZE = 16;
//암호화 알고리즘
private static final String ALGORITHM = "AES";
private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
private final byte[] encryptKeyBytes;
public AES128Crypto(String encryptKey) {
if(encryptKey == null || encryptKey.length() < MINIMUM_ENCRYPT_KEY_SIZE) {
throw new RuntimeException(String.format("암호화 키는 최소 %d자리 이상이어야 합니다.", MINIMUM_ENCRYPT_KEY_SIZE));
}
//암호화키를 16자리만 자른 후 사용한다.
encryptKeyBytes = encryptKey.substring(0, MINIMUM_ENCRYPT_KEY_SIZE).getBytes(StandardCharsets.UTF_8);
}
/**
* 암호화
* */
public String encrypt(String message) throws Exception {
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
SecretKeySpec keySpec = new SecretKeySpec(encryptKeyBytes, ALGORITHM);
byte[] ivBytes = createRandomIv();
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] inputBytes = new byte[BLOCK_SIZE + messageBytes.length];
//iv 와 Plain Text를 합쳐서 암호화 한다.
System.arraycopy(ivBytes, 0, inputBytes, 0, BLOCK_SIZE);
System.arraycopy(messageBytes, 0, inputBytes, BLOCK_SIZE, messageBytes.length);
return Base64.encodeBase64String(cipher.doFinal(inputBytes));
}
/**
* 복호화
* */
public String decrypt(String encryptedMessage) throws Exception {
byte[] msgBytes = Base64.decodeBase64(encryptedMessage);
//암호화된 메세지 자체가 (iv + 메세지)로 되어있기 때문에 앞에서 부터 BLOCK_SIZE 만큼 잘라내어 iv를 만들고
//나머지 부분을 잘라내서 복호화한다.
byte[] ivBytes = Arrays.copyOfRange(msgBytes, 0, BLOCK_SIZE);
byte[] inputBytes = Arrays.copyOfRange(msgBytes, BLOCK_SIZE, msgBytes.length);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec keySpec = new SecretKeySpec(encryptKeyBytes, ALGORITHM);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return new String(cipher.doFinal(inputBytes));
}
/**
* Create Random Initialization Vector
* */
private byte[] createRandomIv(){
SecureRandom random = new SecureRandom();
byte[] ivBytes = new byte[BLOCK_SIZE];
random.nextBytes(ivBytes);
return ivBytes;
}
}
Java 모듈 사용방법
package com.tistory.louisdev;
import org.junit.Assert;
import org.junit.Test;
public class AES128CryptoTest {
private static final String SECRET_KEY = "1234567890123456";
@Test
public void testAES128Crypto() throws Exception {
Crypto crypto = new AES128Crypto(SECRET_KEY);
String plainText = "서울특별시";
String encryptText = crypto.encrypt(plainText);
String decryptText = crypto.decrypt(encryptText);
System.out.println("Encrypt Text : " + encryptText);
System.out.println("Decrypt Text : " + decryptText);
Assert.assertEquals(plainText, decryptText);
}
}
암호화키(Secret Key)를 동일하게 하면 Java 암호화 -> Python 복호화
, Python 암호화 -> Java 복호화
가 가능하다.
'etc' 카테고리의 다른 글
[JupyterLab] Nginx Reverse Proxy를 통한 Jupyter Lab 연결 설정 (0) | 2020.09.04 |
---|---|
[Mybatis] Oracle Bind Mismatch 해결하기 (0) | 2020.08.03 |
[Docker] Dockerfile에 Locale 설정 및 KST 시간 적용하기 (0) | 2020.05.06 |
[기타] Chrome NET::ERR_CERT_REVOKED 해결방법 (0) | 2020.02.12 |
[Kafka Manager] 설치하기 (0) | 2020.02.12 |