본문 바로가기

Personal Posting/Flutter

Flutter Data ENCRYPT & DECRYPT (플러터 데이터 암호화/복호화)

1. 암호화 / 복호화에 대한 정리

암호화(Encryption): 평문 데이터를 인가되지 않은 사람이 읽지 못하도록 암호화된(암호문) 형태로 변환하는 과정

복호화(Decryption): 암호화된 데이터를 다시 원래의 평문 데이터로 되돌리는 과정

 

2. Flutter에서 지원하는 암호화 알고리즘

알고리즘 특징
AES 정부, 금융기관 등에서 널리 사용하는 대칭키(비밀키) 암호화 방식. 빠르고 안전함.
Fernet 대칭키 방식. 128비트 AES CBC 모드와 HMAC-SHA256 인증을 결합하여 데이터 변조 방지.
Salsa20 스트림 암호화 방식. 빠르고 안전하며, 다양한 분야에서 사용됨.

3. Flutter에서 구현

1) encrypt 패키지 설치

# pubspec.yaml
encrypt: ^5.0.3 # 현 시점 최신버전

 

2) 사용하는 코드에서 import

import 'package:encrypt/encrypt.dart';

 

3) 예제코드 구현

import 'package:encrypt/encrypt.dart';

class EncryptData {
  static Encrypted? aesEncrypted;
  static String? aesDecrypted;

  // AES 암호화
  static void encryptAES(String plainText) {
    final key = Key.fromUtf8('my 32 length key................'); // 32바이트 키
    final iv = IV.fromLength(16); // 16바이트 IV
    final encrypter = Encrypter(AES(key));
    aesEncrypted = encrypter.encrypt(plainText, iv: iv);
    print(aesEncrypted!.base64); // 암호문 출력
  }

  // AES 복호화
  static void decryptAES(String plainText) {
    final key = Key.fromUtf8('my 32 length key................');
    final iv = IV.fromLength(16);
    final encrypter = Encrypter(AES(key));
    aesDecrypted = encrypter.decrypt(aesEncrypted!, iv: iv);
    print(aesDecrypted); // 복호화된 평문 출력
  }
}
  • Key: 32바이트(256비트) 길이의 문자열이어야 AES-256이 정상 동작한다.
  • IV(초기화 벡터): 16바이트 길이로 지정한다.
  • 암호화된 데이터는 base64 형태로 출력된다.

4) Fernet 암호화/복호화 예제

import 'package:encrypt/encrypt.dart';
import 'dart:convert';

void main() {
  final plainText = '암호화할 평문 데이터';

  // 32바이트 길이의 키를 생성
  final key = Key.fromUtf8('my32lengthsupersecretnooneknows1');

  // Fernet은 base64 인코딩된 키를 사용해야 함
  final b64key = Key.fromUtf8(base64Url.encode(key.bytes).substring(0,32));

  // Fernet 알고리즘 객체 생성
  final fernet = Fernet(b64key);

  // Encrypter 객체 생성
  final encrypter = Encrypter(fernet);

  // 암호화
  final encrypted = encrypter.encrypt(plainText);
  print('암호문(base64): ${encrypted.base64}');

  // 복호화
  final decrypted = encrypter.decrypt(encrypted);
  print('복호화된 평문: $decrypted');

  // 암호화된 데이터의 타임스탬프 추출
  print('타임스탬프: ${fernet.extractTimestamp(encrypted.bytes)}');
}
  • Fernet은 AES-128 CBC + HMAC-SHA256 조합으로, 키를 base64로 인코딩하여 사용한다.
  • 사용법은 AES와 유사하며, 암호화/복호화 시 별도의 Fernet 객체를 생성한다.

5) Salsa20 암호화/복호화 예제

import 'package:encrypt/encrypt.dart';

void main() {
  final plainText = '암호화할 평문 데이터';

  // 32바이트 길이의 랜덤 키와 8바이트 IV 생성
  final key = Key.fromSecureRandom(32);
  final iv = IV.fromSecureRandom(8);

  // Salsa20 알고리즘 객체 생성
  final encrypter = Encrypter(Salsa20(key));

  // 암호화
  final encrypted = encrypter.encrypt(plainText, iv: iv);
  print('암호문(base64): ${encrypted.base64}');

  // 복호화
  final decrypted = encrypter.decrypt(encrypted, iv: iv);
  print('복호화된 평문: $decrypted');

  // 암호화된 데이터의 바이트, base16, base64 출력
  print('암호문(bytes): ${encrypted.bytes}');
  print('암호문(base16): ${encrypted.base16}');
}
  • Salsa20은 스트림 암호화 방식으로, 대용량 데이터나 실시간 암호화에 적합합니다.
  • 사용법은 AES와 비슷하나, 알고리즘 객체만 변경하면 됩니다.

4. 주의사항

  • Key와 IV는 반드시 동일해야 복호화가 제대로 동작한다.
  • Key 길이가 16, 24, 32바이트 중 하나여야 하며, 그렇지 않으면 오류가 발생한다.
  • 암호화/복호화에 사용하는 알고리즘, 키, IV는 반드시 보안에 유의해서 관리해야 한다.
  • 실제 서비스에서는 키와 IV를 코드에 하드코딩하지 않고, 안전한 저장소(예: 환경변수, secure storage 등)에 보관하는 것이 좋습니다. 개인적으로는 .env를 주로 사용하고 있다. (https://honken.tistory.com/168 참고)

5. 결론

Flutter에서 데이터 암호화와 복호화는 encrypt 패키지를 이용해 손쉽게 구현할 수 있으며, AES, Fernet, Salsa20 등 다양한 알고리즘을 지원하므로, 서비스 특성에 맞게 선택하여 사용할 수 있다. 앱의 민감 정보 보호와 네트워크 보안을 위해 반드시 암호화/복호화 기능을 적용하는 것이 권장한다.