본문 바로가기

Javascript

[JavaScript] .crypto.createHmac 'sha256' 해시 알고리즘 암호화, 복호화 / 자바스크립트, node.js, JWT 토큰

반응형

 

JavaScript Node.js |  crypto.createHmac() 

crypto.createHmac() 매서드는 Hmac 객체를 생성하는데 사용된다. 

 

 

 

기본 구조 

crypto.createHmac(algorithm, key, options)

첫 번째 인자값 : 알고리즘 방식 ex) sha256, sha512

두 번째 인자값 : Hmac key값 (암호 HMAC hash 를 만드는데 사용)

                      It returns string, Buffer, TypedArray, DataView or KEyObject. 

세 번째 인자값 : (Optional) - stream behavior 를 컨트롤 한다. It returns an object. 

 

 

 

예시 

const crypto = require('crypto');

function createPW(userpw){    
    const cryptoPassword = crypto.createHmac('sha256',Buffer.from(process.env.salt))
                            .update(userpw)
                            .digest('base64')
                            .replace('==','').replace('=','');                            
    return cryptoPassword;
}  

module.exports=createPW;

 

process.env.salt- 이미 dotenv 설치, 아래 any stirng 값을 입력했다. 아무 string이면 된다고 한다 

.env 코드 

SALT=ANYTHINGYOUCANWRITEHERETOMAKEITWORKASAKEYFORHASHALGORITHM

위의 cryptoPassword 암호화 과정을 살펴보면,

crypto.createHmac('sha256', Buffer.from(process.env.salt)).update(userpw).digest('base64').replace('==','').replace('=','')

crypto.createHmac('sha256', Buffer.from(process.env.salt)).update(userpw).digest('base64').replace('==','').replace('=','')

crypto.createHmac 의

첫 번째 인자값 : 암호방식 => 'sha256' 방식을 쓰겠다. 

두번 째 인자값 :

process.env.salt 를 Buffer.from한 형태 => Stirng(암호화할 때 첨부하는 키값)을 바이너리 형식으로 바꾸겠다. 

.update(userpw) => 그 값에 'sha256' 방식으로 암호화한 값에 userpw를 더하겠다.  

.digest('base64') => userpw 까지 더한 암호 표현을 base64 방식 (글자 수를 굉장히 짧게 줄이는)으로 소화하겠다.

.replace('==','') => '==' 은 '' 빈값으로 바꾸겠다. (암호화하고 맨 끝에 '=' or '==' 이 뜰 때가 있음) 

.replace('=','') => '=' 은 '' 빈값으로 바꾸겠다. (같은 이유) 

 

 

 

 

 

각각의 내용 consol에서 보기 

const crypto = require('crypto');

function createPW(userpw){  //createHmac 두번째 인자값 = any String like salt 암호화필요  
    const cryptoPassword = crypto.createHmac('sha256',Buffer.from('anystring'))
                            .update(userpw)
                            .digest('base64')
                            .replace('==','').replace('=','');                            

        let a = Buffer.from('anystring')
        let b = crypto.createHmac('sha256', Buffer.from('anystring'));
        let c = crypto.createHmac('sha256', Buffer.from('anystring')).update(userpw);
        let d = crypto.createHmac('sha256', Buffer.from('anystring')).update(userpw).digest('base64');
        let e = crypto.createHmac('sha256', Buffer.from('anystring')).update(userpw).digest('base64').replace('=','');
        console.log(a)
        console.log(b)
        console.log(c)
        console.log(d)
        console.log(e)
        return cryptoPassword;
}  

createPW('example');
module.exports=createPW;

 

 

 

 

 

다른 예제 , JWT 암호화 과정

require('dotenv').config();
const crypto = require('crypto');

function createToken(userid) {
    let header = { "tpy": "JWT", "alg": "HS256", }
    let exp = new Date().getTime() + ((60 * 60 * 2) * 1000) //1970.1.1부터 0 // 1월1일 1초지났으면 1000 
    let payload = {
        userid, exp,
    }

    const encodingHeader = Buffer.from(JSON.stringify(header))
                            .toString('base64')
                            .replace('==', '').replace('=', '');
    const encodingPayload = Buffer.from(JSON.stringify(payload))    // 요 안의 값을 다시 꺼내올예정
                            .toString('base64')
                            .replace('==', '').replace('=', '');
                            
    //console.log(encodingHeader, encodingPayload); 
    //암호화 시작 //첫번째 : 암호방식 /두번째인자 : key값 (이게 공개되면 해킹됨) - best-env 
    let signature = crypto.createHmac('sha256', Buffer.from(process.env.salt))
                            .update(encodingHeader + "." + encodingPayload)
                            .digest('base64')
                            .replace('==', '').replace('=', '');
    let jwt = `${encodingHeader}.${encodingPayload}.${signature}` 
    return jwt;
}

module.exports = createToken;

 

 

 

 

 

 

복호화 과정 (암호 해독이 아닌 payload 안에 있는 내용 다시 빼내기) 

JWT 암호를 만들기 위해 넣은 값 - payload 안의 내용을 꺼내오기 

 

 payload 만든 코드  

순서 : 객체 -> 문자열 -> 바이너리 -> base64

    const encodingPayload = Buffer.from(JSON.stringify(payload))    // 요 안의 값을 다시 꺼내올예정
                            .toString('base64')
                            .replace('==', '').replace('=', '');

1. JSON.stringify(payload) - payload를 문자열로 바꿔라 (payload 안 내용이 객체 -> JSON.stringify 쓴 이유)  

2. Buffer.from(JSON.stringify(payload)) - 문자열로 바꾼 payload를 바이너리 형태로 바꿔라 (12 32 45 32 ... 이런 형태) 

3. .toString('base64') - 바이너리 형태로 바꾼 걸 base64형태로 바꾸겠다. (글자수 확 줄이겠다.) 

(4. replace('==','') - '==' 요 문자가 있으면 빈값으로 바꾸겠다. )

 

 

 payload 안의 객체 내용 빼내기 

위의 1,2,3 번을 (4번은 제외) 그대로 되돌리면 된다.  

순서 :  base64 -> 바이너리 -> 문자열 -> 객체

        let { userid, exp } = JSON.parse(Buffer.from(payload, 'base64').toString());

1. Buffer.from(payload,'base64') - base64 형태로 바뀌었던 payload를 다시 바이너리 형태로 바꾸겠다. 

2. Buffer.from(payload,'base64').toString() - 바이너리 형태로 바꾼걸 문자열로 바꿔라 

3. JSON.parse( 위의 코드) - 문자열로 바꾼 것을 다시 parse 객체로 바꾸겠다. 

 

 

 

 


 

 

질문 내용 

require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');

module.exports = (req, res, next) => { 질문 // 어쩔떄는 맨 아래 exports / 여기는 조금 다른 이유?
    let accessToken = tokenKey('asdf');
    let [header, payload, signature] = accessToken.split('.');
    let signatureToCheck = signatureCheck(header, payload);

    function signatureCheck() {
        const signatureChecked = crypto.createHmac('sha256', Buffer.from(process.env.salt))
            .update(header + '.' + payload)
            .digest('base64')
            .replace('==', '=').replace('=', '');
        return signatureChecked
    }

    if (signature == signatureToCheck) {
        console.log('검증')
        let { userid, exp } = JSON.parse(Buffer.from(payload, 'base64').toString());
        //질문 - 위의 코드  toString('base64')와 어떻게 다른지
        console.log(userid, exp)
        let newExp = new Date().getTime();
        if (newExp > exp) {
            console.log('토큰 만료');
            redirect('/msg=토큰만료')
            return 0;
        }
        req.userid=userid;
        next();
    } else {
        console.log('실패')
        res.redirect('/msg=해킹이다')
    }
}

 

- 질문 답변 :  toStirng  - 글자 바꾼 형태를 Stirng할건데(이미 string임) base64로 / 그럼 toString('base64')는 이미 string으로 바뀐 문자열을 64로 바꾼다

 

let { userid, exp } = JSON.parse(Buffer.from(payload, 'base64').toString());
        //질문 - 위의 코드  toString('base64')와 어떻게 다른지
        // JWT 토큰 값 = accessToken (base64)로이미 변환된 글자 

payload = base64 로 변환되어 있음 .toString() 원래 글자로 바꾼다. (디코딩) string을 객체로 바꿈 

그럼 저기 (payload,'base64').toString()  toSTring --> base64로 되어있는 문자열을--> b** console.log쳐보기 

 

2 -> 16진수 연동잘됨

16 -> 64진수 변환 쉽다 

64진수 좋은 점 

10진수 ex) 10 == 0010 4칸 사용 

16진수 ex) 10 == A 1칸 사용

64진수 -> 10진수의 글자들을 또 함축 가능 

=>변환쉽고, 글자수도 줄여져서 , 특히 대량일 때. 

 

 

반응형