aes-256-cbc 복호화 여정

juha·2025년 5월 25일

new_knowledge

목록 보기
1/2
post-thumbnail

aes-256-cbc란?

aes

  • 고급 암호화 표준(Advanced Encryption Standard, AES)은 2001년 미국 표준 기술 연구소(NIST)에 의해 제정된 암호화 방식
  • 암호화와 복호화 과정에서 동일한 키를 사용하는 대칭 키 알고리즘
  • AES는 ISO/IEC 18033-3 표준에 포함되어 있으며 여러 암호화 패키지에서 사용

256

  • 256 비트 알고리즘

cbc

  • aes 알고리즘과 함께 사용되는 블록 암호 모드
  • Cipher Block Chaining의 약자
  • AES처럼 데이터를 일정한 크기의 블록으로 나눠서 암호화하는 '블록 암호' 방식에서 사용하는 여러 가지 '운용 모드(Operating Mode)' 중 하나
  • 각 블록을 암호화할 때 바로 이전에 암호화된 블록의 암호문과 현재 평문 블록을 XOR 연산하는 방식
  • 맨 처음 블록을 암호화할 때는 '초기화 벡터(IV, Initialization Vector)'라는 값을 사용

암호화 방법

aes-256-cbc 생성해보기

# 예 1
echo password | openssl aes-256-cbc -k key -e -p -pbkdf2 -out output && cat output

# -p 옵션: salt, key, iv 값 프린트
salt=A02E8E4135B862BA
key=DB96C3D6AADA30923798183CCBBAD7E014434429B6084AF0A07DC739F5D57D26
iv =AB6CF2CA1740B6C97FD4E6C2478376C5

# 실질적으로 만들어진 aes
Salted__�.�A5�b��0�9L�▒]���f4��%  

옵션 설명

  • k : passphrase (긴 비밀번호)
  • e : 암호화 (encrypt)
  • p : print iv, key
  • pbkdf2 : Password-Based Key Derivation Function 2의 약자
    - 사용하는 비밀번호나 passphrase 같은 걸 이용해서 암호화에 사용할 '키'를 안전하게 만들어내는 함수
  • out : outfile 생성

aes-256-cbc + base64

  • 위에서 보듯이 aes는 보기 어렵고 다른데다 저장하기 어렵다. 따라서 해당 암호를 옮기기가 쉽지 않아 유저들이 들고다니기 좋도록 base64로 인코딩을 한 번 한다.

옵션 설명

  • a : base64 인코딩
# 예 2
echo password | openssl aes-256-cbc -k forty2 -e -p -pbkdf2 -out output -a && cat output
salt=BB85AD94FA0A73AD
key=1D35324FA0770B96974BF63BEF75C292828136ECA5BEABAAC8F0A38EB6AF1CFF
iv =DF24AE7B9DD0C7955863FCD562816E02

# base64로 변환한 aes
U2FsdGVkX1+7ha2U+gpzrR2SXAj8zrwnrGv988jlrXY=

복호화 하기

  • PBKDF2_ITER 의 openssl 기본 값은 1만번이므로 define 해준다.
  • aes 값은 헤더인 "salted__" + salt[8] + ciphertext 생성
  • passphrase + salt를 통하여 iv[16] + key[32] 로 분리 하여 복호화
  • IV : Initialization Vector 즉, 초기화 벡터 - 암호화의 보안을 강화하기 위해 사용하는 난수
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

// openssl라이브러리 사용
#include <openssl/evp.h>
#include <openssl/aes.h>

#define PBKDF2_ITER 10000 //opensssl default value is 10000
#define IV_LEN 16
#define KEY_LEN 32
#define AES_HEADER_LEN 8
#define SALT_LEN 8

void decode_aes_pbkdf2(unsigned char *aes_256_cbc, int aes_len, unsigned char *passphrase, char *password_buf) {
    unsigned char salt[8] = {0};
    unsigned char buf[4096] = {0};
    unsigned char key[KEY_LEN] = {0};
    unsigned char iv[IV_LEN] = {0};
    int len = 0;
    int total_len = 0;
    int header_and_salt_len = AES_HEADER_LEN + SALT_LEN;
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();

    memcpy(salt, aes_256_cbc + 8, 8);
    // saled__ = 8
    PKCS5_PBKDF2_HMAC(passphrase, strlen(passphrase), salt, strlen(salt), PBKDF2_ITER, EVP_sha256(), IV_LEN + KEY_LEN, buf);

    memcpy(key, buf, KEY_LEN);
    memcpy(iv, buf + KEY_LEN, IV_LEN);

    if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) {
        strcpy(buf, "EVP init error check salt and passphrase\n");
        goto aes_error;
    }

    if (!EVP_DecryptUpdate(ctx, (unsigned char *)password_buf, &len, \
        aes_256_cbc + header_and_salt_len, aes_len - header_and_salt_len)) {
        strcpy(buf, "decrypt update error \n");
        goto aes_error;
    }
    total_len = len;
    if (!EVP_DecryptFinal_ex(ctx, (unsigned char *)password_buf + len, &len)) {
        fprintf(stderr, "decrypt text : %s \n", password_buf);
        strcpy(buf, "decrypt final error\n");
        goto aes_error;
    }
    total_len += len;
    password_buf[total_len] = 0;
    EVP_CIPHER_CTX_free(ctx);
    
    // check error
    printf("create decode aes file\n");
    int fd = open("c_aes", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    write(fd, password_buf, strlen(password_buf));
    close(fd);
    return ;

aes_error:
    EVP_CIPHER_CTX_free(ctx);
    password_buf[0] = 0;
    fprintf(stderr, buf);
}


void decode_pwd(char *base64_pwd, unsigned char *plaintext) {
    int len;
    unsigned char aes[4096] = {0};

    len = strlen(base64_pwd);
    fprintf(stderr,"base 64 len is %d in pwd\n", len);
    if (len < 1) {
        fprintf(stderr, "DB_PWD empty in sysconfig\n");
        printf("input : %s\n", base64_pwd);

        plaintext[0] = 0;
        return;
    }
    len = EVP_DecodeBlock(aes, base64_pwd, len);
    if (len < 1) {
        fprintf(stderr, "Decode fail\nCheck password\n");
        plaintext[0] = 0;
        return;
    }
    fprintf(stderr, "full:%6s|hex:%X\n", aes,aes + strlen("salted__"));

    // printf("create decode file");
    // int fd = open("c_base64", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    // write(fd, aes, len -1);
    // close(fd);
    decode_aes_pbkdf2(aes, len - 1, "forty2", plaintext);
}


int main() {
    int fd;
    char *base64 = "U2FsdGVkX1+7ha2U+gpzrR2SXAj8zrwnrGv988jlrXY=";
//    char base64[1024]={0};
    int len;
    char plaintext[1024] = {0};

//    memset(base64, 0x00, 1024);
//    fd = open("enc", O_RDONLY);
//    len =read(fd, base64, sizeof(base64));
//    printf("%d\n[%s]\n", len, base64);
//    for (int i=0; i < len; i++)
//        printf("%d:[%c]\n", i, base64[i]);
//    printf("\n%d\n\n[%s]", len, base64);
//    if (1 > read(fd, base64, sizeof(base64))) {
//        perror("read err");
//        close(fd);
//        return 0;
//    }
//    close(fd);

    printf("read: %s|len:%d\n",base64 ,strlen(base64));
    decode_pwd(base64, plaintext);
    printf("Decoded password: %s[padding]\n", plaintext);


}

reference

profile
재미있는 것을 하는 개발자

0개의 댓글