当前位置: 首页 > 面试题库 >

从原生Java中的字节数组私钥生成EC公钥(7+)

廉鸿运
2023-03-14
问题内容

我正在尝试学习一些加密编码,并生成了当前保存在字节数组中的32字节私有密钥( byte [] privatekey )。我知道公钥是使用
secp256k1 命名为椭圆曲线参数和一个公式生成的,其中publickey = G * privatekey,其中G是椭圆曲线上的某个点(ECPoint?),但是我无法将该命名参数的规范和公式转换为的实际编码公钥。我知道自Java
7起,java.security.*andjava.security.spec.*包中包含一些类,以简短的代码即可完成此操作,但是我找不到一个很好的示例来说明如何在不使用第三方库的情况下执行此操作。

这个比特币stackexchange链接具有所有理论上的答案以及出色的python和C#代码,但是在Java中则没有。

编辑/更新:我尝试通过以下代码获得所需的内容:

String secp256k1_G_uncompressed_string = "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8";
byte[] secp256k1_G_uncompressed_bytes = DatatypeConverter.parseHexBinary(secp256k1_G_uncompressed_string);
String privatekeystring = "1184CD2CDD640CA42CFC3A091C51D549B2F016D454B2774019C2B2D2E08529FD";
byte[] privatekeybytes = DatatypeConverter.parseHexBinary(privatekeystring);
BigInteger secp256k1_G_num = new BigInteger(1, secp256k1_G_uncompressed_bytes);
BigInteger privatekey_num = new BigInteger(1, privatekeybytes);
BigInteger curvepoint = secp256k1_G_num.multiply(privatekey_num);
byte[] publickeybytes = curvepoint.toByteArray();
System.out.println(DatatypeConverter.printHexBinary(privatekeybytes));
System.out.println(DatatypeConverter.printHexBinary(publickeybytes));

应该使用正确的编码生成的公钥是这样的:

04d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6
fbdd594388756a7beaf73b4822bc22d36e9bda7db82df2b8b623673eefc0b7495

但是正在生成的公钥是这样的:

4E6801418BB6EF9F462F69830F82EB51BB9224219B9D89C8C34FB746297F59779D8B986194181BD7AB99DC7E3086914EA13C4B37E05716CADCA0AE391CE81C4B85E0F09E8628F0F81692B5D08D0D8B9E65A


问题答案:

椭圆曲线点不是整数。将点(G)的编码表示形式放在a中,BigInteger并尝试将其用作整数并不十分正确。椭圆曲线点乘法不是整数乘法,并且没有这么简单BigInteger.multiply。它用标量写在左侧,例如kG而不是Gk。

对于任何Java程序员来说,将比特币Q上给出的标准(或至少是常规的)算法转换为Java确实是一个相当简单的练习。

椭圆曲线上点的标量乘法包含(在答案中)P192的正确实现,也称为secp192r1;可以通过将p和a替换为规范中的值(来自https://www.secg.org的
SEC2 或X9.62,如果有的话)或任何现有的实现-包括Java(请参见下文),将其转换为secp256k1
-并丢弃P192专用的测试数据。实际上,您最需要更改p;Koblitz曲线选择为a =
0。椭圆曲线乘法函数包含一个不完全正确的实现,据说该实现是针对secp256k1的,但实际上并未包含任何曲线的常数。

从Java 7开始,在java.security和java.security.spec。包中包含一些类,以简短的代码即可完成此操作

并不是的。首先,Java的加密隔离你看到的类java.security,并javax.crypto从实现代码,这是完全不同的类(主要是(仍然)下sun.*com.sun.*)放在一个或多个“提供者”中,这些“提供者”是单独的罐子,在技术上是可选的;尽管大多数人不会,但是可以在不更改代码调用的情况下删除,添加或更改提供程序。自从Java
5(称为1.5)以来,就出现了EC加密的JCA’facade’类,但是标准版本中没有包含实现EC算法的提供程序;要使用它们,您必须添加第三方提供程序。从Java
7开始,包括一个标准的SunEC提供程序。但是,JCA(不仅对于EC,对于所有算法)在生成后将私钥和公钥严格分开,并且特别地,它没有提供任何方法来访问EC内部存在的私对公派生逻辑

它的确包含一些标准曲线的参数,包括secp256k1,您可以使用这些参数来避免从规格中复制它们的工作。似乎没有直接访问此数据的方法,但是您可以通过生成随机数密钥并将其丢弃来间接地进行访问。另外,由于您已经有一个私钥,因此可以创建Java使用的编码(PKCS8)并将其读入,从而产生相同的曲线参数和一个可用的密钥。通常,构造ASN.1
DER编码(如PKCS8)相当复杂,但 对于EC
之所以简化,是因为(1)每个人都使用“命名”形式将曲线编码为单个OID,并且(2)标准指定了给定曲线的长度固定的私有值的编码;结果,给定EC曲线的PKCS8编码由固定前缀和私钥值组成。示例片段:

    KeyPairGenerator kg = KeyPairGenerator.getInstance ("EC");
    kg.initialize (new ECGenParameterSpec ("secp256k1"));
    ECParameterSpec p = ((ECPublicKey) kg.generateKeyPair().getPublic()).getParams();
    System.out.println ("p=(dec)" + ((ECFieldFp) p.getCurve().getField()).getP() );
    ECPoint G = p.getGenerator(); 
    System.out.format ("Gx=(hex)%032x%n", G.getAffineX());
    System.out.format ("Gy=(hex)%032x%n", G.getAffineY());
    //
    byte[] privatekey_enc = DatatypeConverter.parseHexBinary(
            "303E020100301006072A8648CE3D020106052B8104000A042730250201010420"+
            "1184CD2CDD640CA42CFC3A091C51D549B2F016D454B2774019C2B2D2E08529FD");
    // note fixed prefix for PKCS8-EC-secp256k1 plus your private value
    KeyFactory kf = KeyFactory.getInstance("EC");
    PrivateKey k1 = kf.generatePrivate(new PKCS8EncodedKeySpec(privatekey_enc));
    ECParameterSpec p2 = ((ECPrivateKey) k1).getParams();
    System.out.println ("again p=(dec)" + ((ECFieldFp) p2.getCurve().getField()).getP() );

产生输出:

p=(dec)115792089237316195423570985008687907853269984665640564039457584007908834671663
Gx=(hex)79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
Gy=(hex)483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
again p=(dec)115792089237316195423570985008687907853269984665640564039457584007908834671663

请注意,基点G的那些坐标符合您的期望。我显示了十进制和十六进制的混合形式,只是为了说明可能性。这不会影响计算机中的实际数字。

根据评论添加:

变量p和p2是ECParameterSpec包含EC曲线参数的对象(基础字段,曲线系数,基点aka生成器,阶数和辅因子;以及内部的“名称”,尽管API并未公开)。我打印的标记为“
p”的值是调用的结果,该结果从曲线参数getP返回 一项
,即基础质数字段的模数,因此需要在链接的文章中显示的计算中使用该值mod(p)modInverse(p)modPow(,p)。由于此p(或P)是曲线的参数,因此该曲线上的所有关键点都是相同的。请注意,即使我打印的两个值来自不同的键,它们也是相同的。实际上,存在两种用于加密的标准化椭圆曲线:素数场上的曲线(表示为Fp)和特征二的扩展场上的曲线(表示为F2m)。secp256k1是第一种,这就是为什么ECFieldFp在调用之前强制转换为getP()

是的,我的固定前缀包含标有用于EC和secp256k1的私钥(PKCS8)编码的标头和字段,并且该前缀对于所有EC
secp256k1私钥都是相同的。p值如上所述,而不是私钥或公钥。是的,如果您拥有公共点,则可以将其与组合ECParameterSpec成一个ECPublicKeySpec并将其转换并使用-
或可以将点编码附加到一个相似但不同的固定前缀上,以获得X509EncodedKeySpecJava用来对公钥进行编码并进行转换的
而不需要ECParameterSpec事先-但你的整个问题,据我了解,是你没有公共点而想要得到它,这需要在链接的帖子所示的点乘计算。



 类似资料:
  • 我正在Erlang中创建一个应用程序,给定一个RSA私钥,它可以返回RSA公钥和与该priv密钥相关联的x509公钥。 ----只是一个更新---- 在花了一些时间了解x509并阅读了Erlang public_key文档后,我找到了一个解决方案: 如果我可以通过简单地获取模数和publicExponent,从“RSaprivateKey”生成“RSapublicKey”,那么生成这两个键的代码如

  • 我以这种格式将私钥保存在DB中。 我需要从中提取公钥。openssl\u pkey\u get\u详细信息需要资源,但如何从字符串创建资源? 我试过这个 错误是openssl\u pkey\u get\u details()期望参数1是资源,给定布尔值 编辑1: 我试图从@kbarborak bellow获得解决方案,但$pk\u get是错误的: var_转储的输出为:

  • 问题内容: 我试图从私钥生成公共ECDSA密钥,但是在Internet上如何进行此操作方面,我还没有找到太多帮助。几乎所有内容都是根据公共密钥规范生成公共密钥的,我不知道该如何获得。到目前为止,这是我汇总的内容: 但是,在运行时,出现以下错误: 我究竟做错了什么?有没有更好/更简便的方法可以做到这一点? 编辑:我设法获得一些代码进行编译,但不能正常工作: 当我运行它时,它会生成一个publicKe

  • 以下网站经常被引用,而且我认为是准确的: https://gobittest.appspot.com/Address 我试图在Golang中重现这些步骤,但第一步就失败了:-( 有人能给我提供一个Golang片段,给定ECDSA私钥,返回公钥吗?我想我可能具体指的是上述站点示例中的私钥指数和公钥指数。 i、 例如,给定随机生成的(十六进制编码的)私钥(指数?)<代码>E83385AF76B2B19

  • 问题内容: 我正在寻找一个Java库或代码来即时生成证书,公共和私有密钥,而无需使用第三方程序(例如openssl)。 我认为是从Java代码中获取keytool + openssl的东西。 考虑使用ssl和客户端身份验证保护的基于Java servlet的Web应用程序。我希望Servlet容器仅在请求时使用Java代码生成客户端证书(例如pkcs12格式)。 问题答案: 您可以使用一对或密钥在

  • 主要的问题是,我对C相当陌生,OpenSSL留档对我来说不够清晰,我尝试过使用读和写rsa键到C中的pem文件,但我不太明白。例如,函数如何创建私有和公共?而从何而来?pcszPassphrase的意义是什么? 我会解释,好像这是某种伪代码,这就是我想做的,粗体部分是我不知道如何做的: 生成私钥和公钥作为十六进制缓冲区(客户端) 基本上,我知道如何处理AES加密/解密和通信协议,反正他们已经实现了