【问题标题】:Get compressed X9.62 encoded EC public key in Java在 Java 中获取压缩的 X9.62 编码的 EC 公钥
【发布时间】:2015-10-04 08:14:43
【问题描述】:

我有一个使用此代码生成的公私密钥对:

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
keyPairGenerator.initialize(256, secureRandom);
KeyPair pair = keyPairGenerator.generateKeyPair();

我想得到一个表示公钥的字节数组,使用 X9.62 点压缩编码:

ANSI X9.62 为压缩、未压缩或混合形式的椭圆点定义强制八位字节字符串表示

我对点压缩的基本理解是,它使另一端能够从公钥表示的点计算出(使用曲线) Y 坐标,只给定 X 坐标和 LSB 的 LSB起始字节(0x02 或 0x03)中的 Y 坐标。我可以得到两个BigInteger X 和 Y 值如下:

ECPoint point = ((ECPublicKey) pk.getPublic()).getW();
BigInteger y = point.getAffineY();
BigInteger x = point.getAffineX();

如何使用 Java 中的压缩形式将公钥信息编码为字节数组?

【问题讨论】:

    标签: java


    【解决方案1】:

    在 SEC1 http://www.secg.org 中更方便地指定 X9.62 压缩和未压缩(但不是混合)形式 https://security.stackexchange.com/questions/63876/can-any-ecc-field-element-be-safely-serialized-as-compressed-and-then-restored 有一个(非Java)示例。

    如果您希望几乎任何其他软件(包括标准 Java 加密)都能读取此编码的公钥, 您不仅需要 X9.62/SEC1 编码,还需要包含在完整 X.509 证书中的编码, 或者至少是 X.509 定义的SubjectPublicKeyInfo (SPKI) 结构。 SPKI 包装算法相关的密钥数据 (对于 ECC X9.62,对于其他算法其他东西)在带有 AlgorithmIdentifier 的 ASN.1 序列中 识别算法(足够简单)加上适用的参数,对于 ECC 是曲线, 几乎总是以“命名”形式使用(标准化)ASN.1 OID 来识别曲线。 任何 Java PublicKey.getEncoded() 包括 ECPublicKey 返回, 和所有KeyFactory.generatePublic() 接受,一个编码Java 名称“X.509”实际上是SPKI。

    OpenSSL 可以使用 SPKI(它在 API 上调用 PUBKEY,在 PEM 分隔符中调用 PUBLIC KEY)或证书。 许多其他软件只接受证书,或将证书与私钥结合的 PKCS#12 aka PFX。 SSH,包括我相信 OpenSSH 我还没有足够新的版本来测试,只接受带有 XDRish 字符串的 SEC1/X9.62 来识别算法和曲线。

    来自 EC 生成器的 KeyPair 初始化为整数大小,就像您实际上使用命名曲线一样, 但是EC{Public,Private}Key.getParams() 返回的ECParameterSpec 的API 无法获取其OID。 您可以获取基础字段、系数、基点等并以显式形式进行编码,但是(1)浪费了大量空间,比 您通过压缩公钥点来保存,(2)其他软件可能不接受/支持它。 你应该

    • 使用ECPublicKey.getEncoded() 获取包含AlgId 的SPKI,从算法相关部分解析点(这很简单), 重新编码点,重建结构;或

    • 通过.initializeECGenParameterSpec(name) 在指定曲线上生成一个键 去另一个方向 然后用生成的公共点对已知曲线(和固定算法)进行编码。

    无论哪种方式,请参阅https://www.rfc-editor.org/rfc/rfc5480,如果您喜欢早期的https://www.rfc-editor.org/rfc/rfc3279.html,了解 ECC 的 SPKI。

    【讨论】:

    • 我正在使用的服务器仅适用于 NID_X9_62_prime256v1 曲线 - 因此在这种特殊情况下,没有必要明确地包含曲线名称/参数以及实际坐标。互操作性对我来说并不重要,因为这种身份验证方案只有一个服务器端实现。如果您查看github.com/kaniini/ecdsatool/blob/master/libecdsaauth/…,您会看到在分配键时设置了曲线类型,以及明确的 X9.62 形式的“压缩”。