14

bitcoin:压缩公钥与未压缩公钥

 4 years ago
source link: https://learnblockchain.cn/article/856
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

欢迎大家关注Rebase公众号

YNJVNzB.jpg!web

前文介绍

生成bitcoin地址 文章中得到了公钥 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d公钥其实是secp256k1椭圆曲线的一个坐标点 ,即(x,y)形式,用16进制表示是 (0xd061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69, 0x1757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d)

而且(x,y) 必然符合:

# python code
Pcurve = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 -1 #有限域
x = 0xd061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69
y = 0x1757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

x_res = x**3+7
y_res = y**2

(x_res%Pcurve) == (y_res%Pcurve)

为啥符合呢

比特币secp256k1椭圆曲线公式是 $y^2=x^3+7$ 椭圆曲线加密算法 定义在有限域 $\mathbb{F} p$上 假设 $y^2=x^3+7$ 在 $\mathbb{F} {23}$,

$x^3+7 \ mod \ 23$ 就是 ((x**3)+7) % 23

$y^2 \ mod \ 23$ 就是 (y**2)%23

((x**3)+7) % 23 == (y**2)%23 必然成立,不成立就不符合椭圆曲线加密的定义了。 secp256k1的有限域是Pcurve,Pcurve是个质数。

未压缩公钥

前缀04+x坐标+y坐标 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

压缩公钥

前缀03+x(如果y是奇数),前缀02+x(如果y是偶数)

0x1757......429d 从最后一位 0xd 来看,这个数是奇数,所以压缩公钥是 03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69

现在一般都使用压缩公钥, 压缩/未压缩公钥生成的地址确实会不一样, 未压缩公钥早已成了非主流。

通过压缩公钥得到 未压缩公钥

python code

没想到求个未压缩,常规方法不可行,计算量太大了,需要用到 二次剩余定理 ,二次剩余在 密码学以及大数分解中都很有用,还有个很出名的 Cipolla算法

def pow_mod(x, y, z):
    "Calculate (x ** y) % z efficiently"
    number = 1
    while y:
        if y & 1:
            number = number * x % z
        y >>= 1
        x = x * x % z
    return number

def get_uncompressed_key(compressed_key):
    Pcurve = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 -1

    y_parity = int(compressed_key[:2]) - 2
    x = int(compressed_key[2:], 16)

    a = (pow_mod(x, 3, Pcurve) + 7) % Pcurve

    y = pow_mod(a, (Pcurve+1)//4, Pcurve)

    if y % 2 != y_parity:
        y = -y % Pcurve

    uncompressed_key = '04{:x}{:x}'.format(x, y)
    print(uncompressed_key)

get_uncompressed_key("03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69")

js code

var compressedKey = "03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69";
var hex = compressedKey.replace(/^0x/, "");
var b = bitcoinjs.Buffer.Buffer.from(hex, "hex");

var keypair = bitcoinjs.bitcoin.ECPair.fromPublicKeyBuffer(b);
keypair.getPublicKeyBuffer().toString("hex");

var o = { compressed: false };
var displayKey = new bitcoinjs.bitcoin.ECPair(null, keypair.__Q, o);

console.log(displayKey.getPublicKeyBuffer().toString("hex"));

求secp256r1未压缩公钥

这是 secp256r1 ,是 prime256v1 ,是 NIST256p ,是 ecdsa-sha2-nistp256 ,也是 P256 椭圆曲线的计算代码, 不是btc用的secp256k1曲线

const { PublicKey } = require('bitcore-lib-p256')
  
const uncompress = key => {
  if (!key.compressed) {
    throw new Error('Publick key is not compressed.')
  }

  const x = key.point.getX()
  const y = key.point.getY()
  const xbuf = x.toBuffer({ size: 32,})
  
  const ybuf = y.toBuffer({ size: 32,})

  return Buffer.concat([Buffer.from([0x04]), xbuf, ybuf])
}
  
const pubKey = '023e3df0d294c19ed29a3e83a21648f7fc6ef9c1363c7dffc7b3650e1f08d98032'
const pubKeyObj = PublicKey.fromString(pubKey)
const rs = uncompress(pubKeyObj).toString('hex')
console.log('rs', rs)

比特币地址

以下是同一个私钥,不同类型的公钥生成的不同地址。 代码见 gen_addr

#############未压缩公钥生成的地址#############
14xfJr1DArtYR156XBs28FoYk6sQqirT2s
35egEPVeimCvWAmXeHXcYtAUtdA8RtsNUY
mjUcbu6BytKoC7YiEkqPxB1sc6U7nnjFse

#############压缩公钥生成的地址#############
1ASfqPzBTfPSBA9DWdHYYNk4qM5NoGNtzL
3B8gkwUd1ZhpGKqedix8y16zysN6QWqQxS
mpxd8T5AGgpgxGcqECFvNHxPhLg5of8Sh3

参考:

https://github.com/iancoleman/keycompression https://www.mina.moe/archives/11441 https://blog.mythsman.com/post/5d2c986667f841464434a58e/ https://bitcointalk.org/index.php?topic=644919 https://git.coolaj86.com/coolaj86/eckles.js https://crypto.stackexchange.com/a/42906

YNJVNzB.jpg!web

前文介绍

生成bitcoin地址 文章中得到了公钥 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d公钥其实是secp256k1椭圆曲线的一个坐标点 ,即(x,y)形式,用16进制表示是 (0xd061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69, 0x1757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d)

而且(x,y) 必然符合:

# python code
Pcurve = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 -1 #有限域
x = 0xd061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69
y = 0x1757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

x_res = x**3+7
y_res = y**2

(x_res%Pcurve) == (y_res%Pcurve)

为啥符合呢

比特币secp256k1椭圆曲线公式是 $y^2=x^3+7$ 椭圆曲线加密算法 定义在有限域 $\mathbb{F} p$上 假设 $y^2=x^3+7$ 在 $\mathbb{F} {23}$,

$x^3+7 \ mod \ 23$ 就是 ((x**3)+7) % 23

$y^2 \ mod \ 23$ 就是 (y**2)%23

((x**3)+7) % 23 == (y**2)%23 必然成立,不成立就不符合椭圆曲线加密的定义了。 secp256k1的有限域是Pcurve,Pcurve是个质数。

未压缩公钥

前缀04+x坐标+y坐标 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

压缩公钥

前缀03+x(如果y是奇数),前缀02+x(如果y是偶数)

0x1757......429d 从最后一位 0xd 来看,这个数是奇数,所以压缩公钥是 03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69

现在一般都使用压缩公钥, 压缩/未压缩公钥生成的地址确实会不一样, 未压缩公钥早已成了非主流。

通过压缩公钥得到 未压缩公钥

python code

没想到求个未压缩,常规方法不可行,计算量太大了,需要用到 二次剩余定理 ,二次剩余在 密码学以及大数分解中都很有用,还有个很出名的 Cipolla算法

def pow_mod(x, y, z):
    "Calculate (x ** y) % z efficiently"
    number = 1
    while y:
        if y & 1:
            number = number * x % z
        y >>= 1
        x = x * x % z
    return number

def get_uncompressed_key(compressed_key):
    Pcurve = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 -1

    y_parity = int(compressed_key[:2]) - 2
    x = int(compressed_key[2:], 16)

    a = (pow_mod(x, 3, Pcurve) + 7) % Pcurve

    y = pow_mod(a, (Pcurve+1)//4, Pcurve)

    if y % 2 != y_parity:
        y = -y % Pcurve

    uncompressed_key = '04{:x}{:x}'.format(x, y)
    print(uncompressed_key)

get_uncompressed_key("03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69")

js code

var compressedKey = "03d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f69";
var hex = compressedKey.replace(/^0x/, "");
var b = bitcoinjs.Buffer.Buffer.from(hex, "hex");

var keypair = bitcoinjs.bitcoin.ECPair.fromPublicKeyBuffer(b);
keypair.getPublicKeyBuffer().toString("hex");

var o = { compressed: false };
var displayKey = new bitcoinjs.bitcoin.ECPair(null, keypair.__Q, o);

console.log(displayKey.getPublicKeyBuffer().toString("hex"));

求secp256r1未压缩公钥

这是 secp256r1 ,是 prime256v1 ,是 NIST256p ,是 ecdsa-sha2-nistp256 ,也是 P256 椭圆曲线的计算代码, 不是btc用的secp256k1曲线

const { PublicKey } = require('bitcore-lib-p256')

const uncompress = key => {
  if (!key.compressed) {
    throw new Error('Publick key is not compressed.')
  }

  const x = key.point.getX()
  const y = key.point.getY()
  const xbuf = x.toBuffer({ size: 32,})

  const ybuf = y.toBuffer({ size: 32,})

  return Buffer.concat([Buffer.from([0x04]), xbuf, ybuf])
}

const pubKey = '023e3df0d294c19ed29a3e83a21648f7fc6ef9c1363c7dffc7b3650e1f08d98032'
const pubKeyObj = PublicKey.fromString(pubKey)
const rs = uncompress(pubKeyObj).toString('hex')
console.log('rs', rs)

比特币地址

以下是同一个私钥,不同类型的公钥生成的不同地址。 代码见 gen_addr

#############未压缩公钥生成的地址#############
14xfJr1DArtYR156XBs28FoYk6sQqirT2s
35egEPVeimCvWAmXeHXcYtAUtdA8RtsNUY
mjUcbu6BytKoC7YiEkqPxB1sc6U7nnjFse

#############压缩公钥生成的地址#############
1ASfqPzBTfPSBA9DWdHYYNk4qM5NoGNtzL
3B8gkwUd1ZhpGKqedix8y16zysN6QWqQxS
mpxd8T5AGgpgxGcqECFvNHxPhLg5of8Sh3

参考:

https://github.com/iancoleman/keycompression https://www.mina.moe/archives/11441 https://blog.mythsman.com/post/5d2c986667f841464434a58e/ https://bitcointalk.org/index.php?topic=644919 https://git.coolaj86.com/coolaj86/eckles.js https://crypto.stackexchange.com/a/42906

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 13分钟前
  • 阅读 ( 9 )
  • 学分 ( 0 )
  • 分类:比特币

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK