通过 ec public 键坐标构建 PEM 文件

Build PEM file by having ec public key coordinates

我尝试通过从给定数字(我的私钥)计算曲线上的点来创建椭圆 public 密钥,所以我有椭圆曲线点 [=24] 的坐标 (x,y) =]

我通过

得到坐标
myPublicKeyCoordinates = myPrivateKeyValue * GPointOnCurve

如何为我的 public 密钥构建 PEM(或 DER)文件?

我不关心语言 (java, python, javascript, ...)
因为我想知道如何构建文件(即使我写了每个字节...)

假设您已经从高效密码学标准组(而非美国证券交易委员会)了解 ITU-T X.680-201508 (the ASN.1 language) and ITU-T X.690-201508 (the BER (and CER) and DER encodings for ASN.1 data), the main defining document for Elliptic Curve Keys and their representation is https://www.secg.org/sec1-v2.pdf

C.3 节(椭圆曲线 Public 键的语法)说 EC public 键的通用传输容器是 X.509 SubjectPublicKeyInfo 结构:

SubjectPublicKeyInfo ::= SEQUENCE {
    algorithm AlgorithmIdentifier {{ECPKAlgorithms}} (WITH COMPONENTS
        {algorithm, parameters}) ,
    subjectPublicKey BIT STRING
}

可能的“算法”(实际上是指密钥编码类型)是 open-ended 集

ECPKAlgorithms ALGORITHM ::= {
    ecPublicKeyType |
    ecPublicKeyTypeRestricted |
    ecPublicKeyTypeSupplemented |
    {OID ecdh PARMS ECDomainParameters {{SECGCurveNames}}} |
    {OID ecmqv PARMS ECDomainParameters {{SECGCurveNames}}},
    ...
}

ecPublicKeyType ALGORITHM ::= {
    OID id-ecPublicKey PARMS ECDomainParameters {{SECGCurveNames}}
}

...

ECDomainParameters 来自 C.2:

ECDomainParameters{ECDOMAIN:IOSet} ::= CHOICE {
    specified SpecifiedECDomain,
    named ECDOMAIN.&id({IOSet}),
    implicitCA NULL
}

C.3 提到一半

The elliptic curve public key (a value of type ECPoint that is an OCTET STRING) is mapped to a subjectPublicKey (a value encoded as type BIT STRING) as follows: The most significant bit of the value of the OCTET STRING becomes the most significant bit of the value of the BIT STRING and so on with consecutive bits until the least significant bit of the OCTET STRING becomes the least significant bit of the BIT STRING.

所以我们向后寻找并找到

An elliptic curve point itself is represented by the following type

ECPoint ::= OCTET STRING

whose value is the octet string obtained from the conversion routines given in Section 2.3.3.

2.3.3(Elliptic-Curve-Point-to-Octet-String转换)字多,但支持最好的格式是不使用点压缩(而且P !=无穷远点)

  1. If P = (xP , yP ) != O and point compression is not being used, proceed as follows:

3.1. Convert the field element xP to an octet string X of length (log2 q)/8 octets using the conversion routine specified in Section 2.3.5.

3.2. Convert the field element yP to an octet string Y of length (log2 q)/8 octets using the conversion routine specified in Section 2.3.5.

3.3. Output M = 0416 || X || Y .

2.3.5 是关于“长度足以容纳字段中所有值的大端字节顺序”(又名“保留前导零”)的一大堆词。

所以现在我们聚会。

鉴于 secp256r1 (d=70A12C2DB16845ED56FF68CFC21A472B3F04D7D6851BF6349F2D7D5B3452B38A) 上的 FIPS 186-3 参考密钥,

Q是 (8101ECE47464A6EAD70CF69A6E2BD3D88691A3262D22CBA4F7635EAFF26680A8, D8A12BA61D599235F67D9CB4D58F1783D3CA43E78F0A5ABAA624079936C0C3A9)

public 键 DER 看起来像

// SubjectPublicKeyInfo
30 XA
   // AlgorithmIdentifier
   30 XB
      // AlgorithmIdentifier.id (id-ecPublicKey (1.2.840.10045.2.1))
      06 07 2A 86 48 CE 3D 02 01
      // AlgorithmIdentifier.parameters, using the named curve id (1.2.840.10045.3.1.7)
      06 08 2A 86 48 CE 3D 03 01 07
   // SubjectPublicKeyInfo.subjectPublicKey
   03 XC 00
      // Uncompressed public key
      04
      // Q.X
      81 01 EC E4 74 64 A6 EA D7 0C F6 9A 6E 2B D3 D8
      86 91 A3 26 2D 22 CB A4 F7 63 5E AF F2 66 80 A8
      // Q.Y
      D8 A1 2B A6 1D 59 92 35 F6 7D 9C B4 D5 8F 17 83
      D3 CA 43 E7 8F 0A 5A BA A6 24 07 99 36 C0 C3 A9

计算 XA、XB 和 XC 的所有字节数:

XC = 32 (Q.X) + 32 (Q.Y) + 1 (0x04) + 1(未使用位为 0x00)= 66 = 0x42

XB = 19 = 0x13

XA 则为 66 + 19 + 2(标记字节)+ 2(长度字节)= 89 = 0x59

(当然,如果我们的任何长度值超过 0x7F,我们将不得不对它们进行正确编码)

所以现在我们还剩下

30 59 30 13 06 07 2A 86 48 CE 3D 02 01 06 08 2A
86 48 CE 3D 03 01 07 03 42 00 04 81 01 EC E4 74
64 A6 EA D7 0C F6 9A 6E 2B D3 D8 86 91 A3 26 2D
22 CB A4 F7 63 5E AF F2 66 80 A8 D8 A1 2B A6 1D
59 92 35 F6 7D 9C B4 D5 8F 17 83 D3 CA 43 E7 8F
0A 5A BA A6 24 07 99 36 C0 C3 A9

并且,我们验证:

$ xxd -r -p | openssl ec -text -noout -inform der -pubin
read EC key
<paste, then hit CTRL+D>
30 59 30 13 06 07 2A 86 48 CE 3D 02 01 06 08 2A
86 48 CE 3D 03 01 07 03 42 00 04 81 01 EC E4 74
64 A6 EA D7 0C F6 9A 6E 2B D3 D8 86 91 A3 26 2D
22 CB A4 F7 63 5E AF F2 66 80 A8 D8 A1 2B A6 1D
59 92 35 F6 7D 9C B4 D5 8F 17 83 D3 CA 43 E7 8F
0A 5A BA A6 24 07 99 36 C0 C3 A9
Private-Key: (256 bit)
pub:
    04:81:01:ec:e4:74:64:a6:ea:d7:0c:f6:9a:6e:2b:
    d3:d8:86:91:a3:26:2d:22:cb:a4:f7:63:5e:af:f2:
    66:80:a8:d8:a1:2b:a6:1d:59:92:35:f6:7d:9c:b4:
    d5:8f:17:83:d3:ca:43:e7:8f:0a:5a:ba:a6:24:07:
    99:36:c0:c3:a9
ASN1 OID: prime256v1
NIST CURVE: P-256

Printing it as "Private-Key: (256-bit)" 只是工具的一个bug/quirk,那里没有私钥。

指定参数曲线的事情更难,但它们不能很好地互操作(https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1 说符合规范的 CA 不得使用指定的参数形式或隐式形式,但必须使用命名形式).