1. AES란

AES는 미국 정부가 채택한 이후 전 세계적으로 널리 사용되고 있다.

1977년 공표된 DES를 대체한 AES는, 암호화와 복호화 과정에서 동일한 키를 사용하는 대칭 키 알고리즘이다.

 

2.키 길이

AES 암호화 키의 길이는

128 bit - 16byte 

192 bit - 24byte 

256 bit - 32byte 

의 세 가지 중 하나가 될 수 있으며,

각각 AES-128, AES-192, AES-256으로 불린다. 

암호화 키의 길이에 따라 실행하는 라운드의 수가 다른데, 각각 10, 12, 14 라운드를 실행한다.

 

3. 운영방식

EBC, CBC차이

전자 코드북(electronic codebook, ECB)은 운용 방식 중 가장 간단한 구조를 가지며, 암호화하려는 메시지를 여러 블록으로 나누어 각각 암호화하는 방식으로 되어 있다.

 

전자 코드북은 모든 블록이 같은 암호화 키를 사용하기 때문에 보안에 취약하다.

 

암호 블록 체인 (cipher-block chaining, CBC) 방식은 1976년 IBM에 의해 개발되었다.[4] 각 블록은 암호화되기 전에 이전 블록의 암호화 결과와 XOR되며, 첫 블록의 경우에는 초기화 벡터가 사용된다. 초기화 벡터가 같은 경우 출력 결과가 항상 같기 때문에, 매 암호화마다 다른 초기화 벡터를 사용해야 한다.

 

 

CBC 방식은 현재 널리 사용되는 운용 방식 중 하나이다

 

4. AES256 CBC방식 소스 구현해보기

https://developer.android.com/reference/javax/crypto/Cipher

위 Cipher클래스에서 Algorithm Modes Paddings를 하기와 같이 설정하여 확인해 보도록 하겠습니다.

AES/CBC/PKCS5Padding으로 확인해보겠습니다.

PKCS5Padding은

AES는 블록사이즈가 16바이트 크기로 16바이트의 배수가 아니라면

마지막 블록의 빈 부분을 채워주는 패딩옵션으로 PKCS5Padding로 적용해보겠습니다.

 

* 참고할만한 java 소스 코드 확인시

AES 블록은 16byte이고 AES키 사이즈는 16, 24, 32이며 

라운드 횟수는 키사이즈에 의해서 라운드 횟수가 정해지고

IV사이즈는 블록사이즈와 동일하게 가져야 하는 것을 확인할 수 있습니다.

 

AESConstants.java

interface AESConstants {
    // AES block size in bytes.
    int AES_BLOCK_SIZE = 16;
    // Valid AES key sizes in bytes.
    // NOTE: The values need to be listed in an *increasing* order
    // since DHKeyAgreement depends on this fact.
    int[] AES_KEYSIZES = { 16, 24, 32 };
}

 

AESCrypt.java

키사이즈에 의해서 라운드 횟수가 정해지는것을 확인할 수 있습니다.

/**
  * Return The number of rounds for a given Rijndael keysize.
  *
  * @param keySize  The size of the user key material in bytes.
  *                 MUST be one of (16, 24, 32).
  * @return         The number of rounds.
  */
private static int getRounds(int keySize) {
    return (keySize >> 2) + 6;
}

 

CipherCore.java

블록사이즈와 initial vector사이즈가 동일한 것을 확인할 수 있습니다.

if (params instanceof IvParameterSpec) {
    ivBytes = ((IvParameterSpec)params).getIV();
    if ((ivBytes == null) || (ivBytes.length != blockSize)) {
        throw new InvalidAlgorithmParameterException
            ("Wrong IV length: must be " + blockSize +
              " bytes long");
    }
}

 

Cipher클래스를 이용해서 하기와 같이 구성을 해보았습니다.

//키길이 AES 256
private val KEY_SIZE = 32
private val SECRET_KEY = "0".repeat(KEY_SIZE)
//모드 CBC
private val AES_TRANSFORMATION = "AES/CBC/PKCS5PADDING"
//AES initial Vector == 블록사이즈 16고정
private val IV = "1234567890123456"
private val ivParameterSpec = IvParameterSpec(IV.toByteArray())
private fun secretKeySpec(): SecretKeySpec {
    return SecretKeySpec(SECRET_KEY.toByteArray(), "AES")
}

private fun encrypt(plainText: String): ByteArray  {
    val cipher = Cipher.getInstance(AES_TRANSFORMATION)
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec(), ivParameterSpec)
    return cipher.doFinal(plainText.toByteArray())
}

private fun decrypt(cipherText: ByteArray?): String {
    val cipher = Cipher.getInstance(AES_TRANSFORMATION)
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec(), ivParameterSpec)
    return String(cipher.doFinal(cipherText))
}

private fun encode(byteArray: ByteArray): ByteArray {
    val decodedBytes = Base64.encode(byteArray, Base64.DEFAULT)
    return decodedBytes
}

private fun decode(encodedString: String): ByteArray {
    val decodedBytes = Base64.decode(encodedString, Base64.DEFAULT)
    return decodedBytes
}

 

실행

val plainText = "|" + REQUEST_DATE
Timber.tag(TAG).i("plainText: " + plainText)
val encryptText = encode(encrypt(plainText))
Timber.tag(TAG).i("encrypt : " + encryptText)

var resultDecrypt = decrypt( decode(resultStr))
Timber.tag(TAG).i("result decrypt : " + resultDecrypt)

 

결과

plainText: |1604809558
encrypt : [B@162f532
decrypt : |1604809558

 

+ Recent posts