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
이제 예제를 볼까요?
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 로 암호화가 되지 않습니다.
다음 포스팅에서는 Node.js 에서 crypto 모듈을 사용해 AES 암호화를 해보겠습니다.
슨생님 좋은 글 잘 읽었습니다!
답글삭제암린이라서 기초적인 질문하나 해도될까요?
해당 글에서는 pkcs#8 to pkcs#1으로 public key만 변환하였는데 private key는 pkcs#8인 상태일텐데, 변환된 public key로 복호화가 가능한가요? private key도 변환할 필요는 없나요?
방문 감사합니다. 해당 명령 수행시 private key는 PKCS#1로 생성됩니다.
삭제주석이 BEGIN RSA PUBLIC KEY 인지 단순히 BEGIN PUBLIC KEY인지 확인해 보면 쉽게 구분이 가능합니다.