본문 바로가기
Programming

Node.js crypto 비대칭키 대칭키 구현하기

by 개발자 염상진 2022. 9. 2.

암호화는 프로그램에서 굉장히 중요한 분야고 계속해서 연구가 진행되고 있습니다. 많은 연구자들이 좀더 안전한 암호화 알고리즘을 개발하고 있고, 현재 우리는 알게 모르게 그들의 결과물을 사용하고 있습니다.

예를 들어 AWS에 내가 만든 app을 배포하려고 할 때 사용하는 pem key는 대표적인 비대칭키로 양방향 암호화 알고리즘 RSA를 사용합니다. 암호된 정보의 기본 요소는 다음 4가지가 되겠습니다.

 

 

 

 

 

정보의 기능

 

1. 기밀성(Confidentiality)

정당한 권한이 있는 사용자는 데이터의 내용을 확인할 수 있어야 합니다.

2. 무결성(Integrity)

정보가 전달되는 과정에서 데이터가 위변조 되어서는 안됩니다.

3. 가용성(Availability)

정해진 기간 동안 데이터를 사용할 권한이 있는 사용자는 정보를 사용할 수 있어야 합니다.

4. 부인봉쇄(Non-repudiation)

수신자 혹은 송신자는 정보가 전달된 후 본인이 보내지 않은 데이터라고 부인할 수 없어야 합니다. 

 

 

 

 

암호와 알고리즘의 종류

 

정보를 암호화 하는 알고리즘에는 크게 단방향 알고리즘과 양방향 알고리즘으로 구분됩니다. 우선 단방향 알고리즘은 평문을 암호문으로 변경할 수 있지만 그 반대는 불가능한 알고리즘입니다. 대표적으로 해시 함수가 단방향 암호 방식입니다. 

해시 함수를 이용한 암호화 알고리즘에는 SNEFRU, MD5, SHA512, SHA256 등이 있습니다. 

두번째로 양방향 알고리즘은 평문에서 암호문으로 변경할 수 있고, 암호문을 평문으로 변경할 수 있습니다. 양방향 암호화 알고리즘에는 대칭키와 비대칭키가 있습니다.

대칭키

대칭키는 암호화 키와 복호화 키가 동일하게 사용됩니다. 대칭키는 크게 블록암호화 스트림 암호로 구분됩니다. 블록 암호는 평문을 일정한 단위로 쪼개 단위마다 암호화를 진행하는 방식입니다. 기본적으로 혼돈이론과 확산이론을 바탕으로 하고 있습니다. 대표적인 대칭키 블록암호 알고리즘으로는 DES, SEED, AES, ARIA 등이 있습니다.

스트림 암호는 평문을 비트나 바이트 단위로 더 작은 단위로 쪼개 암호화를 하는 방식입니다. 속도가 블록암호에 비해 빠르기 때문에 휴대폰이나 군사목적으로도 사용되는 방식입니다. 대표적으로 LFSR, RC4 등이 있습니다.

대칭키는 동일한 키로 암호화/복호화가 가능하기 때문에 빠른 속도를 가지게 됩니다. HTTPS 에서도 최초 Handshake가 완료되면 대칭키를 통해 패킷을 암호화해서 사용하게 됩니다. 

 

 

 

 

비대칭키

비대칭키는 암호화 키와 복호화 키가 다른 암호방식입니다. 비대칭키 암호방식에는 크게 공개키 방식비밀키 방식 두가지가 존재합니다. 공개키 방식에서는 공개키로 암호화를 진행하고 비밀키로 복호화를 하는 방식으로 사용됩니다. 즉, 암호화를 해야 하는 데이터가 유출되면 안되는 경우 사용됩니다. 대표적인 공개키 암호화 알고리즘으로 RSA가 있습니다.

두번째 비밀키 방식은 비밀키로 암호화를 진행하고 공개키로 복호화를 진행하는 방식입니다. 이는, 데이터의 중요성 보다는 누가 송신했느냐가 더 중요할 때 사용됩니다. 대표적으로 전자서명을 할 때 비밀키 방식이 사용됩니다. 대표적으로 타원곡선디지털 서명 알고리즘(ECDSA)가 사용됩니다. 

 

Node.js crypto

 

Node.js 환경에서는 암호화를 할 수 있는 crypto 모듈을 기본 제공하고 있습니다.

Node.js crypto module Document

 

해시 알고리즘(단방향 암호)

crypto 모듈을 사용해서 해시 암호를 생성할 수 있습니다.

const crypto = require("crypto");

const createHashedPassword = (password) => {
  return crypto.createHash("sha512").update(password).digest("base64");
};


console.log(createHashedPassword("password123"));
vtTvodT9vZVL03Bdaip4Jw7JpS7Pv7AQxhhir1x2rxdh/+sa72rKG/XQKzeBqoVPq9K2nHkN504X7P7Dy2rEvw==

 

password123이라는 비밀번호를 단방향 암호화 함수를 사용해 암호화를 할 수 있습니다. 복호화는 불가능합니다. 여기서 문제는 실행할 때 마다 동일한 hash값을 뱉는 건데요, 이를 위해서 salt를 사용해야 합니다. salt는 소금을 친다는 뜻으로 해시함수를 사용할 때 변경값을줘서 실행할 때 마다 다른 해시값을 뱉어내게 됩니다.

randomBytes와 salt를 추가해 해시를 생성하는 pbkdf(Password-Based Key Derivation Function 2) 함수를 사용합니다. buffer로 salt를 생산한 뒤 해시값을 생산하는 과정을 10000번 반복합니다. 이제 실행할 때 마다 다른 값을 뱉어내게 됩니다. 

const createHashedPassword = (password) => {
  //   return crypto.createHash("sha512").update(password).digest("base64");
  crypto.randomBytes(32, (err, buf) => {
    const salt = buf.toString("base64");
    crypto.pbkdf2(password, salt, 100000, 64, "sha512", (err, key) => {
      console.log("password : ", key.toString("base64"));
    });
  });
};
8r/U/MzaocprSEobSImPWS653Pr/dXNKlE3Tga1GF5oU3+1520EWVcedOrZyMT7eP+9dRR7iu8KZfy+S3hkLFw==
Y4/Qh54cRTTMbRjZzrPiV+15oZmnszpzPKv1xKtCpHNNoZlb2GrRAsWbRwK3sOIyU2ZVqpkYxgP1oeMimO1Yvw==

 

양방향 암호화 알고리즘

crypto 모듈을 사용하면 양방향 비대칭키 암호를 구현할 수 있습니다. 공개키 방식과 비밀키 방식이 있는데, 여기서는 비밀키 방식을 사용해 양방향 비대칭키 암호화를 어떻게 구현하는지 알아보도록 합니다. 우선 crypto 모듈에서 generateKeyPair 모듈을 가져옵니다. 

const { generateKeyPair } = require('node:crypto');

 

가져온 generateKeyPairSync 모듈로 pubKey와 PrivateKey 쌍을 생성합니다. 생성된 PubKey와 PrivateKey를 출력해보면 저희가 자주 볼수 있는 PEM 키처럼 생긴 KEY가 출력됩니다. 

 

 

 

 

generateKeyPair('rsa', {
    modulusLength: 4096,
    publicKeyEncoding: {
      type: 'spki',
      format: 'pem'
    },
    privateKeyEncoding: {
      type: 'pkcs8',
      format: 'pem',
      cipher: 'aes-256-cbc',
      passphrase: 'top secret'
    }
  }, (err, publicKey, privateKey) => {
    console.log(publicKey, privateKey)
});

 

자 이제 Key Pair가 생성되었으니 암호화 / 복호화를 진행합니다. 먼저 암호화를 하기 위해서는 publicEncrypt 함수를 사용합니다. password123 이라는 문자열을 생성된 PubKey로 암호화를 진행합니다. 

generateKeyPair(
  "rsa",
  {
    modulusLength: 4096,
    publicKeyEncoding: {
      type: "spki",
      format: "pem",
    },
    privateKeyEncoding: {
      type: "pkcs8",
      format: "pem",
      cipher: "aes-256-cbc",
      passphrase: "top secret",
    },
  },
  (err, publicKey, privateKey) => {
    const str = "password123";
    const enc = crypto.publicEncrypt(publicKey, Buffer.from(str));
    const encstr = enc.toString("base64");

    console.log(encstr);
  }
);

 

PubKey로 암호화된 결과가 출력됩니다. 

 

이제 PubKey로 암호화된 문자열을 PrivateKey로 복호화를 진행합니다. Node version 10 이상은 PrivateKey를 인자로 받는 Key를 다시 생성해줘야 복호화가 가능합니다. 복호화는 privateDecrypt 함수를 사용합니다.

generateKeyPair(
  "rsa",
  {
    modulusLength: 4096,
    publicKeyEncoding: {
      type: "spki",
      format: "pem",
    },
    privateKeyEncoding: {
      type: "pkcs8",
      format: "pem",
      cipher: "aes-256-cbc",
      passphrase: "top secret",
    },
  },
  (err, publicKey, privateKey) => {
  
    // 암호화
    const str = "password123";
    const enc = crypto.publicEncrypt(publicKey, Buffer.from(str));
    const encstr = enc.toString("base64");
    
    // 복호화
    // Node v10 이상은 key를 추가로 생성해줘야 됨
    const key = crypto.createPrivateKey({
      key: privateKey,
      format: "pem",
      passphrase: "top secret",
    });

    const dec = crypto.privateDecrypt(key, Buffer.from(encstr, "base64"));
    const decstr = dec.toString("utf8");

    console.log("PubKey Result", decstr);
  }
);

 

PrivateKey를 이용해 암호문을 해독해 출력합니다. 

PubKey Result password123

 

 

 

 

 

양방향 비대칭키의 PubKey와 PrivateKey는 수학적으로 연결되어 있습니다. 그중에서도 RSA 방식은 소인수 분해 방식을 이용한 암호화 알고리즘입니다. 예를 들어 35라는 숫자를 소인수 분해하면 5와 7이 나옵니다. 하지만 12938937589230749283487102398571293847192384233247928342이란 거대한 숫자를 소인수분해 하기 위해서는 상당한 시간이 필요합니다. 여기서 소인수들이 PrivateKey가 되고 그 결과값이 공개키가 됩니다.

개인키개인키  = 공개키

 

 

Reference

 

 

 

 

 

[Git] 되돌리기 명령어 (restore, reset, clean 사용법)

Local Repository에서 작업하다 수정사항을 이전 단계로 돌려야 하는 경우 일일이 수정사항을 찾아서 되돌리기는 시간이 많이 소요됩니다. 수정 사항을 되돌리는 작업은 2가지 단계로 나눌 수 있습

about-tech.tistory.com

 

 

[Klaytn] Caver-js 사용법 설치 contract call send

Caver-js Klaytn 블록체인을 Javascript를 사용해 접근하기 위해서는 Caver-js를 사용한다. caver-js는 HTTPS, 웹 소켓 연결로 Klaytn 블록체인과 Javascript 간 상호작용을 가능하게 한다. Caver-js 설치 우선 ca..

about-tech.tistory.com

 

 

Truffle Testing 방법 (Mocha, chai 사용)

Truffle 에서 개발, 배포, 테스팅 까지 원큐에 끝낼 수 있습니다. Truffle Test Truffle에서 테스트를 할 수 있는 방법은 2가지가 있습니다. solidity파일로도 가능하고, test.js 파일로도 가능합니다. 두가지

about-tech.tistory.com

 

댓글