为什么 secp256k1 未压缩 public 密钥未按预期格式化?
Why are secp256k1 uncompressed public keys not formatted as expected?
一点 Rust 代码:
let secret_key = SecretKey::from_slice(&rand::thread_rng().gen::<[u8; 32]>()).expect("32 bytes, within curve order");
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
let pk : [u8; 33] = public_key.serialize();
println!("secret_key {:?}", secret_key);
println!("public_key {:?}", public_key);
println!("pk {:x?}", &pk[0..32]);
产生以下形式的输出:
secret_key SecretKey(03697e6c2168bd5f99f4df7086adf0598f9ae97cced61b072dded9f77a1e837f)
public_key PublicKey(b53c8b4a40a9512037199f4047376905cbe8008be1612e166cb01fa35a60b0c9cd17d5ef86f9c92590fe1125f0fa9b478ca5966eb4be1454b0171adaebe35cfd)
pk [3, c9, b0, 60, 5a, a3, 1f, b0, 6c, 16, 2e, 61, e1, 8b, 0, e8, cb, 5, 69, 37, 47, 40, 9f, 19, 37, 20, 51, a9, 40, 4a, 8b, 3c]
secret_key SecretKey(6d8b97d7bfc4240589d5b523dd5d87096dcbd5b14ea5f780912ab713f6fcfbb3)
public_key PublicKey(3484567baaa84424ceeb76456b6f4ec54efac4c4aa5a06215733aa5d5b2f0d06120a5d05f68dfb5a8b6c204b5efa37201350cfb7905014d239e5fd71e1e23775)
pk [2, 6, d, 2f, 5b, 5d, aa, 33, 57, 21, 6, 5a, aa, c4, c4, fa, 4e, c5, 4e, 6f, 6b, 45, 76, eb, ce, 24, 44, a8, aa, 7b, 56, 84]
secret_key SecretKey(60778385b0110ea8b780f27ca96fce19dfe37d8ad94d2492a0c5595261e9fa49)
public_key PublicKey(db1f88690d138e88544d6a47ed7718cf048abd969cfb79684a3dc19053661bc9c3341094dc84aef237832c993bc16df4b5cf0df27eaa4508ea6f7b67f4062ed7)
pk [3, c9, 1b, 66, 53, 90, c1, 3d, 4a, 68, 79, fb, 9c, 96, bd, 8a, 4, cf, 18, 77, ed, 47, 6a, 4d, 54, 88, 8e, 13, d, 69, 88, 1f]
序列化的 public 密钥看起来符合预期,33 字节,以 0x02 或 0x03 开头,表示缺少的 Y 组件的符号。
X 组件存在于未压缩的 public 密钥中 db1f8869...
但它偏移了一个字节并被反转。这只为 Y 分量留下了 31 个字节,这似乎不够。
未压缩和压缩的 public 键的字节序是什么?
我有什么不明白的?
“偏移一个字节”的问题是因为你的切片索引有误。第二个值是 过去 要包含的最后一个索引,所以你想要 &pk[0..33]
而不是 &pk[0..32]
,或者因为那是整个数组 &pk[..]
.
反转字节是因为 public 键的调试输出只是简单地转储了键的内部表示。在内部,密钥保存为 int64
s 的数组,它被直接复制到 char 数组中,因此在小端系统上,这看起来像字节被反转*。使用 serialize
(或 {}
格式说明符)时,密钥的格式正确,最高有效字节在前。
*实际上比这要复杂一点,计算是用52位的整数数组完成的,然后打包成一个更密集的“存储”形式,使用所有64位。
secp256k1
crate 中还有另一种方法,您可以调用 public 密钥来获取名为 serialize_uncompressed()
的未压缩密钥。 (然后您需要删除第一个字节以获得 64 字节密钥)。
一点 Rust 代码:
let secret_key = SecretKey::from_slice(&rand::thread_rng().gen::<[u8; 32]>()).expect("32 bytes, within curve order");
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
let pk : [u8; 33] = public_key.serialize();
println!("secret_key {:?}", secret_key);
println!("public_key {:?}", public_key);
println!("pk {:x?}", &pk[0..32]);
产生以下形式的输出:
secret_key SecretKey(03697e6c2168bd5f99f4df7086adf0598f9ae97cced61b072dded9f77a1e837f)
public_key PublicKey(b53c8b4a40a9512037199f4047376905cbe8008be1612e166cb01fa35a60b0c9cd17d5ef86f9c92590fe1125f0fa9b478ca5966eb4be1454b0171adaebe35cfd)
pk [3, c9, b0, 60, 5a, a3, 1f, b0, 6c, 16, 2e, 61, e1, 8b, 0, e8, cb, 5, 69, 37, 47, 40, 9f, 19, 37, 20, 51, a9, 40, 4a, 8b, 3c]
secret_key SecretKey(6d8b97d7bfc4240589d5b523dd5d87096dcbd5b14ea5f780912ab713f6fcfbb3)
public_key PublicKey(3484567baaa84424ceeb76456b6f4ec54efac4c4aa5a06215733aa5d5b2f0d06120a5d05f68dfb5a8b6c204b5efa37201350cfb7905014d239e5fd71e1e23775)
pk [2, 6, d, 2f, 5b, 5d, aa, 33, 57, 21, 6, 5a, aa, c4, c4, fa, 4e, c5, 4e, 6f, 6b, 45, 76, eb, ce, 24, 44, a8, aa, 7b, 56, 84]
secret_key SecretKey(60778385b0110ea8b780f27ca96fce19dfe37d8ad94d2492a0c5595261e9fa49)
public_key PublicKey(db1f88690d138e88544d6a47ed7718cf048abd969cfb79684a3dc19053661bc9c3341094dc84aef237832c993bc16df4b5cf0df27eaa4508ea6f7b67f4062ed7)
pk [3, c9, 1b, 66, 53, 90, c1, 3d, 4a, 68, 79, fb, 9c, 96, bd, 8a, 4, cf, 18, 77, ed, 47, 6a, 4d, 54, 88, 8e, 13, d, 69, 88, 1f]
序列化的 public 密钥看起来符合预期,33 字节,以 0x02 或 0x03 开头,表示缺少的 Y 组件的符号。
X 组件存在于未压缩的 public 密钥中 db1f8869...
但它偏移了一个字节并被反转。这只为 Y 分量留下了 31 个字节,这似乎不够。
未压缩和压缩的 public 键的字节序是什么?
我有什么不明白的?
“偏移一个字节”的问题是因为你的切片索引有误。第二个值是 过去 要包含的最后一个索引,所以你想要 &pk[0..33]
而不是 &pk[0..32]
,或者因为那是整个数组 &pk[..]
.
反转字节是因为 public 键的调试输出只是简单地转储了键的内部表示。在内部,密钥保存为 int64
s 的数组,它被直接复制到 char 数组中,因此在小端系统上,这看起来像字节被反转*。使用 serialize
(或 {}
格式说明符)时,密钥的格式正确,最高有效字节在前。
*实际上比这要复杂一点,计算是用52位的整数数组完成的,然后打包成一个更密集的“存储”形式,使用所有64位。
secp256k1
crate 中还有另一种方法,您可以调用 public 密钥来获取名为 serialize_uncompressed()
的未压缩密钥。 (然后您需要删除第一个字节以获得 64 字节密钥)。