최근 수정 시각 : 2024-09-02 08:24:38

AES

고급 암호화 표준에서 넘어옴

파일:나무위키+유도.png  
은(는) 여기로 연결됩니다.
SNK가 제작한 게임기 네오지오에 대한 내용은 네오지오 문서
번 문단을
부분을
, 능동 정전기식 펜 입력 기술(Active ElectroStatic)에 대한 내용은 스타일러스 펜 문서
번 문단을
번 문단을
부분을
부분을
, Audio Engineering Society에 대한 내용은 오디오 엔지니어링 소사이어티 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
참고하십시오.
'''이론 컴퓨터 과학
{{{#!wiki style="display: inline-block; font-family:Times New Roman, serif;font-style:italic"'''
{{{#!wiki style="margin: 0 -10px -5px; min-height: calc(1.5em + 5px)"
{{{#!folding [ 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px"
<colbgcolor=#a36> 이론
기본 대상 수학기초론{수리논리학(논리 연산) · 계산 가능성 이론 · 범주론 · 집합론} · 이산수학(그래프 이론) · 수치해석학 · 확률론통계학 · 선형대수학
다루는 대상과 주요 토픽
계산 가능성 이론 재귀함수 · 튜링 머신 · 람다대수 · 처치-튜링 명제 · 바쁜 비버
오토마타 이론 FSM · 푸시다운 · 튜링 머신(폰노이만 구조) · 정규 표현식 · 콘웨이의 생명 게임 · 형식언어
계산 복잡도 이론 점근 표기법 · 튜링 기계^고전, 양자, 비결정론적, 병렬 임의접근 기계^ · 알고리즘 · 자료구조 · 알고리즘 패러다임(그리디 알고리즘, 동적 계획법)
정보이론 데이터 압축(무손실 압축 포맷 · 손실 압축 포맷) · 채널 코딩(채널 용량) · 알고리즘 정보 이론(AIT) · 양자정보과학
프로그래밍 언어이론 프로그래밍 언어(함수형 언어 · 객체 지향 프로그래밍 · 증명보조기) · 메타 프로그래밍 · 유형 이론 · 프로그래밍 언어 의미론 · 파싱 · 컴파일러 이론
주요 알고리즘 및 자료구조
기초 정렬 알고리즘 · 순서도 · 탐색 알고리즘
추상적 자료형 및 구현 배열^벡터^ · 리스트^연결 리스트^ · 셋(set)^레드-블랙 트리, B-트리^ · 우선순위 큐^, 피보나치 힙^
수학적 최적화 조합 최적화 외판원 순회 문제 · 담금질 기법 · 유전 알고리즘 · 기계학습
볼록 최적화 내부점 방법 · 경사하강법
선형계획법 심플렉스법
계산 수론 및 암호학 밀러-라빈 소수판별법 · Pollard-rho 알고리즘 · 쇼어 알고리즘 · LLL 알고리즘 · 해시(MD5 · 암호화폐 · 사전 공격(레인보우 테이블) · SHA) · 양자 암호
대칭키 암호화 방식 블록 암호 알고리즘(AES · ARIA · LEA · Camellia) · 스트림 암호 알고리즘(RC4)
공개키 암호화 방식 공개키 암호 알고리즘(타원 곡선 암호 · RSA) · 신원 기반 암호 알고리즘(SM9)
계산기하학 볼록 껍질 · 들로네 삼각분할 및 보로노이 도형^Fortune의 line-sweeping 알고리즘^ · 범위 탐색^vp-tree, R-tree^ · k-NN
그래프 이론 탐색^BFS, DFS, 다익스트라 알고리즘, A* 알고리즘^ · 에드몬드-카프 · 크루스칼 알고리즘 · 위상 정렬 · 네트워크 이론
정리
정지 문제대각선 논법 · 암달의 법칙 · P-NP 문제미해결 · 콜라츠 추측미해결
틀:이산수학 · 틀:수학기초론 · 틀:컴퓨터공학 }}}}}}}}}


1. 개요2. 역사3. 특징4. 알고리즘
4.1. 개요4.2. KeyExpansion
4.2.1. 개관4.2.2. RotWord & SubWord4.2.3. Rcon, Round Constant
4.3. SubBytes
4.3.1. InvSubBytes
4.4. ShiftRows
4.4.1. InvShiftRows
4.5. MixColumns
4.5.1. InvMixColumns
4.6. AddRoundKey4.7. 이 외
4.7.1. BASE64 인코딩4.7.2. PKCS#7 패딩
5. 안전성6. 프로그래밍 언어 API7. 기타8. 관련 문서

1. 개요

fFgx/YKxIRcNIQwkmcWzMw==
단어 namu.wiki를 암호 umanle, IV 12345678b0z2345n, AES-256-CBC 방식으로 암호화한 문장이며, 계산시 암호 뒷부분은 0x0 으로 패딩
표준문서: FIPS-197.

AES는 <Advanced Encryption Standard>의 약자로 '고급 암호화 표준'라는 의미이다. AES는 미국 표준 기술 연구소에 의해서 연방 정보 처리 표준으로 지정된 암호화 방식이며 NSA에 의해 1급 비밀에 사용할 수 있도록 승인된 암호화 알고리즘이며, 오픈소스로 공개된 알고리즘이다. 대칭키를 쓰는 블럭 암호이다. 높은 안전성과 속도로 인해 인기를 얻어 전 세계적으로 많이 이용되고 있다. 이러한 이점으로 인해서 랜섬웨어에 많이 이용되고 있기도 하다.[1]

현재 AES는 DES(데이터 암호화 표준)의 뒤를 이을 AES(고급 암호화 표준)라는 이름을 걸고 미국 표준 기술 연구소가 주최한 공모전에서 채택된 Rijndael(레인달) 알고리즘을 가리킨다. 엄밀하게는 Rijndael 알고리즘의 여러 가능성 중, 암호화 블럭의 크기가 128비트이며 암호화 키의 길이가 128, 192, 256비트인 세 가지 종류가 AES 표준으로 지정되었다. 각각 AES-128, AES-192, AES-256으로 불린다.

2. 역사

AES이전 미국 표준 기술 연구소는 1977년에 DES를 표준으로 지정하였고 DES는 오랫동안 암호화의 표준으로 잘 사용되었다. 그러나 1990년대에 기술의 발전으로 56비트 키를 쓰는 DES가 더 이상 안전하지 않게 되었고[2], DES를 3번 사용하는 3-DES와 같은 방법도 사용되기는 하였으나 여러 문제로 인해 새로운 암호화 표준을 지정할 필요가 있었다.

NIST는 1997년 1월에 AES라는 이름의 표준을 제정할 것을 발표하였고, 1997년 9월부터 암호화 알고리즘의 공모를 받기 시작했다. 조건은 128비트 블록 암호이며, 128, 192, 256비트 길이의 키를 지원할 것이었다. 총 15개의 후보 알고리즘은 1998년 8월 20일의 첫번째 AES 후보 학술대회(First Advanced Encryption Standard Candidate Conference)와 1999년 3월의 두번째 학회를 거치며 5개로 추려졌다. MARS, RC6, Rijndael, Serpent, Twofish 알고리즘이 최종 후보에 올랐고, 2000년 4월의 세번째 학회에서 다루어졌다. 결국 나머지 후보군들 중에서 안전성, 유연성, 성능 등을 가장 잘 만족하는 Rijndael 알고리즘이 최종적으로 채택되었고, NIST는 2001년 11월 26일에 Rijndael 알고리즘[3]을 FIPS 197, AES라는 이름으로 표준으로 발표하였다.

3. 특징

대칭형, 블럭 암호화 알고리즘이다. 대칭형 암호화 알고리즘 중 가장 유명하다.

암호화 키는 128, 192, 256의 세 가지 중 하나가 될 수 있으며, 각각 AES-128, AES-192, AES-256으로 불린다. 암호화 키의 길이에 따라 실행하는 라운드의 수가 다른데, 각각 10, 12, 14 라운드를 실행한다.

S-Box를 간단히 설명하자면 입력 데이터를 지정된 숫자로 바꿔서 암호를 깨기 어렵게 만드는 기법이다. AES는 이걸 창조롭게 재발명하여 암호화 속도를 높이고 싶으면 S-Box를 메모리에 박아놓고, 프로그램 메모리 양을 줄이려면 실행시 S-Box를 연산으로 구해내는 기법을 사용했다.

4. 알고리즘

AES의 과정을 쉽게 설명하고 보여주는 동영상. 아래의 내용이 너무 복잡하다면 영상과 같이 보자.

Rijndael(레인달)이 AES로 채택되었으므로 아래는 Rjindael 알고리즘에 대한 설명이기도 하다.
아래 설명에서 블럭 크기인 128비트와, 키 길이인 128, 192, 256비트를 헷갈리지 않도록 주의하자.

4.1. 개요

Rijndael 알고리즘은 크게 보아 네 단계로 이루어진다.
  • KeyExpansion: key schedule(키 스케줄)이라고도 부른다. 128, 192 또는 256비트 길이인 하나의 주 암호화 키를 받아서 아래 라운드들에서 사용할 여러 개의 128비트 라운드 키를 생성한다.
  • 0 라운드: 위의 단계에서 생성한 라운드 키 중 첫번째 키를 사용, AddRoundKey를 한 번 실행한다.
  • 1~(9, 11, 13) 라운드: SubBytes, ShiftRows, MixColumns, AddRoundKey를 순서대로 실행한다. 이것을 AES-128, 192, 256에 따라 각각 9번, 11번, 13번 반복한다.
  • 마지막 (10, 12, 14)번째 라운드: SubBytes, ShiftRows, AddRoundKey를 순서대로 실행한다.

4.2. KeyExpansion

위에서 언급한 것처럼 key schedule, 키 스케줄이라고도 부른다. 하나의 주 암호화 키로부터 많은 라운드 키들을 만들어 낸다. 주 키의 길이에 따라 총 라운드 수가 달라지므로, 만들어야 할 라운드 키의 갯수도 다르다. AES-128, 192, 256에 따라 각각 10개, 12개, 14개의 라운드 키를 만든다.

Rijndael 알고리즘은 라운드 키를 만들 때 32비트=4바이트=워드씩, 연속적으로 만든다. AES는 세 버전 모두 128비트의 블록 사이즈를 사용하므로, 하나의 라운드 키는 이 4바이트 워드를 네 개 뭉쳐서 만든다.[4] 그러므로 AES-128, 192, 256 버전은 각각 44, 52, 60개의 4바이트 워드를 만들어야 한다.

4.2.1. 개관

주 키의 길이를 32비트로 나눈 것을 [math(N)]이라 하자. 즉 주 키가 [math(N)]워드다. 예를 들어 AES-192라면 [math(N = 6)]일 것이다. 그리고 필요한 워드는 [math(4R)]이며 AES-192의 경우 [math(52)]워드다. 각 워드를 [math(W_0, W_1, ..., W_{4R-1})]라 할때 아래 규칙에 따라 생성된다.
  • i < N: W(i) = N(i)
  • i ≥ N, i ≡ 0 mod N: W(i) = W(i-N) XOR RotWord(SubWord(W(i-1))) XOR Rcon(i/N)
  • i ≥ N, N > 6, i ≡ 4 mod N: W(i) = W(i-N) XOR SubWord(W(i-1))
  • 그 외: W(i-N) XOR W(i-1)
즉 첫 N개의 워드는 주 키를 순서대로 그대로 가져다 쓴다. 그 다음부턴 N개 워드마다 순환하는데 첫 워드는 이전 워드에 RotWord, SubWord한 것, N칸 이전의 워드, Rcon(i/N) 3개를 XOR한 값으로 정해진다.
그리고 나머지 N-1개는 단순히 이전 워드와 N칸 이전 워드를 XOR한다.
예외로 i ≥ N, N > 6 and i ≡ 4 mod N인 경우[5]에는 W(i) = W(i-N) XOR SubWord(W(i-1))를 적용한다.

4.2.2. RotWord & SubWord

RotWord는 4바이트 워드를 바이트 단위로 한 칸 민(shift/rotate) 것이다. 즉 RotWord([89 ab cd ef]) = [ab cd ef 89]이다.
SubWord는 바이트 단위로 아래에 서술할 SubBytes를 실행하는 함수이다. 위의 예를 사용하면 SubWord([ab cd ef 89]) = [62 bd df a7]이 될 것이다.

4.2.3. Rcon, Round Constant

키를 더더욱 알기 어렵게 하기 위해 섞어주는 상수이다. 이름은 round constant이지만, 이 라운드는 AES의 큰 흐름에서 말하는 라운드와 다르다! 그 라운드는 4개의 워드를 만들 때마다(즉 128비트마다) 바뀌지만, 이 round constant는 N개의 워드를 만들 때마다 다음 값으로 바뀐다. 즉 W(i)를 구할 때에는 rcon(i/N)을 쓴다. 역시 4바이트 워드이며 값은 다음과 같이 주어진다.
  • rcon(k) = [rc(k) 00 00 00]
  • rc(1) = 0x01
  • If rc(k-1) < 0x80: rc(k) = rc(k-1) * 2
  • If rc(k-1) >= 0x80: rc(k) = (rc(k-1) * 2) XOR 0x11B

즉 아랫쪽 3바이트는 항상 0이며, 첫번째 바이트는 2배 하거나 2배 한 이후 0x11B와 XOR한 것이다. 계산해보면 첫번째 바이트는 0x01, 02, 04, 08, 10, 20, 40, 80, 1B, 36, ... 으로 주어진다.
Rcon은 N개의 워드를 만들때마다 바뀌므로, 필요한 라운드 키의 개수와는 다르다. 필요한 워드 수가 각각 44, 52, 60인 AES-128, 192, 256은 각각 4, 6, 8로 나누어보면 10, 8, 7번째까지의 Rcon이 필요함을 알 수 있다. AES-128의 경우 44라운드가 필요한데 10개의 Rcon만 쓰이는 이유는 첫 N개의 워드는 주 키를 그대로 가져다 쓰기 때문이다.

4.3. SubBytes

SubBytes 단계에서는 128비트블럭 안의 16바이트를 바이트 단위로 쪼개, 각 바이트마다 다른 내용으로 치환한다. 이때 미리 주어진 Rijndael S-Box (Substitution Box)를 사용한다.

아래의 표가 S-Box이다.
<colbgcolor=#ddccee,#211032> 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00 63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76
10 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0
20 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15
30 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75
40 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84
50 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF
60 D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8
70 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2
80 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73
90 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB
A0 E0 32 3A 0A 49 06 24 5C C2 d3 AC 62 91 95 E4 79
B0 E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08
C0 BA 78 25 2E 1C A6 B4 C6 E8 dD 74 1F 4B BD 8B 8A
D0 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E
E0 E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF
F0 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16

맨 왼쪽 열이 높은 4비트, 맨 위쪽 행이 낮은 4비트이다. 예를 들어, 0x4C는 40과 0C가 만나는 칸의 0x29가 된다.

4.3.1. InvSubBytes

Inverse SubBytes. SubBytes와 같지만 복호화를 할 때의 단계이다. 당연히 S-Box를 뒤집은 Inv S-Box를 쓴다.
<colbgcolor=#ddccee,#211032> 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00 52 09 6A D5 30 36 A5 38 BF 40 A3 9E 81 F3 D7 FB
10 7C E3 39 82 9B 2F FF 87 34 8E 43 44 C4 DE E9 CB
20 54 7B 94 32 A6 C2 23 3D EE 4C 95 0B 42 FA C3 4E
30 08 2E A1 66 28 D9 24 B2 76 5B A2 49 6D 8B D1 25
40 72 F8 F6 64 86 68 98 16 D4 A4 5C CC 5D 65 B6 92
50 6C 70 48 50 FD ED B9 DA 5E 15 46 57 A7 8D 9D 84
60 90 D8 AB 00 8C BC D3 0A F7 E4 58 05 B8 B3 45 06
70 D0 2C 1E 8F CA 3F 0F 02 C1 AF BD 03 01 13 8A 6B
80 3A 91 11 41 4F 67 DC EA 97 F2 CF CE F0 B4 E6 73
90 96 AC 74 22 E7 AD 35 85 E2 F9 37 E8 1C 75 DF 6E
A0 47 F1 1A 71 1D 29 C5 89 6F B7 62 0E AA 18 BE 1B
B0 FC 56 3E 4B C6 D2 79 20 9A DB C0 FE 78 CD 5A F4
C0 1F DD A8 33 88 07 C7 31 B1 12 10 59 27 80 EC 5F
D0 60 51 7F A9 19 B5 4A 0D 2D E5 7A 9F 93 C9 9C EF
E0 A0 E0 3B 4D AE 2A F5 B0 C8 EB BB 3C 83 53 99 61
F0 17 2B 04 7E BA 77 D6 26 E1 69 14 63 55 21 0C 7D

SubBytes에서와 마찬가지로, 0x29를 복호화 하면 0x4C가 된다.

4.4. ShiftRows

ShiftRows 단계에서는 128비트=16바이트 블럭을 4x4 바이트 행렬로 보고 각 행마다 왼쪽으로 민(shift/rotate)다. ShiftRows처럼 열을 서로 섞어주는 단계가 없다면 각각의 열이 서로 따로 따로 암호화되어, 4개의 블럭 암호화를 실행한 것과 마찬가지가 되어 좋지 않다.
16바이트를 행 우선으로 아래의 표처럼 배열한 다음, 두번째 줄은 왼쪽으로 한 칸, 세번째 줄은 두 칸, 네번째 줄은 세 칸 만큼 민다. 첫번째 줄은 변화가 없다.
즉 아래의 128비트 블럭은
0x00 0x01 0x02 0x03
0x04 0x05 0x06 0x07
0x08 0x09 0x0A 0x0B
0x0C 0x0D 0x0E 0x0F

ShiftRows를 실행한 후에는 이렇게 된다.
0x00 0x01 0x02 0x03
0x05 0x06 0x07 0x04
0x0A 0x0B 0x08 0x09
0x0F 0x0C 0x0D 0x0E

AES의 경우는 128비트 블럭만을 사용하지만, 더 큰 블럭을 갖는 Rijndael 알고리즘에서는 열의 수가 달라지며 미는 바이트의 수도 달라진다.

4.4.1. InvShiftRows

Inverse Shift Rows. 역시 복호화 할 때 필요한 단계이다.
4x4 행렬을 만든 후 두 번째 줄은 왼쪽으로 3번, 세 번째 줄은 2번, 네 번째 줄은 1번 민다.

4.5. MixColumns

마찬가지로 4x4 행렬을 만든 뒤, 이번에는 열 단위에서 섞어주는 단계이다. 식은 보기 편하게 다음처럼 행렬로 주어진다.

a0 * [2 3 1 1] = r0
a1 * [1 2 3 1] = r1
a2 * [1 1 2 3] = r2
a3 * [3 1 1 2] = r3

여기서 r값들을 구하려면, 이런 식으로 해야한다.

r0 = (a0 * 2) + (a1 * 3) + (a2 * 1) + (a3 * 1).
r1 = (a0 * 1) + (a1 * 2) + (a2 * 3) + (a3 * 1).
r2 = (a0 * 1) + (a1 * 1) + (a2 * 2) + (a3 * 3).
r3 = (a0 * 3) + (a1 * 1) + (a2 * 1) + (a3 * 2).

다만 이때 덧셈은 평범한 덧셈이 아니며, XOR을 사용해야 한다. 마찬가지로 곱셈도 덧셈이 XOR인 것에 맞게 계산해 줄 필요가 있다. 만약 더하다가 OverFlow가 발생하면 0x1b와 XOR을 해 주면 된다
C언어로 표현하면 이렇게 된다.
#!syntax cpp
typedef unsigned char byte;
void rijndael_mixcolumn (byte * data, size_t data_len) {
	if ((data_len % 4) != 0)
		return;

	for (size_t i = 0 ; i < data_len ; i += 4) {
		byte copy_arr [4], res [4];

		memcpy (copy_arr, data + i, 4);

		res [0] = (data [0 + i] << 1) ^ (0x1B & ((byte) ((signed char) data [0 + i] >> 7)));
		res [1] = (data [1 + i] << 1) ^ (0x1B & ((byte) ((signed char) data [1 + i] >> 7)));
		res [2] = (data [2 + i] << 1) ^ (0x1B & ((byte) ((signed char) data [2 + i] >> 7)));
		res [3] = (data [3 + i] << 1) ^ (0x1B & ((byte) ((signed char) data [3 + i] >> 7)));

		data [0 + i] = res [0] ^ copy_arr [3] ^ copy_arr [2] ^ res [1] ^ copy_arr [1];
		data [1 + i] = res [1] ^ copy_arr [0] ^ copy_arr [3] ^ res [2] ^ copy_arr [2];
		data [2 + i] = res [2] ^ copy_arr [1] ^ copy_arr [0] ^ res [3] ^ copy_arr [3];
		data [3 + i] = res [3] ^ copy_arr [2] ^ copy_arr [1] ^ res [0] ^ copy_arr [0];
	}
}


이 과정을 미리 계산해서 Table-lookup에 저장해 놓고 계산하는 방법도 있으며, 모든 AES 최적화에는 이러한 Table-lookup방식을 적용한다.[6]

마지막 라운드에서는 MixColumns가 없다는 걸 잊지 말자.

4.5.1. InvMixColumns

Inverse Mix Columns. 역시 복호화 할때 사용한다.

[math(S' = \begin{bmatrix}
14 & 11 & 13 & 9\\
9 & 14 & 11 & 13\\
13 & 9 & 14 & 11\\
11 & 13 & 9 & 14
\end{bmatrix} \times S)]

4.6. AddRoundKey

드디어 KeyExpansion 단계에서 만든 라운드 키를 쓸 때가 되었다.
128비트 블럭에 128비트 라운드 키(네 워드를 이어붙여 만든)를 XOR한다.
XOR의 특성 때문에, AddRoundKey 단계는 복호화하는 데 굳이 별도의 역 공식이 필요하지 않고, 같은 라운드 키로 XOR을 한 번 더 하기만 하면 복호화가 된다.

AddRoundKey는 0라운드, 1~(9,11,13), (10,12,14) 라운드에서 실행하므로, 위에서 언급했듯 총 (11, 13, 15)개의 라운드 키가 필요하다.

4.7. 이 외

4.7.1. BASE64 인코딩

BASE64는 이진 데이터를 플레인 텍스트로 인코딩하는 하나의 방식일 뿐 본래 AES 암호화 알고리즘과는 관련이 없다. 다만 AES 암호화된 결과 데이터는 이진 데이터이기 때문에 데이터 교환 시 불편한 경우가 많아 대부분 암호화된 결과 데이터를 BASE64로 인코딩해서 나온 플레인 텍스트를 사용한다. 물론 BASE64로 인코딩 했다면 복호화 이전에 BASE64 디코딩이 필요하다.

4.7.2. PKCS#7 패딩

AES가 블럭 암호화 알고리즘인 만큼 다른 모든 블럭 암호화 알고리즘처럼 패딩(padding)하여 블럭의 빈 자리를 채울 필요가 있다. 일반적으로 PKCS#5와 PKCS#7에 정의된 패딩 알고리즘이 널리 사용된다.
PKCS#7 패딩 알고리즘은 블럭의 크기가 k바이트(k는 1~255)일 때 패딩하고자 하는 데이터의 바이트 단위 길이가 k의 배수가 되는데 필요한 나머지 바이트 개수를 n이라 하면 n이라는 값을 n개 덧붙인다. 단, 바이트 단위 길이가 딱 k의 배수이더라도 k라는 값을 k개 덧붙인다.[7]

코드로 표현하자면 다음과 같다.
#!syntax javascript
const k = /* 1 ~ 255 */; // blockSize
const n = k - baseDataLen % k; // padSize == padValue

예를 들어 원문이 아래와 같고, 블럭의 크기가 128비트(16바이트)라 하면,
01 02 03 04 05 06 07 08 01 02 04 08 10 20 40 80 00 11 22 33 44 55 66 77 88 99 AA
총 27바이트이므로 16의 배수인 32바이트가 되려면 5바이트가 부족하다.
01 02 03 04 05 06 07 08 01 02 04 08 10 20 40 80 00 11 22 33 44 55 66 77 88 99 AA
따라서 바이트 0x055개만큼 뒤에 덧붙여서 아래와 같이 두 블럭을 만든다.
01 02 03 04 05 06 07 08 01 02 04 08 10 20 40 80 00 11 22 33 44 55 66 77 88 99 AA 05 05 05 05 05

PKCS#5 패딩은 PKCS#7 패딩과 구현 방식이 같지만 블럭의 크기가 8바이트로 고정된 것이다. AES는 128비트(16바이트) 블럭 알고리즘이기 때문에 사실 PKCS#5 패딩을 쓸 일이 없다. 그러나 PKCS#7 패딩과 구현 방식이 같아 보통 같은 구현을 돌려쓰는데다, Java에서 'AES/CBC/PKCS5Padding' 표기법만 허용하고 'AES/CBC/PKCS7Padding' 표기법은 지원하지 않다보니 오히려 AES에 PKCS#5 패딩을 쓰는 것이 맞는 줄 아는 경우가 많다.

5. 안전성

미국 정부가 기밀문서 암호화 처리에 이것을 채택했다. 미국 정부 표준 암호화 알고리즘이기도 하다. 즉 미국 정부가 믿고 이 기술을 사용할 정도라는 것이다. 현존 암호화 알고리즘 기술 중 AES가 최강의 암호화 알고리즘이고, 키없이 해독하는 것이 거의 불가능하다고 평가되고 있다. 심지어 다른 최신 cipher와 마찬가지로, known-plaintext 조건에서도 해독이 불가능하다고 한다.

소수를 사용한 암호화, ECDLP(Elliptic Curve Discrete Logarithm Problem)의 특성을 이용한 공개키 암호화 방식 등이 양자컴퓨터에 취약할 가능성을 보이지만, SPN(Substitution Permutation Network)을 기반으로 하는 AES의 특성상 양자컴퓨터에도 안전하다.

컴퓨팅 기술의 급속한 발전에 따라 현재 권장되는 암호화 수준은 192비트 이상이며 대다수의 금융기관이나 웹사이트들은 256비트 이상의 암호화 체계로 전환했다.

컴퓨터 파일이나 각종 디스크 암호화에도 매우 적절한 암호화 방식이다. 특히, 널리 사용되는 표준 암호화 방식이라 하드웨어 지원이 빵빵해서 요즘 하드웨어 정도되면 암호화 되지 않은 디스크와 성능차가 느껴지지 않을 수준이다. 유명한 암호화 프로그램 VeraCrypt마이크로소프트BitLocker가 대표적으로 사용된다. 암호나 키 파일이 털리지 않는 이상 디지털 포렌식을 하든 슈퍼컴퓨터를 가져오든 복호화가 불가능하다.

6. 프로그래밍 언어 API

자바와 같은 JVM 기반 언어는 java.security 패키지와, javax.crypto 패키지를 사용하여 AES 암호화를 사용할 수 있다. JSP 역시 자바 기반이기때문에 사용 가능. 자바 예제 코드.

C/C++는 기본 라이브러리에 포함되어 있지 않으나, 다양한 서드파티 라이브러리를 사용할 수 있다.

PHP에서는, 설치된 PHP 런타임이 OpenSSL이나 mcrypt[8] 확장의 지원을 포함하고 있는 경우 openssl_encrypt() 또는 mcrypt_encrypt() 메소드를 불러 AES를 사용할 수 있다. 예시 만약 해당 확장이 설치되어 있지 않다면, phpseclib 등의 외부 라이브러리를 이용해야 한다.

C# 등의 Microsoft .NET 환경은 System.Security.Cryptography 네임스페이스와 System.Security.Cryptography.Aes 클래스를 사용하면 된다. 설명은 MSDN 참조.

Python은 외부 라이브러리를 통해 지원한다. pycryptodome 모듈을 사용할 수 있다. 더 이전에 만들어진 pycrypto라는 모듈도 있지만, 업데이트가 안된지 오래되었고 Python 3.X 상위 버젼에서 해당 모듈 설치시 에러를 뿜어내며 설치가 되지 않는다.

Go 언어는 표준 라이브러리로 제공된다. "crypto/aes" 및 "crypto/cipher"(모드 구현) 패키지를 이용하면 된다. ECB 모드는 1블록(128비트)씩 cipher.Block의 Encrypt/Decrypt 함수를 직접 호출하여 이용 가능하다.

JavaScript의 경우 Web Crypto API(SubtleCrypto)에서 AES 암호화를 지원한다. 단, 보안성이 떨어지는 ECB 모드를 제외하고 CBC, CTR, GCM 모드만 지원한다.

Node.js에서는 crypto 모듈에서 OpenSSL을 기반으로 AES 암호화를 지원한다. 또한 Node.js 19부터 Web Crypto API를 정식 지원한다. Deno에서도 1.18 버전부터 Web Crypto API를 완전히 지원한다.

광범위하게 사용되는 암호화 방식이다 보니, 많은 CPU에서 AES 암/복호화의 하드웨어 가속과 이를 위한 명령어를 도입한 상태이다.
  • x86 어셈블리에는 인텔에서 개발한 AES 암복호화 하드웨어 가속용 확장 명령어셋인 "AES-NI(New Instructions)"가 존재한다. 인텔의 경우 웨스트미어에서 처음 도입하였으나 당시에는 서버용 Xeon과 Core i5 이상 제품군에만 탑재되었다. 그러던 것이 하스웰부터는 Core i3 이상부터, 스카이레이크부터는 모든 프로세서에서 지원한다. AMD의 경우 불도저 이후의 모든 프로세서에서 AES-NI를 지원한다.
  • Arm에서는 Armv8-A부터 AES 하드웨어 가속 명령어셋을 지원한다(FEAT_AES).
  • POWER의 경우 POWER7+부터 "Vector Facility"(구 VMX/AltiVec)에서 AES 하드웨어 가속 명령어셋을 정의하고 있다.

7. 기타

Rijndael 알고리즘의 이름은 알고리즘을 만든 두 벨기에 암호학자 빈센트 레이먼(Vincent Rijmen)과 조앤 대먼(Joan Daemen)의 이름에서 따온 것이다.
AES에 대해 설명해주는 만화(영문)

8. 관련 문서




[1] 이런 성능 대비 안정성이 매우 뛰어나다는 점이 널리 사용되는 주요 원인이라 볼 수 있다. AES보다 강력한 암호화 방식은 널렸지만 암호화, 복호화 과정에서 사용되는 리소스를 생각하면 효율성에서 AES를 이기는 암호화 방식은 거의 없다. 아무리 강력해도 처박아두기만 하는 용도의 콜드 데이터가 아닌 이상은 읽고 쓰는데 하루종일 걸린다면 써먹기는 힘들 것이고, 암호화가 느려 터져서 랜섬웨어로도 사용하지 않을 것이다.[2] 90년대 당시의 몇몇 슈퍼 컴퓨터들을 사용하여 하루 정도의 시간을 쓰면 DES 암호를 충분히 풀어낼 수 있었으며 현재의 인텔 9900k 기준으로 대략 반나절이면 풀수있다. 계산법은 당시 싱글 400MHz CPU 128개 병렬연산 기준으로 25.4시간이 걸렸기에 9900K 8코어 16스레드를 10코어로 계산할경우 대략 반나절이 나온다.[3] 의 128비트 블록, 128, 192, 256비트 키 버전[4] Rijndael 알고리즘을 쓰지만 블록 사이즈가 128비트가 아닌 경우에는 필요한 워드의 갯수가 다르다.[5] AES표준에서 N>6인 경우는 N=8인 AES-256만 존재한다.[6] 이는 당연히 소프트웨어적인 최적화로, 하드웨어에서의 최적화 방식과는 다르다. 대부분의 암호 알고리즘의 최적화에서 입력값에 따라 출력값의 연산을 수행하는 것이 아니라, 테이블에서 불러오는 방식으로 적용가능하다.[7] 마지막에 01이나 02 02 등 padding으로 오인될 수 있는 값이 붙는 경우를 예방하기 위함이다.[8] 2007년 이후로 업데이트가 끊긴 라이브러리로, 보안 취약성이 우려되어 PHP 7.2부터는 지원이 삭제되었다