2018년 1월 24일 수요일

Node.js 에서 RSA 로 암복호화 하기

Hello SyntaxHighlighter
Node.js 의 crypto 모듈은 기본적으로 제공되므로, npm 으로 설치할 필요가 없습니다.

crypto 기본 모듈을 사용해 RSA 의 키로 암복호화 하는 방법을 알아 봅시다.

일단 그 전에 OpenSSL로 RSA Private Key, Public Key 를 생성합니다.


C:\Users\Public>openssl genrsa -out private.key 2048

Generating RSA private key, 2048 bit long modulus
.................................................................................................................................+++
.........+++
unable to write 'random state'
e is 65537 (0x10001)


예전 블로그의 AES 관련 글 을 보시면 아시다시피,

Private key 로 Public key 를 Generate 할 수 있습니다.

C:\Users\Public>openssl rsa -in private.key -out public.key -pubout

그런데 단순히 이렇게 하시면, 결과가 PKCS#8 표준으로 나옵니다.

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuErwCGWyq3nIASTKhgiH
GAaURZ9rs5EBR9L1YUJh1ftYgQSt0gUzab6hyW/upgM4Z4Mu6pA+KZCHncKc4SHJ
XdJvYnj1L6/RRinXQp+R3MmGypoB/r1ckM1aEt75/I0m7Dlatj58f56Z21yHsJNb
tf1LAD981a3kR6kaIb7Uc5bsUDwObF2r5xAenQDtaZoWgaErHWwiqzJJQebcAVVq
lq2/+f1kzRVqHsasosXOD6hrDz9oC2SvBKWVrmOPF4D+mwwaChEKAhFDvCKj5NYw
prmoOXWK6t5WroYvsVo5Sa039DiCPXMsug9MidhQLB7SpW7Bi+xeuwvObWppgGZ8
FQIDAQAB
-----END PUBLIC KEY-----

PKCS#8 표준의 ASN.1 구조를 보면 다음과 같습니다.
PublicKeyInfo ::= SEQUENCE {
  algorithm       AlgorithmIdentifier,
  PublicKey       BIT STRING
}

AlgorithmIdentifier ::= SEQUENCE {
  algorithm       OBJECT IDENTIFIER,
  parameters      ANY DEFINED BY algorithm OPTIONAL
}

우리는 PKCS#1 을 사용할 것이므로 아래와 같이 변환합니다.

C:\Users\Public>openssl rsa -pubin -in public.key -RSAPublicKey_out


-----BEGIN RSA PUBLIC KEY-----

MIIBCgKCAQEAuErwCGWyq3nIASTKhgiHGAaURZ9rs5EBR9L1YUJh1ftYgQSt0gUz
ab6hyW/upgM4Z4Mu6pA+KZCHncKc4SHJXdJvYnj1L6/RRinXQp+R3MmGypoB/r1c
kM1aEt75/I0m7Dlatj58f56Z21yHsJNbtf1LAD981a3kR6kaIb7Uc5bsUDwObF2r
5xAenQDtaZoWgaErHWwiqzJJQebcAVVqlq2/+f1kzRVqHsasosXOD6hrDz9oC2Sv
BKWVrmOPF4D+mwwaChEKAhFDvCKj5NYwprmoOXWK6t5WroYvsVo5Sa039DiCPXMs
ug9MidhQLB7SpW7Bi+xeuwvObWppgGZ8FQIDAQAB

-----END RSA PUBLIC KEY-----



PKCS#1 의 ASN.1 구조는 다음과 같습니다.
RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}

(참고로 반대로 ( PKCS#1 -> PKCS#8 ) 변환하는 하는 방법은 아래와 같습니다.)
openssl rsa -RSAPublicKey_in -in -pubout

이제 예제를 볼까요?



RSA encyrpt & decrypt in node.js


var crypto = require('crypto');

var PRIVKEY = '-----BEGIN RSA PRIVATE KEY-----\n'+
'MIIEowIBAAKCAQEAuErwCGWyq3nIASTKhgiHGAaURZ9rs5EBR9L1YUJh1ftYgQSt\n'+
'0gUzab6hyW/upgM4Z4Mu6pA+KZCHncKc4SHJXdJvYnj1L6/RRinXQp+R3MmGypoB\n'+
'/r1ckM1aEt75/I0m7Dlatj58f56Z21yHsJNbtf1LAD981a3kR6kaIb7Uc5bsUDwO\n'+
'bF2r5xAenQDtaZoWgaErHWwiqzJJQebcAVVqlq2/+f1kzRVqHsasosXOD6hrDz9o\n'+
'C2SvBKWVrmOPF4D+mwwaChEKAhFDvCKj5NYwprmoOXWK6t5WroYvsVo5Sa039DiC\n'+
'PXMsug9MidhQLB7SpW7Bi+xeuwvObWppgGZ8FQIDAQABAoIBAQCPOcYkcI0UETgs\n'+
'E2DGHBiJxoszNLuqOVaKcFw9sy5/87ALzQwdvecAFqR7/d617KjIYb5zk5iMCwQq\n'+
'ylXL7csmfGYOXL0Iy5ZT9i6SW5srwP9ds6U7SgWHj+Ch6+LSsQx/5+8k1ZlCQYuH\n'+
'XPkjdNKAtJK2ZaDqHBPe0YA6m6lXDrEOJl6xrlUWCZS02XPIXFaB+qTBG2UqWCUK\n'+
'KzVa9qIqWf3bVGJCLc70u5UiuvCC+V8VtJ964AEnj90qZy1tRhEc2X8bbWmhL3yB\n'+
'3SLWH4ZvJyEDQe/yycx9rO6CymDj3c378IyWORYt1y6mKRmltr2NJ1Ecbl9xaFrc\n'+
'JeVO8wDdAoGBAOUjZzuaVppccEIiBbLS0NksSeD4tfjlUPFqVU1obZcbkDugK1Id\n'+
'og+W5yVy2NqgeEi4nQ9Ogi485cWbCzTZM52d9zuFe/60hBFPU8jWxafW0OW2o6ci\n'+
'F1vtzWEF2EO7omUoiGu6mOI4yRcUMwXiw1cCWr2NYpASQ39QuxwXKgg7AoGBAM3l\n'+
'sC7nxqDusq+J9CI6V+oNb2v7KgapQmrdXNB5l6Mk2Dl/uNfuX5cDceMQlO5B/ObT\n'+
'44kgsBR3tO6y7PQLDClt5ZPrVJZ16+cCj41UDDgZDD0vDaU24K/qSVrsOH4gNgeM\n'+
'CcsInzX/l1bm+RAhE0pHuPHBZFuLi8brV/wZCpfvAoGAQu4XbmKDn20W4UpczcIk\n'+
'fPshzVP4m24oOYwsxIKXWEcV10TOwpqjRth2RgsI6rtqxxsdzWXKQsVI/HJwUIyN\n'+
'NiH5IGq6MEj8Nq4sNAMAEyl9NUwm+1/K4PBSSF/Trt0070Vqq8UCeTnLCzG8QaDe\n'+
'HCE07h9JRfn/u0WSkf72KRcCgYAUTGmjJix53y50idgsq63RIEP01E0fXP50RKCK\n'+
'2QHvDonWmVXiy9hWrftDVHYqSw0gwJD1CujxC6AlzDP6F0C6sN/qRlAPiU6ZdrIq\n'+
'T7foq+d9/K6OtCtQjHtw4ErtfEV3VwH8Jzxy+WC1K44wXeJl904vX06Ci+5azQbe\n'+
'jqVxtwKBgBnxVKFQCmuDjOZrf7V0jB/mf4ir9npqvHdJMHE/Et70lOaUbhat6i+Y\n'+
'PPtJewynfF4zp+HD6jOlj4RhbCncMNdaWAyDYOucYOJOpMr5mIYJHdCOVQ0aSRD7\n'+
'/dZEOGblrch6VXOEvC++yaToNhjkeTP63IH4K6uqa9btelz3Df5Y\n'+
'-----END RSA PRIVATE KEY-----\n';

var PUBKEY = '-----BEGIN RSA PUBLIC KEY-----\n'+
'MIIBCgKCAQEAuErwCGWyq3nIASTKhgiHGAaURZ9rs5EBR9L1YUJh1ftYgQSt0gUz\n'+
'ab6hyW/upgM4Z4Mu6pA+KZCHncKc4SHJXdJvYnj1L6/RRinXQp+R3MmGypoB/r1c\n'+
'kM1aEt75/I0m7Dlatj58f56Z21yHsJNbtf1LAD981a3kR6kaIb7Uc5bsUDwObF2r\n'+
'5xAenQDtaZoWgaErHWwiqzJJQebcAVVqlq2/+f1kzRVqHsasosXOD6hrDz9oC2Sv\n'+
'BKWVrmOPF4D+mwwaChEKAhFDvCKj5NYwprmoOXWK6t5WroYvsVo5Sa039DiCPXMs\n'+
'ug9MidhQLB7SpW7Bi+xeuwvObWppgGZ8FQIDAQAB\n'+
'-----END RSA PUBLIC KEY-----\n';

// RSA PRIVATE ENCRYPT -> PUBLIC DECRYPT //
 myMSG = "[ORIGINAL] I'm securekim !!!";
 
function privENC_pubDEC(originMSG){
 encmsg = crypto.privateEncrypt(PRIVKEY, Buffer.from(originMSG, 'utf8') ).toString('base64');
 msg = crypto.publicDecrypt(PUBKEY, Buffer.from(encmsg, 'base64'));
 console.log("Encrypted with private key : "+encmsg);
 console.log(msg.toString());
}

function pubENC_privDEC(originMSG){
 encmsg = crypto.publicEncrypt(PUBKEY, Buffer.from(originMSG, 'utf8') ).toString('base64');
 msg = crypto.privateDecrypt(PRIVKEY, Buffer.from(encmsg, 'base64'));
 console.log("\nEncrypted with public key : "+encmsg);
 console.log(msg.toString());
}

privENC_pubDEC(myMSG);
pubENC_privDEC(myMSG);



  • 결과 :


Encrypted with private key : Q9Tz2LRlv91Rk2ScR/Wjv77ouDsiWyefVIKlssIYHBJihZeQ0IDH+rUCMG0aBhSDNT92zZjAmPF5Pah9xejqYsfCfDslmNp+FXvmfoA02uviW6b4A6e90U3atyU1PIZHtp4nDtIAYCvY78xzEyZ5oqwhbDcKuIcs4Uo4dtXNmo6N9/+Rxckf0ByCyRAUv86x1a6SYzgqyGthaDAnIUxFAXsJpAkrAOTeBy9PKx785DEXYDBOzgsFcZRHwMuFNFlMTYfQtW/rwPL0cSA6ud3Ew0Qsv4wY73ty46s2kYezSo6YjrHkYDotlhXLK5rqY+bBtU/SAufvhj/BDxYEDe9scQ==
[ORIGINAL] I'm securekim !!!

Encrypted with public key : CEhDVj/6KquMN5QRi69okZXz6gVjHKQGOm1C1CvBExRTQrKN8sMMT1wqTQpRdVzu+YUlidgCmj+v+5bgiSMl1Ul38VUHT9rL42fwT226RYfhlMvRn2umddboyHh0TMPwWqI6aQ/36DQqmuXN34Rk2It2qs5gERZWTJgRdQ1mYrrz7nOA5nnu/vZyOmk4PjYww9QediUF+J9pxOAlYsIT+AdOXVvdKeB6M5esGZWwQ3trZvklG0KmZSNUQ6HyvQ/+MorsKDMOZxpRcBxTB+3DzzUb+UtvBSlsUNYfnMAYlEeB7M4AzgIJpZ8jfYOecp9DSx6eTzk919YVCyUU3GL2oQ==

[ORIGINAL] I'm securekim !!!


일반적으로 각 함수가 사용되는 용도는 다음과 같습니다.

ㆍprivENC_pubDEC

   - Signature Verify
    예를 들어 상호 인증시 클라이언트는 이때까지 주고받은 내용을 Hash 한 다음
    Private key 로 암호화해 Signature 를 생성해 서버로 전달합니다.
    서버는 이때까지 주고받은 내용을 Hash 한 값과,
    클라이언트가 보내준 인증서의 Public key로 Signature 를 복호화 한 값을 비교하여
    동일한지 판단 합니다.

ㆍpubENC_privDEC

    - AES KEY nego
     클라이언트가 AES Key 를 서버의 Public key 로 암호화해서
     서버에게 전달 해 주면 서버에서는 복호화 합니다.
     이렇게 하여 클라이언트와 서버는 AES Key 를 나눠갖게 되고,
     이후 통신은 AES KEY로 하게 됩니다.

이후 통신을 AES KEY 로 암호화 하는 이유는 RSA 암호화 방식은 AES 에 비해 너무 느리기 때문입니다.

그리고 메시지의 길이가 긴 경우 아래와 같이 RSA 로 암호화가 되지 않습니다.

Error: error:0406C06E:rsa routines:RSA_padding_add_PKCS1_type_1:data too large for key size

다음 포스팅에서는 Node.js 에서 crypto 모듈을 사용해 AES 암호화를 해보겠습니다.