반응형

서론

https 를 사용함에도 반드시 암호화 해달라는 고객의 요청으로 처리하면서

클라이언트- 서버 통신에서 비밀번호 평문을 막기위해 사용하였다.

회원가입 / 비밀번호 변경 / 비밀번호 인증 등에서 사용하고 있다.

당연하겠지만, DB 저장용이 아닌 구간 암호화용으로만 써야한다.

DB 암호화용은 SHA 와 같은 단방향 해싱 암호화를 하자.

자바 암-복호화

클라이언트가 JS를 사용하는 경우 CryptJS를 사용하고

서버가 Java 인 경우는 아래 코드를 활용하면 된다.

다만, salt 와 iv 는 동적으로 변경해야 더 안전하다.

해싱 암호화 횟수를 늘리면 늘릴 수록 안전하다만, 횟수에 비례해 암호화 속도가 느려지는 단점이있다.

자바는 아래와 같이 암호화하면 된다.

Base64 인-디코딩을 위해 apache common 라이브러리가 필요하다.

import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

public class AesCryptUtil {

    public static final String ENC_KEY = "e534cf179007db7e6360ebf95fa5d51c";
    public static final String salt = "deafa8b6802cebcc0bcceaaa5f3461a9"; 
    public static final String iv = "e9d3712c4d5c35093d340733b8c26b92";

    public static String decrypt(String data) {
        try {
            return decrypt(salt, iv, ENC_KEY, data, 1000, 128);    
        } catch(Exception e) {
            return "";
        }
    }

    private static String decrypt(String salt, String iv, String passphrase, String ciphertext, int iterationCount, int keySize) throws Exception {        
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), Hex.decodeHex(salt.toCharArray()), iterationCount, keySize);
        SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");        
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Hex.decodeHex(iv.toCharArray())));        
        byte[] decrypted = cipher.doFinal(Base64.decodeBase64(ciphertext));        
        return new String(decrypted, "UTF-8");
    }

    public static String encrypt(String data) {
        try {
            return encrypt(salt, iv, ENC_KEY, data, 1000, 128);    
        } catch(Exception e) {
            return "";
        }
    }

    private static String encrypt(String salt, String iv, String passphrase, String ciphertext, int iterationCount, int keySize) throws Exception {        
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), Hex.decodeHex(salt.toCharArray()), iterationCount, keySize);
        SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");        
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(Hex.decodeHex(iv.toCharArray())));
        byte[] encrypted = cipher.doFinal(ciphertext.getBytes("UTF-8"));
        return new String(Base64.encodeBase64(encrypted));
    }

    public static void main(String[] args) {

        String enc = encrypt("test001");
        System.out.println(enc);

        System.out.println(decrypt(enc));
    }
}

JavaScript 암호화

다운로드

  • 사용할 JS 는 Crypto-js 와 Crypto-js AES 이다.

코드

var iv = 'e9d3712c4d5c35093d340733b8c26b92';
var salt = 'deafa8b6802cebcc0bcceaaa5f3461a9';
var passPhrase = 'e534cf179007db7e6360ebf95fa5d51c';
var keySize = 128;
var iterationCount = 1000;
var key128Bits = CryptoJS.PBKDF2(passPhrase, 
                    CryptoJS.enc.Hex.parse(salt), 
                    { keySize: keySize / 32, iterations: iterationCount }
            );

iv 와 salt 는 새로 hashing 해서 만들기를 권장한다.

JavaScript 변수명 또한 읽었을 때 이해가 안되도록 전혀 엉뚱한 변수명으로 하는 것도 좋다.

반응형

WRITTEN BY
데르벨준

,