(MATLAB) 양방향 암호화 (AES, .NET)

암호화 및 복호화 가능한 AES 알고리즘을 MATLAB에서 활용하는 두 가지 방법에 대해 소개합니다.

순수 MATLAB 코드

첫 번째는 David Hill 님이 MathWorks 공식 홈페이지에 올려준 MATLAB 코드로 작성된 AES 알고리즘을 활용하는 방법 입니다.

Advanced Encryption Standard (AES)-128,192, 256

이 프로그램은 Cipher, InvCipher를 포함한 여러개의 함수로 구현되어 있으며, 적당한 Key 값과 Input 문자를 입력하면 암호화 된 결과를 return하는 구조입니다.

저자가 함수 사용법을 간단하게 잘 정리해 두었기 때문에, 따로 자세한 설명은 생략합니다.

다만, 저자의 설명만으로 구현이 어려운 분들은 이 함수들을 활용하여 암호화 및 복호화를 테스트 할 수 있는 코드를 아래의 링크를 통해 공유합니다.

matlab/encrypt_AES at master · chopthal/matlab

이 어플리케이션은 UseCipher.m 이라는 하나의 함수 내에 모든 함수들을 합치고, 키 값을 감추기 위해 p코드로 변환하였습니다. 원본 m파일은 origin 폴더에서 확인할 수 있습니다.

AES 알고리즘에 대한 설명은 아래의 링크로 대신합니다.

Crocus

David Hill이 AES 알고리즘을 MATLAB 함수로 구현해 준 덕분에 MATLAB 단일 코드만으로 암호화 시퀀스를 구현할 수 있게 되었습니다. 또한 행렬 연산에 최적화 된 MATLAB 프로그램의 특성 때문인지, 아래 소개할 두 번째 방법에 비해 단위 문자열 (16, 32 문자)의 암호화 및 복호화 속도가 빠른 장점을 갖습니다.

다만, 이 함수의 단점은 Cipher 함수가 한 번에 처리할 수 있는 문자가 정해져 있으므로 (16 또는 32글자), 많은 수의 문자를 암호화 및 복호화 하기 위해서는 반복문을 사용하는 것이 불가피 합니다. 비밀번호와 같은 짧은 문장을 암호화 하기에는 매우 효율적이지만 장시간 로깅된 데이터를 처리하는 등 글자 수가 많은 경우에는 적용이 어려울 수 있습니다. 예를 들어, 16글자를 암호화 하는데 약 6 ms의 시간이 소요되며, 16 MB의 데이터를 암호화 하기 위해서는 6,000 초 이상이 필요합니다.

.NET class 활용

이전 SHA256 암호화 게시물에서 간단하게 소개했던 것과 같이, MATLAB에서 .NET의 속성들을 일부 활용하는 것이 가능합니다.

(MATLAB) SHA256 암호화 기반 Login 기능 구현

MATLAB의 .NET 속성

소개할 두 번째 방법을 통해 .NET의 Aes, MemoryStream, CryptoStream 클래스를 통해 반복문을 사용하지 않고 장문의 텍스트를 짧은 시간에 암호화 및 복호화 할 수 있습니다.

이 방법에서 소개하는 코드는 아래의 Microsoft 공식 문서와 RijndaelManaged 알고리즘을 c#으로 구현한 개인 블로그 게시물을 참고하여 작성하였습니다.

Aes 클래스 (System.Security.Cryptography)

C# AES 암호화

주의해야 할 점은,

  • c#의 using 문법을 사용할 수 없기 때문에 stream을 닫는 과정에서 오류가 발생할 수 있어, 메모리 누수가 발생할 수 있습니다.
  • Encoding, Convert 과정에서 문자열이 소실될 수 있습니다. 이 부분은 아직 이해도가 부족하여 향 후 개선 예정입니다.
  • 암호화 → 복호화를 거친 후의 결과 문장에는 blank (\0, null) 문자들이 포함되어 있어 암호화 이전의 원본 문장과 완벽하게 일치하지는 않습니다.

June 9, 2022 위 문제점들은 해결되었습니다.

  • stream.Close() 를 추가 하였습니다. 기존 에러는 Decrypt 객체에서 padding을 PKCS7에서 None으로 변경하여 해결하였습니다.
  • 문자열 소실은 지원되는 아스키 코드 범위를 넘어서는 문자를 입력할 때 발생했던 문제로, dummy 데이터를 추가하는 코드를 제거하였습니다.

암호화 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function output = AesEncrypt(input, key, iv)
import System.*;
import System.Text.*;
import System.IO.*;
import System.Security.Cryptography.*;

aesAlg = Aes.Create();
aesAlg.Key = Encoding.UTF8.GetBytes(key);
aesAlg.IV = Encoding.UTF8.GetBytes(iv);
aesAlg.Padding = PaddingMode.PKCS7;

encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
msEncrypt = MemoryStream();
csEncrypt = CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
xXml = Encoding.UTF8.GetBytes(input);
csEncrypt.Write(xXml, 0, xXml.Length);
buf = msEncrypt.ToArray();
output = Convert.ToBase64String(buf, 0, buf.Length, Base64FormattingOptions.None);
output = char(output);

csEncrypt.Close; msEncrypt.Close;
end

복호화 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function output = AesDecrypt(input, key, iv)
import System.*;
import System.Text.*;
import System.IO.*;
import System.Security.Cryptography.*;

aesAlg = Aes.Create();
aesAlg.Key = Encoding.UTF8.GetBytes(key);
aesAlg.IV = Encoding.UTF8.GetBytes(iv);
aesAlg.Padding = PaddingMode.None;

decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
msDecrypt = MemoryStream();
csDecrypt = CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);
xXml = Convert.FromBase64String(input);
csDecrypt.Write(xXml, 0, xXml.Length);
buf = msDecrypt.ToArray();
tmp = Encoding.UTF8.GetString(buf);
output = char(tmp);

csDecrypt.Close; msDecrypt.Close;
end
  • System.Security.Cryptography 클래스 내 Aes 객체 상속
  • MemoryStream, CryptoStream 객체를 가져와 input 문자열을 암호화
  • 암호화 결과를 String으로 변환한 후 MATLAB 함수인 char를 이용하여 MATLAB string으로 변환

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
% 현재 디바이스의 .NET 지원 여부 확인
if ~NET.isNETSupported
disp('#NET is not supported in this system.')
return
end

% System.String 사용을 위한 import
import System.*;

key = "00000000000000000000000000000000"; % length : 32
iv = "0000000000000000"; % length : 16

% 길이 20의 랜덤 문자열 생성
str = char(floor(rand(1, 20) * (double('z') - double('A') + 1) + double('A')));
% 문자열 개수가 16의 배수가 아닌 경우 뒤에 blank (' ')를 채움
modStr = mod(length(str), 16);
if modStr > 0
str = [str blanks(16 - modStr)]; % Filling strings for encryption
end
disp(str);
input = String(str);
encrypted = AesEncrypt(input, key, iv);
disp(encrypted);
input = String(encrypted);
decrypted = AesDecrypt(input, key, iv);
disp(decrypted)

실행 결과

  • 원본 문자 → 암호화 → 복호화 (원본 문자)로 변환하는데 성공 하였습니다.

  • 전체 소스코드는 아래 링크를 참조하세요.
    matlab/aes_dotnet at master · chopthal/matlab

  • 첫 번째 방법에서 예로 든, 16 MB 문자의를 처리하는 경우 약 0.5초 이내에 암호화 및 복호화가 수행됩니다.

  • 16개 문자를 처리하는데 걸리는 시간은 약 10 ms로 첫 번째 방법 (6 ms)에 비해 오래 걸립니다.

  • 실제로 16개 문자를 처리할 때와 10,000개 문자를 처리할 때 10 ms 내외로 시간이 소요됩니다.

  • 따라서, 긴 문장을 처리하는 경우, MATLAB의 반복문을 도는 시간을 MemoryStream을 통해 단축시킴으로써 많은 시간을 단축할 수 있습니다.