체인의정석

Cloud HSM에서 ec-point를 통해 유효한 공개키, 지갑주소 뽑아내기 본문

블록체인/퍼블릭 블록체인

Cloud HSM에서 ec-point를 통해 유효한 공개키, 지갑주소 뽑아내기

체인의정석 2025. 6. 25. 12:20
728x90

Cloud HSM에서 공개키에 대한 값을 받을 때 지갑 주소 형태가 아닌 ec-point 라는 값을 받았다.

찾아보니 해당 값에서 공개키를 도출할 때는 상황에 따라 다 다르게 도출을 해야하는것 같다.

일단 설정 값 중 "key-length-bytes": 135 라는 문구가 보였는데

원래 HSM을 쓰기 전 정석적으로 65바이트의 공개키를 사용하고 이후에 그 공개키를 지갑 주소로 변환하는 형태이다.
따라서 135 만큼의 길이가 있다는 것은 특정 부분을 제외하고 공개키를 찾아야 한다는 점이다.

📦 정석적인 EC 공개키 길이

Ethereum에서 사용하는 secp256k1 곡선 기준:

항목 길이 (bytes)

0x04 (접두어) 1 byte
X 좌표 32 bytes
Y 좌표 32 bytes
합계 (정상 EC_POINT) 65 bytes

 

그럼 앞부분은 어떤 의미를 가질까?

패딩 또는 길이 필드 포함 HSM 구현체에 따라 `04
ASN.1 또는 DER 헤더 포함 PKCS#11 또는 CloudHSM은 EC_POINT를 DER 인코딩된 구조로 내보낼 수 있고, 헤더가 앞에 붙거나 중간에 붙음
앞뒤로 5~7바이트의 추가 구조 135 - 65 = 70 bytes의 초과는 없지만, **135 - 128 = 7 bytes**라면 공개키 외에 앞부분에 불필요한 bytes 7개가 붙어 있을 가능성이 있음
마지막 128바이트가 X + Y 좌표 따라서 실제 Ethereum 주소에 필요한 정보는 뒤에서 128 글자 = 64바이트 x좌표 + y좌표만 있다는 해석 가능

HSM의 특성에 따라서 구현체에 따른 부분 헤더, 불필요한 bytes등이 들어 잇을 수 있으며 실제 유효한 값은 뒤의 128글자에 해당되는 64byte 라고 한다.  한마디로 "key-length-bytes": 135라는 것은 HSM이 EC 공개키를 내보내면서 앞에 비표준적인 header나 padding을 포함하고 있다는 뜻이며 이에 따라 공개키를 구할 때 마지막 부분을 추출해서 공개키를 만든 후에 지갑 주소로 이후에 만드는 과정이 필요하다.

따라서 해당 방식을 통해 아래와 같은 스크립트를 hardhat에 넣어서 cloud hsm의 지갑주소를 도출할 수 있었다.

import { keccak256 } from 'ethereum-cryptography/keccak';
import { hexToBytes, bytesToHex } from 'ethereum-cryptography/utils';

/**
 * CloudHSM EC_POINT에서 Ethereum 주소 도출
 *
 * 일부 HSM 구현은 EC 공개키(EC_POINT)를 DER 또는 비표준 구조로 내보내며,
 * 선행 바이트(header, padding 등)가 포함되어 있을 수 있습니다.
 *
 * 따라서 가장 신뢰할 수 있는 방식은 EC_POINT의 마지막 128자리 hex 문자열
 * (즉, 64바이트: 32바이트 X 좌표 + 32바이트 Y 좌표)만을 추출하여 keccak256 해싱을 수행하고,
 * 그 해시의 마지막 20바이트를 Ethereum 주소로 사용하는 것입니다.
 *
 * @param ecPointHex EC_POINT 값 (hex string, 예: '04abcdef...')
 * @returns Ethereum address (0x-prefixed)
 */
export function deriveEthAddressFromEcPoint(ecPointHex: string): string {
  const cleanHex = ecPointHex.toLowerCase().replace(/^0x/, '');

  if (cleanHex.length < 128) {
    throw new Error('EC_POINT 길이가 너무 짧습니다 (최소 128자 필요).');
  }

  const last128 = cleanHex.slice(-128); // 마지막 64바이트 (X + Y 좌표)
  const pubkeyBytes = hexToBytes(last128);
  const hash = keccak256(pubkeyBytes);
  return '0x' + bytesToHex(hash.slice(-20));
}

// 예시 실행
const ecPoint = 'ECPOINT 값';
console.log('🪪 ETH Address:', deriveEthAddressFromEcPoint(ecPoint));

 

728x90
반응형
Comments