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

Mor*_*ert 2 openssl swift apple-cryptokit x25519

假设我使用 openssl 创建一个 x25519 密钥对,它将输出一个 64 字节私钥和相应的 44 字节 Base64 编码公钥,如下所示

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


-----BEGIN PUBLIC KEY-----
MCowBQYDK2VuAyEAE0eiiP0PKjy9AVM/0z2ZIZn453WSJNemrQ58HAXDaX0=
-----END PUBLIC KEY-----
Run Code Online (Sandbox Code Playgroud)

Swift CryptoKit 仅接受私钥和公钥初始化各 32 个字节。

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

尽管如此,对公钥使用相同的原理是行不通的(这并不令人惊讶)

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

这是使用 Base64 解码公钥的前 32 个字节的无效示例

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

let publicKey = try! Curve25519.KeyAgreement.PublicKey(rawRepresentation: rawPublicKey) 
Run Code Online (Sandbox Code Playgroud)

dav*_*085 6

-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEIMBF8S7zUco4bRrMiIuyTcSYU/rAVlNtE8SMYWphUatw
-----END PRIVATE KEY-----
Run Code Online (Sandbox Code Playgroud)

per rfc7468是一个 PKCS8 未加密的PrivateKeyInfo,它以ASN.1 DER编码,包含有关算法的数据(一般情况下但不是这里的参数)以及实际密钥。将其运行到openssl asn1parse -i(自动 de-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
Run Code Online (Sandbox Code Playgroud)

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

-----BEGIN PUBLIC KEY-----
MCowBQYDK2VuAyEAE0eiiP0PKjy9AVM/0z2ZIZn453WSJNemrQ58HAXDaX0=
-----END PUBLIC KEY-----
Run Code Online (Sandbox Code Playgroud)

类似的是X.509和PKIX定义的SubjectPublicKeyInfo结构,它同样是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
Run Code Online (Sandbox Code Playgroud)

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


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