如何将由 openssl 创建的 44 字节 x25519 public 密钥传递给需要 32 字节密钥长度的 CryptoKit

How do I pass a 44 Bytes x25519 public key created by openssl to CryptoKit which requires a key length of 32 Bytes

假设我使用 openssl 创建一个 x25519 密钥对,它将输出一个 64 字节的私钥和相应的 44 字节的 Base64 编码 public 密钥,看起来像

-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEIMBF8S7zUco4bRrMiIuyTcSYU/rAVlNtE8SMYWphUatw
-----END PRIVATE KEY-----


-----BEGIN PUBLIC KEY-----
MCowBQYDK2VuAyEAE0eiiP0PKjy9AVM/0z2ZIZn453WSJNemrQ58HAXDaX0=
-----END PUBLIC KEY-----

Swift CryptoKit 只接受每个 32 字节,私有和 public 密钥初始化。

如果我没理解错的话,64 字节的私钥是种子,其中前 32 字节是实际的私钥。

仍然,对 public 键使用相同的原理是行不通的(其实并不奇怪)

现在的问题是:如何将 public 密钥转换为 Swift CryptoKit 所需的 32 字节?

这是使用 base64 解码的前 32 个字节的非功能示例 public 密钥

let base64PublicKey = Data(base64Encoded: "MCowBQYDK2VuAyEAE0eiiP0PKjy9AVM/0z2ZIZn453WSJNemrQ58HAXDaX0=")!.dropLast(12)

let publicKey = try! Curve25519.KeyAgreement.PublicKey(rawRepresentation: rawPublicKey) 
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEIMBF8S7zUco4bRrMiIuyTcSYU/rAVlNtE8SMYWphUatw
-----END PRIVATE KEY-----

per rfc7468 is a PKCS8-unencrypted PrivateKeyInfo which is encoded in ASN.1 DER 并包含有关算法的数据(一般情况下但不是此处的参数)以及实际密钥。 运行 这变成 openssl asn1parse -i (自动取消 base64)给出

    0:d=0  hl=2 l=  46 cons: SEQUENCE
    2:d=1  hl=2 l=   1 prim:  INTEGER           :00
    5:d=1  hl=2 l=   5 cons:  SEQUENCE
    7:d=2  hl=2 l=   3 prim:   OBJECT            :X25519
   12:d=1  hl=2 l=  34 prim:  OCTET STRING      [HEX DUMP]:0420C045F12EF351CA386D1ACC888BB24DC49853FAC056536D13C48C616A6151AB70

算法特定的私钥是 OCTETSTRING,偏移量为 12+2,长度为 34,但它实际上包含一个嵌套的 OCTETSTRING 编码,其前两个八位字节是 04=tag 和 20=length,因此真正的私钥是在偏移量 16 处,长度为 32——或者更简单地说是最后 32 个字节。

-----BEGIN PUBLIC KEY-----
MCowBQYDK2VuAyEAE0eiiP0PKjy9AVM/0z2ZIZn453WSJNemrQ58HAXDaX0=
-----END PUBLIC KEY-----

similarly is the SubjectPublicKeyInfo 由 X.509 和 PKIX 定义的结构,它同样是 DER,除了密钥之外还包含数据。解析它(使用 -dump)给出:

    0:d=0  hl=2 l=  42 cons: SEQUENCE
    2:d=1  hl=2 l=   5 cons:  SEQUENCE
    4:d=2  hl=2 l=   3 prim:   OBJECT            :X25519
    9:d=1  hl=2 l=  33 prim:  BIT STRING
      0000 - 00 13 47 a2 88 fd 0f 2a-3c bd 01 53 3f d3 3d 99   ..G....*<..S?.=.
      0010 - 21 99 f8 e7 75 92 24 d7-a6 ad 0e 7c 1c 05 c3 69   !...u.$....|...i
      0020 - 7d

BITSTRING 值的第一个八位位组用于 unused/pad 位数,此处为 00,因此真正的公钥值是偏移量 9+2+1=12 处的 33-1=32 个八位位组,或者最后 32 个字节。


Ed25519 对私钥进行哈希处理,生成一个有时称为种子的 32 字节标量和一个确定公钥的 32 字节值。此种子可以与私钥一起存储,以提高签名效率,但 OpenSSL 不会为 Ed25519 执行此操作,并且它根本不适用于 X25519。