IT/안드로이드+JAVA

[Android] AES256 암호화 복호화 (2가지 방법)

안경 쓴 귀니 2021. 1. 24. 15:37
반응형

AES256 암호화하는 방법

  1. 키를 직접 지정해서 암호화, 복호화하는 방법
  2. 키 스토어에서 생성한 키를 사용해서 암호화, 복호화하는 방법

1번 방법이 일반적으로 많이 사용되고 알려진 방법이다.

 

 

방법 1. 키를 직접 지정해서 암호화, 복호화하는 방법

  • 샘플 코드
public static byte[] iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };

// 사용자 지정 키로 AES256 암호화
public static String encByKey(String key, String value) throws Exception {
    return encByKey(key.getBytes(), value.getBytes());
}

// 사용자 지정 키로 AES256 복호화
public static String encByKey(byte[] key, byte[] value) throws Exception {
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
    byte[] randomKey = cipher.doFinal(value);
    return Base64.encodeToString(randomKey, 0);
}

// 사용자 지정 키로 AES256 복호화
public static String decByKey(String key, String plainText) throws Exception {
    return decByKey(key.getBytes(), Base64.decode(plainText, 0));
}

// 사용자 지정 키로 AES256 복호화
public static String decByKey(byte[] key, byte[] encText) throws Exception {
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
    byte[] secureKey = cipher.doFinal(encText);
    return new String(secureKey);
}

 

  • 사용 방법
try {
    String key = "secretKey1234567";
    String plainText = "test1234";
    String encText = AES.encByKey(key, plainText);
    String decText = AES.decByKey(key, encText);
    Log.d(TAG ,"암호화 결과 : " + encText);
    Log.d(TAG ,"복호화 결과 : " + decText);
} catch (Exception e) {
    e.printStackTrace();
}

 

  • 로그 결과
암호화 결과 : wI8W9C4f6d9/tbnNqpEQBg==
복호화 결과 : test1234

 

 

방법2. 키 스토어에서 생성한 키를 사용해서 암호화, 복호화하는 방법

암호화 시, 암호화된 값과 IV 값이 모두 필요하므로 String []으로 리턴한다.

  • 샘플 코드
// 키스토어 키 존재 여부 확인
public static boolean isExistKey(String alias) throws Exception {
    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
    keyStore.load(null);
    Enumeration<String> aliases = keyStore.aliases();
    while (aliases.hasMoreElements()) {
        String nextAlias = aliases.nextElement();
        if (nextAlias.equals(alias)) {
            return true;
        }
    }
    return false;
}

// 키스토어 키 생성
public static void generateKey(String alias) throws Exception {
    final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
    final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias,
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .build();
    keyGenerator.init(keyGenParameterSpec);
    keyGenerator.generateKey();
}

// 키스토어 키 조회
public static SecretKey getKeyStoreKey(String alias) throws Exception {
    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
    keyStore.load(null);
    final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null);
    return secretKeyEntry.getSecretKey();
}

// 키스토어 키로 AES256 암호화
public static String[] encByKeyStoreKey(SecretKey secretKey, String plainText) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    byte[] enc = cipher.doFinal(plainText.getBytes());
    byte[] iv = cipher.getIV();
    String encText = Base64.encodeToString(enc, 0);
    String ivText = Base64.encodeToString(iv, 0);

    String[] result = new String[2];
    result[0] = encText;
    result[1] = ivText;
    return result;
}

// 키스토어 키로 AES256 복호화
public static String decByKeyStoreKey(SecretKey secretKey, String encText, String iv) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec spec = new GCMParameterSpec(128, Base64.decode(iv, 0));
    cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
    byte[] dec = cipher.doFinal(Base64.decode(encText, 0));  // 확인 필요
    return new String(dec);
}

 

  • 사용 방법
String alias = "com.test.alias";
try {
    if (!AES.isExistKey(alias)) {
        AES.generateKey(alias);
    }
    SecretKey secretKey = AES.getKeyStoreKey(alias);
    String[] enc = AES.encByKeyStoreKey(secretKey, "test1234");
    String dec = AES.decByKeyStoreKey(secretKey, enc[0], enc[1]);
    Log.d(TAG, "암호화 결과 : " + enc[0]);
    Log.d(TAG, "암호화 IV : " + enc[1]);
    Log.d(TAG, "복호화 결과 : " + dec);
} catch (Exception e) {
    e.printStackTrace();
}

 

 

  • 로그 결과
암호화 결과 : QYVP3waFyEUU8erV+zZoT4Lh7qmpYrqG
암호화 IV : ILdpQTINl5SXuMSK
복호화 결과 : test1234

 

 

 

주의사항

1. 1번 방법을 사용할 때는 키 길이를 잘 맞춰서 사용해야한다.
   만약 키 길이를 맞추기 어렵다면, SHA256 해시를 사용해서 길이를 맞추는 방법도 있다.

2. 2번 방법을 사용할 때 암호화 시 나오는 IV 값을 가지고 있다가 복호화 시 사용해야 한다.
   그러므로, IV를 파일 등에 저장해서 가지고 있어야 한다.

 

 

전체 소스코드

1. AES

public class AES {
    public static byte[] iv = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };

    // 키스토어 키 존재 여부 확인
    public static boolean isExistKey(String alias) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        Enumeration<String> aliases = keyStore.aliases();
        while (aliases.hasMoreElements()) {
            String nextAlias = aliases.nextElement();
            if (nextAlias.equals(alias)) {
                return true;
            }
        }
        return false;
    }

    // 키스토어 키 생성
    public static void generateKey(String alias) throws Exception {
        final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
        final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .build();
        keyGenerator.init(keyGenParameterSpec);
        keyGenerator.generateKey();
    }
    
    // 키스토어 키 조회
    public static SecretKey getKeyStoreKey(String alias) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null);
        return secretKeyEntry.getSecretKey();
    }

    // 키스토어 키로 AES256 암호화
    public static String[] encByKeyStoreKey(SecretKey secretKey, String plainText) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] enc = cipher.doFinal(plainText.getBytes());
        byte[] iv = cipher.getIV();
        String encText = Base64.encodeToString(enc, 0);
        String ivText = Base64.encodeToString(iv, 0);

        String[] result = new String[2];
        result[0] = encText;
        result[1] = ivText;
        return result;
    }

    // 키스토어 키로 AES256 복호화
    public static String decByKeyStoreKey(SecretKey secretKey, String encText, String iv) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, Base64.decode(iv, 0));
        cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
        byte[] dec = cipher.doFinal(Base64.decode(encText, 0));  // 확인 필요
        return new String(dec);
    }

    // 사용자 지정 키로 AES256 암호화
    public static String encByKey(String key, String value) throws Exception {
        return encByKey(key.getBytes(), value.getBytes());
    }

    // 사용자 지정 키로 AES256 복호화
    public static String encByKey(byte[] key, byte[] value) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
        byte[] randomKey = cipher.doFinal(value);
        return Base64.encodeToString(randomKey, 0);
    }

    // 사용자 지정 키로 AES256 복호화
    public static String decByKey(String key, String plainText) throws Exception {
        return decByKey(key.getBytes(), Base64.decode(plainText, 0));
    }

    // 사용자 지정 키로 AES256 복호화
    public static String decByKey(byte[] key, byte[] encText) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
        byte[] secureKey = cipher.doFinal(encText);
        return new String(secureKey);
    }

}

 

 

2. MainActivity

public class MainActivity extends AppCompatActivity {
    public static final String TAG = MainActivity.class.getSimpleName();
    public static String alias = "com.test.alias";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            // 방법1. 키를 직접 지정해서 암호화, 복호화 하는 방법
            String key = "secretKey1234567";
            String plainText = "test1234";
            String encText = AES.encByKey(key, plainText);
            String decText = AES.decByKey(key, encText);
            Log.d(TAG ,"암호화 결과 : " + encText);
            Log.d(TAG ,"복호화 결과 : " + decText);

            // 방법2. 키스토어에서 생성한 키를 사용해서 암호화, 복호화 하는 방법
            if (!AES.isExistKey(alias)) {
                AES.generateKey(alias);
            }
            SecretKey secretKey = AES.getKeyStoreKey(alias);
            String[] enc = AES.encByKeyStoreKey(secretKey, "test1234");
            String dec = AES.decByKeyStoreKey(secretKey, enc[0], enc[1]);
            Log.d(TAG, "암호화 결과 : " + enc[0]);
            Log.d(TAG, "암호화 IV : " + enc[1]);
            Log.d(TAG, "복호화 결과 : " + dec);
            
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

 

반응형