정의
토스처럼 어떤 은행이던 잔액을 확인할 수 있듯이 , 지갑이라는것은 블록체인 기반으로 만들어진 코인(비트코인,이더리움)들을 지갑이라는 프로그램에서 모두 확인할 수 있습니다.
출금 예시
보내는이 : 홍길동
받는이 : 코난
금액 : 10,000
위와 같이 예시를 들어보면 보내는이 (홍길동)이라는 사람이 받는이 (코난)에게 돈을 보내려고 한다면,
홍길동이라는 사람이 누구인가를 알 수 있어야하고, 그 홍길동이라는 사람이 10,000원 이상을 가지고 있는지 확인해야하고.
돈을 보내려 할때 홍길동 본인이 돈을 보내려는게 맞나 확인하기위해 인증도 거쳐야합니다.
지갑
이라는 것은 홍길동, 코난이라는 사람의 계좌번호만 관리하는 프로그램이 지갑입니다.
지갑을 만드는 시스템을 살짝 보면,
100원 동전이 있다고 하였을때 이것을 256번 던졌을때 1번째부터 256번째까지 같을 확률이 얼마나 될까요? 2의 256승이니 말도 안되는 확률이 나올것입니다.
이것을 프로그램으로 보았을때 256개의 bit가 모두 같아야 한다는 것입니다. 이것을 byte로 환산하면 32byte, 64자리 숫자가 됩니다.
동전을 256번 던지는 값을 16진수로 변환하여 (계좌번호 비밀번호) = 개인키
라고 합니다. => 은행의 OTP 또는 계좌비밀번호 개념
개인키를 인자값으로 어떤 메서드를 실행하면 나오는 리턴값 = 공개키
=> 평생계좌번호가 아닌 실제로 쓰이는 값?
공개키를 가지고 어떤 암호화를 하여 나온 결과값이 주소 또는 32byte 글자를 앞에서 12byte를 자르고 뒤에 20byte만큼의 40글자를 계정이라고 합니다.
=> 은행의 계좌번호
개인키를 가지고 그대로 보내주는것이 아니라 다른 값으로 변환하여 보내서 나를 증명하는 것을 서명이라고 합니다.
import { randomBytes } from 'crypto'; // 랜덤으로 byte를 뽑아줍니다.
import { SHA256 } from 'crypto-js'; // SHA256 암호화
import elliptic from 'elliptic'; // 타원곡선 알고리즘 결과를 리턴해주는 라이브러리
const ec = new elliptic.ec('secp256k1');
/** 지갑을 랜덤으로 만들어보기 */
describe('지갑 이해', () => {
let privKey: string;
let publicKey: string;
let signature: elliptic.ec.Signature;
let hash: string;
it('개인키 (privKey)', () => {
privKey = randomBytes(32).toString('hex'); // 랜덤으로 byte를 추출하여 Buffer로 리턴된 값을 hex로 toString
console.log('개인키', privKey);
/* 개인키 83c227b9fa8c7a4ebbbdbfdd67bcb802774d4fa59a592d8a273b61d699e91552 */
});
개인키를 만들기 위해
randomBytes()
함수로 256자리의 2진수로 이루어진 랜덤 값을 추출하고toString('hex')
함수로 16진수값으로 만들었습니다.
import { randomBytes } from 'crypto'; // 랜덤으로 byte를 뽑아줍니다.
import { SHA256 } from 'crypto-js'; // SHA256 암호화
import elliptic from 'elliptic'; // 타원곡선 알고리즘 결과를 리턴해주는 라이브러리
const ec = new elliptic.ec('secp256k1');
/** 지갑을 랜덤으로 만들어보기 */
describe('지갑 이해', () => {
let privKey: string;
let publicKey: string;
let signature: elliptic.ec.Signature;
let hash: string;
it('개인키 (privKey)', () => {
privKey = randomBytes(32).toString('hex'); // 랜덤으로 byte를 추출하여 그 Buffer로 리턴된 값을 hex로 string화 시킨다.
console.log('개인키', privKey);
/* 개인키 83c227b9fa8c7a4ebbbdbfdd67bcb802774d4fa59a592d8a273b61d699e91552 */
});
it('공개키 (publicKey)', () => {
const keyPair = ec.keyFromPrivate(privKey); // 비밀키를 가지고 타원곡선 알고리즘으로 암호화하여 공개키를 만듬
publicKey = keyPair.getPublic().encode('hex', true); // 위에서 뽑아온 값을 encode하여 16진수로 추출합니다.
console.log('공개키', publicKey);
/* 공개키 0223ebf69d01a2e5cb8141377245a5abcd7ef39abf8c8ac163073766f8641189b0 */
});
공개키를 만들때는 타원곡선 알고리즘을 사용하기 쉽게한
elliptic
라이브러리를 사용하여const ec = new elliptic.ec('secp256k1')
를 통해ec
라는 인스턴스를 만들고ec
라는 인스턴스 안에 있는keyFromPrivate()
메서드를 사용하여 컴퓨터가 읽을 수 있도록 해주고 이 값을 16진수로encode
하여 공개키를 생성하였습니다.
이때 공개키의 값이 개인키와 마찬가지로 64글자가 아닌것은 타원곡선 알고리즘에서y
값이 짝수일때에는 앞자리에 02를 붙이고 홀수일때는 03을 붙이기 때문입니다.
import { randomBytes } from 'crypto'; // 랜덤으로 byte를 뽑아줍니다.
import { SHA256 } from 'crypto-js'; // SHA256 암호화
import elliptic from 'elliptic'; // 타원곡선 알고리즘 결과를 리턴해주는 라이브러리
const ec = new elliptic.ec('secp256k1');
/** 지갑을 랜덤으로 만들어보기 */
describe('지갑 이해', () => {
let privKey: string;
let publicKey: string;
let signature: elliptic.ec.Signature;
let hash: string;
it('개인키 (privKey)', () => {
privKey = randomBytes(32).toString('hex'); // 랜덤으로 byte를 추출하여 그 Buffer로 리턴된 값을 hex로 string화 시킨다.
console.log('개인키', privKey);
/* 개인키 83c227b9fa8c7a4ebbbdbfdd67bcb802774d4fa59a592d8a273b61d699e91552 */
});
it('공개키 (publicKey)', () => {
const keyPair = ec.keyFromPrivate(privKey); // 비밀키를 가지고 타원곡선 알고리즘으로 암호화하여 공개키를 만듬
publicKey = keyPair.getPublic().encode('hex', true); // 위에서 뽑아온 값을 encode하여 16진수로 추출합니다.
console.log('공개키', publicKey);
/* 공개키 0223ebf69d01a2e5cb8141377245a5abcd7ef39abf8c8ac163073766f8641189b0 */
});
// 서명을 만들때 필요한 값 = 개인키, 해시값(transaction hash 값)
it('디지털 서명', () => {
const keyPair = ec.keyFromPrivate(privKey);
hash = SHA256('ingoo').toString(); // 추후에 거래내역을 암호화 하여 signature를 만듭니다.
signature = keyPair.sign(hash); // keyPair와 hash를 가지고 sigNature를 만듭니다. / signature를 가지고 publickey를 사용하여 이 개인키가 올바른지 아닌지 알 수 있다.
console.log('서명', signature);
/*
서명 Signature {
r: BN {
negative: 0,
words: [
35076865, 2682153,
49280326, 14095229,
38038072, 17798748,
54528708, 62799196,
13171684, 3278574
],
length: 10,
red: null
},
s: BN {
negative: 0,
words: [
6309881, 62125812, 35062084, 24295760,
55757757, 42834996, 33720198, 16977388,
23834472, 447880, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0
],
length: 10,
red: null
},
recoveryParam: 0
}
*/
});
// 서명을 검증할때 필요한 값 = 서명, 해시값(transaction hash 값), 공개키
it('서명 검증 (verify)', () => {
const verify = ec.verify(hash, signature, ec.keyFromPublic(publicKey, 'hex'));
console.log('검증', verify);
/* 검증 true */
});
서명을 만들때는 개인키를
ec.keyFromPrivate()
를 통해 컴퓨터가 읽을 수 있는 값으로 변환해준후 hash값을 가지고signature
을 만들어줍니다. 지금은 임의의 값으로 넣어두고 추후에는transaction hash
값 , 즉 거래내역을 암호화하여hash
값을 채워줄것입니다.
검증을 할때는 hash값과 signature, 공개키를 가지고
ec.verify()
메서드를 사용하여 이 서명이 본인이 만든것이 맞는지 신원확인을 하고 데이터의 위.변조를 확인하여return
값인true
혹은false
로 검증을 합니다.
import { randomBytes } from 'crypto'; // 랜덤으로 byte를 뽑아줍니다.
import { SHA256 } from 'crypto-js'; // SHA256 암호화
import elliptic from 'elliptic'; // 타원곡선 알고리즘 결과를 리턴해주는 라이브러리
const ec = new elliptic.ec('secp256k1');
/** 지갑을 랜덤으로 만들어보기 */
describe('지갑 이해', () => {
let privKey: string;
let publicKey: string;
let signature: elliptic.ec.Signature;
let hash: string;
it('개인키 (privKey)', () => {
privKey = randomBytes(32).toString('hex'); // 랜덤으로 byte를 추출하여 그 Buffer로 리턴된 값을 hex로 string화 시킨다.
console.log('개인키', privKey);
/* 개인키 83c227b9fa8c7a4ebbbdbfdd67bcb802774d4fa59a592d8a273b61d699e91552 */
});
it('공개키 (publicKey)', () => {
const keyPair = ec.keyFromPrivate(privKey); // 비밀키를 가지고 타원곡선 알고리즘으로 암호화하여 공개키를 만듬
publicKey = keyPair.getPublic().encode('hex', true); // 위에서 뽑아온 값을 encode하여 16진수로 추출합니다.
console.log('공개키', publicKey);
/* 공개키 0223ebf69d01a2e5cb8141377245a5abcd7ef39abf8c8ac163073766f8641189b0 */
});
// 서명을 만들때 필요한 값 = 개인키, 해시값(transaction hash 값)
it('디지털 서명', () => {
const keyPair = ec.keyFromPrivate(privKey);
hash = SHA256('ingoo').toString(); // 추후에 거래내역을 암호화 하여 signature를 만듭니다.
signature = keyPair.sign(hash); // keyPair와 hash를 가지고 sigNature를 만듭니다. / signature를 가지고 publickey를 사용하여 이 개인키가 올바른지 아닌지 알 수 있다.
console.log('서명', signature);
/*
서명 Signature {
r: BN {
negative: 0,
words: [
35076865, 2682153,
49280326, 14095229,
38038072, 17798748,
54528708, 62799196,
13171684, 3278574
],
length: 10,
red: null
},
s: BN {
negative: 0,
words: [
6309881, 62125812, 35062084, 24295760,
55757757, 42834996, 33720198, 16977388,
23834472, 447880, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0
],
length: 10,
red: null
},
recoveryParam: 0
}
*/
});
// 서명을 검증할때 필요한 값 = 서명, 해시값(transaction hash 값), 공개키
it('서명 검증 (verify)', () => {
const verify = ec.verify(hash, signature, ec.keyFromPublic(publicKey, 'hex'));
console.log('검증', verify);
/* 검증 true */
});
it('계정 만들기', () => {
const buffer = Buffer.from(publicKey); // 공개키를 buffer로 암호화 값을 구합니다.
const address = buffer.slice(26).toString(); // buffer를 가지고 앞에 12byte를 자르고 뒤에 20byte를 가지고 address를 만듭니다.
console.log('주소', address);
/* 주소 45a5abcd7ef39abf8c8ac163073766f8641189b0 */
});
});
지갑 주소를 만들때는 만들어준 공개키의 앞의 24자리를 잘라내고 40자리만 남겨주면 지갑 주소가 됩니다.
지금buffer.slice(26)
로 26자리를 잘라준 이유는elliptic
을 사용하여 만들어진 공개키는 앞자리에 02 혹은 03이 붙기 때문에 이 값도 같이 제거해주기 위해 26자리를 잘라주었습니다.