일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 시퀄라이즈 기본설정
- 라우터와 미들웨어
- 세션으로 로그인 구현
- cookie-parser 만들어보기
- next 매개변수
- buffer.from
- nodejs파일업로드
- useContext
- node.js path
- javascript기초
- express실행
- JWT 로그인 기능 구현
- 블록 만들기
- JWT 하드코딩
- 아이디 중복체크기능
- express session
- mysql wsl
- JWT 만들어보기
- ws 라이브러리
- FormData()
- 비동기파일업로드
- useEffect clean up
- 라우터미들웨어 분리
- express.static
- 라우터 분리
- OAuth 카카오
- 라우트 매개변수
- express router
- css기초
- Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
- Today
- Total
즐코
지갑과 트랜잭션에서의 서명/검증 (Ft.개인키,공개키) 본문
지갑, 트랜잭션에 대해 이해하려면 개인키/공개키를 알아야하므로 이와 관련된 암호화 개념부터 시작한다.
1/ 암호화 기법
지갑에 대해서 알려면 우선 암호화기법에 대해 가볍게 짚고 넘어가야한다.
우선 암호화를 하는 가장 중요한 이유는 바로 무결성이다.
어떠한 정보나 데이터가 원본으로부터 조작되지 않았는지 무결성을 체크하는데 있어서 이 해시알고리즘이 쓰인다고 보면 된다.
1. 양방향암호화 - 대칭형
대칭키 하나로 암호화/복호화가 가능하다. 암호화한 정보를 다른 사람에게 보낼 때 이 대칭키도 같이 보내야하기때문에 대칭키의 보안이 매우 중요하다.
2. 양방향암호화 - 비대칭형
암호화하는데 있어서 공개키(public key)를 사용하고 나중에 복호화 시 개인키(private key)를 쓴다. (반대로도 가능하다!)
원리를 조금 더 자세하게 설명하자면,
1/ x라는 사람이 개인키를 먼저 생성하고 이에 맞게 공개키를 생성한다. 즉 2개의 키 (한쌍의 키페어)를 가지고 있는 셈이다.
2/ 공개키는 아무나 다 볼 수 있다. 이 때, y라는 사람이 x에게 보내고 싶은 정보, 데이터를 이 공개키로 암호화해서 x에게 보내준다.
3/ x는 이 해시된 정보를 자신만 가지고 있는 개인키로 복호화해서 해당 정보를 알 수 있게 된다.
=> 대칭키보다 훨씬 보안에 좋으며, 블록체인 네트워크 상에서 블록 데이터의 무결성, 신원을 검증하는데 있어서 전자서명이 쓰이는데 이때 대칭형 대신 비대칭형 암호화가 사용된다.
2/ 지갑에서의 개인키, 공개키, 주소 || 계정
지갑이란 무엇이냐
각 거래하는 사람들의 계좌번호(계정)만 관리하는 프로그램이다. (개인키는 별도로 내 로컬에 저장한다.)
그리고 이 지갑은 무조건 블록체인으로 만들어야하는 게 아니다. 지갑은 하나의 프로그램 같은 것이므로 많이들 앱을 다운받아서 쓰곤 한다. 대표적으로 메타마스크가 있다.
개인키
256자리의 2진수(0,1) 랜덤문자열을 64자리의 16진수 문자열로 만든 것을 개인키라고 한다.
즉, 겹칠 확률이 1/2^256 로 거의 0에 가깝다고 보면 된다.
코드로 개인키를 만들어보면 아래와 같다. 랜덤으로 32바이트(256비트)의 2진수 문자열을 만들고 그걸 16진수로 변환하는 코드이다.
// core/wallet/wallet.test.ts
import { randomBytes } from "crypto";
describe("지갑이해하기", () => {
let privateKey: string;
it("개인키_privateKey", () => {
privateKey = randomBytes(32).toString("hex");
console.log("개인키:", privateKey);
// 개인키: cd9019de2593c2c6cdb1ad1fb351b3e4a73bcc86341fd9e14435414aa4371bc7
});
});
공개키
거래에 있어서 개인키를 공개하는 건 보안같은 건 집어치우란 소리이므로 개인키와 한쌍의 키페어를 이루는 공개키를 생성해야한다.
개인키를 elliptic(타원곡선알고리즘..?)으로 돌려 공개키 public key를 얻는다. 물론 이또한 64자리의 16진수 문자열로 변환해준다.
코드로 공개키를 만들어보면 아래와 같다. elliptic이라는 타원곡선알고리즘을 사용하는 암호화 라이브러리를 사용해준다.
위에서 만든 개인키를 이용하여 키페어를 구하고, 거기서 public key를 꺼내서 16진수로 바꿔준다.
다만, 개인키처럼 공개키도 64글자가 나와야하는데 콘솔창에 찍어보면 66글자가 나온다.
이는 앞에 기본적으로 03,02 처럼 0으로 시작하는 2자리의 고정값이 붙어서 그렇다고 한다.
// core/wallet/wallet.test.ts
import { randomBytes } from "crypto";
import elliptic from "elliptic";
const ec = new elliptic.ec("secp256k1");
describe("지갑이해하기", () => {
let privateKey: string;
let publicKey: string;
it("개인키_privateKey", () => {
privateKey = randomBytes(32).toString("hex");
});
it("공개키 생성하기", () => {
const keyPair = ec.keyFromPrivate(privateKey);
publicKey = keyPair.getPublic().encode("hex", true);
console.log("공개키:", publicKey);
// 공개키: 0319cb93f021c710a2754233d0e82f16e7699f973a1b27deae670f9bc3c8d7f87c
});
});
주소 || 계정
주소 또는 계정은 코인들마다 생성하는 방식이 다르다고 한다. (부르는 방법도 코인들마다 다르다고 한다. 비트는 주소, 이더는 계정) - 비트코인 주소 : 공개키를 2번의 다른 암호화방식을 거쳐 만든다.- 이더리움 계정 : 공개키 32바이트(64자리) 중 앞의 12바이트(24자리)를 잘라서 총 40자리의 문자열
코인마다 주소/계정 생성방식이 다르다곤 하지만 공통점은 공개키만을 이용한다는 것이다.
이 주소/계정은 편의를 위해 존재하는 것이다. 거래내역에 있어서 보낸이/받는이를 표현하기 위한 수단정도로 생각하면 된다.
이런 측면에서볼 때 주소/계정을 만드는데 있어서 비트코인처럼 암호화를 2번 거치는 건 비효율적이란 소리도 있다..
코드로 짜보면 아래의 계정만들기와 같다. 다만, 공개키의 앞 2자리 고정값 (0x)도 추가로 빼줘야하므로 26자리를 제거했다.
// core/wallet/wallet.test.ts
import { randomBytes } from "crypto";
import elliptic from "elliptic";
const ec = new elliptic.ec("secp256k1");
describe("지갑이해하기", () => {
let privateKey: string;
let publicKey: string;
it("개인키_privateKey", () => {
privateKey = randomBytes(32).toString("hex");
});
it("공개키 생성하기", () => {
const keyPair = ec.keyFromPrivate(privateKey);
publicKey = keyPair.getPublic().encode("hex", true);
console.log("공개키:", publicKey);
// 공개키: 02e04eb0c453a9e72480aa62cdabfaa5740243899dfeaebadcc3e1d3d2ae6b039f
});
it("계정만들기", () => {
const buffer = Buffer.from(publicKey);
const address = buffer.slice(26).toString();
console.log(address); // abfaa5740243899dfeaebadcc3e1d3d2ae6b039f
console.log(address.length); // 40
});
});
3/ 트랜잭션에서의 서명과 검증
서명 signature
거래내역(보내는 이, 받는 이, 금액, 수수료 등등)을 보낼 때 이 트랜잭션의 무결성 체크를 위해 검증 절차를 거친다. 이 검증절차에 필요한 게 서명이다. 이 때 OTP 또는 비밀번호와 같은 개인키를 던지면 안되나싶지만, 우리는 현재 탈중앙화의 블록체인 네트워크에 있다는 걸 잊으면 안된다. 기존의 은행 거래 같은 경우 데이터를 관리하는 은행에서 보안을 담당해주지만, 블록체인네트워크에선 그런 개념이 없다. 개인키 자체를 던지면 비밀번호를 알려주는 것과 마찬가지이다. 그렇기 때문에 서명을 만들어서 거래를 하는 것이다.
서명에 필요한 값은 아래와 같다.
1- private key
2- transaction hash (SHA256 알고리즘으로 해싱한 거래내역)
코드로 짜보면 아래의 디지털 서명과 같다. 트랜잭션 내용을 SHA256으로 해쉬한 값을 개인키로 암호화하고 16진수로 바꿔준다.
즉, 개인키 자체를 던지는게 아니라 이 개인키를 암호화하는데 사용하는 것이다.
이제 만들어진 서명으로 검증 단계를 거친다.
검증
넘어온 서명(signature)을 같이 넘어온 공개키로 복호화했을 때, 복호화한 이 hash값과 데이터를 해싱한 transaction hash와 일치하면, 그 공개키와 개인키가 한 쌍인 것을 인증한 것이므로 검증이 완료된 것이다.
즉, 트랜잭션의 신원과 무결성을 인증한 것이다.
신원 확인 : 넘어온 공개키로 복호화할 수있는 건 오로지 공개키와 한 쌍인 개인키로 암호화한 내용들이므로, 넘겨받은 서명이 공개키로 복호화가 된다면 그 개인키를 소유한 사람이 만든 서명임을 검증할 수 있다.
데이터의 무결성 : 데이터를 해싱한 transaction hash와 서명을 복호화해서 나온 해시값의 일치 여부를 확인해서 데이터가 조작되지않았는지 확인할 수 있다.
맨 아래 검증 Verify 부분의 코드를 보면 된다.
1- 우선 transaction 내용을 SHA256으로 해쉬화하고
2- 그 transaction hash와 서명 및 공개키를 검증메소드 (ec.verify)에 넣어주면 boolean값이 떨어진다. true면 검증 완료
// core/wallet/wallet.test.ts
import { randomBytes } from "crypto";
import elliptic from "elliptic";
import { SHA256 } from "crypto-js";
const ec = new elliptic.ec("secp256k1");
describe("지갑이해하기", () => {
let privateKey: string;
let publicKey: string;
it("개인키_privateKey", () => {
privateKey = randomBytes(32).toString("hex");
});
it("공개키 생성하기", () => {
const keyPair = ec.keyFromPrivate(privateKey);
publicKey = keyPair.getPublic().encode("hex", true);
console.log("공개키:", publicKey);
// 공개키: 02e04eb0c453a9e72480aa62cdabfaa5740243899dfeaebadcc3e1d3d2ae6b039f
});
it("계정만들기", () => {
const buffer = Buffer.from(publicKey);
const address = buffer.slice(26).toString();
console.log(address); // abfaa5740243899dfeaebadcc3e1d3d2ae6b039f
console.log(address.length); // 40
});
it("디지털 서명", () => {
const keyPair = ec.keyFromPrivate(privateKey);
const hash = SHA256("ingoo").toString();
signature = keyPair.sign(hash, "hex");
// ec.sign(keyPair, hash)
});
it("검증 Verify", () => {
const hash = SHA256("ingoo").toString();
const verify = ec.verify(hash, signature, ec.keyFromPublic(publicKey, "hex"));
console.log(verify);
});
});
'BlockChain' 카테고리의 다른 글
지갑, 트랜잭션 구현 (2) / 트랜잭션 풀 (0) | 2022.06.19 |
---|---|
HTTP 기본 인증 / 지갑, 트랜잭션 구현 (1) (0) | 2022.06.19 |
P2P 네트워크 / 블록체인 주고받기 (0) | 2022.06.15 |
논스 추가 + 난이도 조절 및 블록 체인 만들기 (0) | 2022.06.14 |
블럭수정 + 블록 검증 코드 추가 (0) | 2022.06.14 |