如何将Java EC密钥导入或导出到使用Java 8编码的X9.63中

如何将Java EC密钥导入或导出到使用Java 8编码的X9.63中,java,cryptography,asn.1,diffie-hellman,wolfssl,Java,Cryptography,Asn.1,Diffie Hellman,Wolfssl,我正在考虑编写一个Java 8程序,使用一个库(wolfSSL/wolfCrypt)与另一个软件进行ECDH交换,该库只能导出/导入X9.63格式的ECC公钥(它们的wc\u ECC\u export\u x963()函数)。我更愿意使用Java附带的提供者来实现这一点 因此,我需要了解如何让Java从一个对象的X9.63编码中创建一个PublicKey对象,并创建一个PublicKey对象的X9.63编码字节 我编写了一些测试代码,试图找出Java使用的格式: // Make a ke

我正在考虑编写一个Java 8程序,使用一个库(wolfSSL/wolfCrypt)与另一个软件进行ECDH交换,该库只能导出/导入X9.63格式的ECC公钥(它们的
wc\u ECC\u export\u x963()
函数)。我更愿意使用Java附带的提供者来实现这一点

因此,我需要了解如何让Java从一个对象的X9.63编码中创建一个
PublicKey
对象,并创建一个
PublicKey
对象的X9.63编码字节

我编写了一些测试代码,试图找出Java使用的格式:

    // Make a key
    ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
    keyGen.initialize(ecSpec);
    KeyPair pair = keyGen.generateKeyPair();

    // Let's see what Java thinks the encoding is
    System.out.println("Pubkey format: " + pair.getPublic().getFormat());
    System.out.println("Privkey format: " + pair.getPrivate().getFormat());

    // And write out the encoded forms to files so we can poke at them
    // with openssl, etc.
    try (FileOutputStream pubOut = new FileOutputStream("ecpub.der");
         FileOutputStream privOut = new FileOutputStream("ecpriv.der")) {
        pubOut.write(pair.getPublic().getEncoded());
        privOut.write(pair.getPrivate().getEncoded());
    }
这将产生以下输出:

Pubkey format: X.509
Privkey format: PKCS#8
然后,我可以使用openssl调查公钥结构:

$ openssl asn1parse -i -in ecpub.der -inform DER
    0:d=0  hl=2 l=  89 cons: SEQUENCE
    2:d=1  hl=2 l=  19 cons:  SEQUENCE
    4:d=2  hl=2 l=   7 prim:   OBJECT            :id-ecPublicKey
   13:d=2  hl=2 l=   8 prim:   OBJECT            :prime256v1
   23:d=1  hl=2 l=  66 prim:  BIT STRING
但这对我来说是不透明的(我不知道ASN.1),我不知道那是什么编码格式,甚至不知道如何找出它是什么编码格式

下面是该Java表示的hexdump:

$ hexdump -C ecpub.der
00000000  30 59 30 13 06 07 2a 86  48 ce 3d 02 01 06 08 2a  |0Y0...*.H.=....*|
00000010  86 48 ce 3d 03 01 07 03  42 00 04 a3 c4 5c 5d aa  |.H.=....B....\].|
00000020  93 70 8b 65 47 9b f9 83  17 01 37 23 30 d2 0c 6a  |.p.eG.....7#0..j|
00000030  c7 93 6e d4 70 b1 5b bf  8e 65 4f 96 70 7e e8 97  |..n.p.[..eO.p~..|
00000040  30 a2 6e e4 1f 50 bb 21  4f a6 7a 01 bd 96 a4 2f  |0.n..P.!O.z..../|
00000050  8b cd 0d d0 d2 4a 63 d1  68 d0 7b                 |.....Jc.h.{|
0000005b

更新

以下是我从
wc\u ecc\u export\u x963()
中得到的信息。我还将在测试程序中包含C源代码<代码>openssl asn1parse阻塞文件FWIW

$ hexdump -C wolf.x963
00000000  04 f1 55 1b 03 d5 91 ed  03 d5 44 f9 09 b2 1e 59  |..U.......D....Y|
00000010  c7 4d ef 1a e9 de 51 16  4e b9 4d 8c 1d 10 73 d4  |.M....Q.N.M...s.|
00000020  9e 09 24 78 5a 03 c4 45  bf 0c 83 22 69 d8 52 ed  |..$xZ..E..."i.R.|
00000030  90 04 00 0c ea 38 95 a9  e5 da 96 d2 ae c4 5c 3a  |.....8........\:|
00000040  c8                                                |.|
00000041
作为参考,这里是生成
wolf.x963
文件的程序(注意——我已经很多年没有用C编程了):


您似乎从
wc\u ecc\u export\u x963
函数中得到的只是未压缩的点表示

256位曲线上的点由两个具有该长度的坐标组成。如果它们表示为固定长度的big-endian无符号整数,那么它们将采用
ceil(256/8)=32个
字节,总共64个字节。它们的前缀是一个字节的未压缩点指示器
04
,即65个字节。正如您在Java中看到的,位字符串的长度是66字节。但是,位字符串包含一个填充指示符,通常设置为
00
,表示位数是8的倍数。换句话说,ASN.1定义中的位字符串就是从
wc\u ecc\u export\u x963
获得的未压缩点


因此,您可以将所有内容放在您的点之前的
04
位置
encoded.length-133
,然后使用Java的X.509解码工具(
KeyFactory
)。然而,我会考虑黑客攻击;它将与任何其他密钥大小不兼容。但是,您也可以使用我很久以前提供的密钥。

您能否展示一个使用
wc\u ecc\u export\u x963导出的密钥示例,以便我可以查看一下?十六进制或基数64就好了。Java中的公钥通常导出为X.509证书中的
SubjectPublicKeyInfo
。如果您的密钥是其中一个的内部结构,我不会感到惊讶。@MaartenBodewes:它看起来像是对未压缩的椭圆曲线点的一种非常简单的编码:一个0x04字节(表示该点未压缩),后跟big endian中的x坐标和y坐标。请参阅的第2.3.3节,我已经包含了您要求的hextump。请告诉我
wc\u ecc\u export\u x963
的输出是否包含以
04
开头的133个字节。我已经添加了函数生成的数据的hextump。请注意,hextump来自secp256r1键,而不是Java示例中使用的secp521r1键?并不是说它改变了答案,这仍然是一个“原始”未压缩点……因为我首先编写了Java程序,然后发现将secp256r1与wolfSSL一起使用是最简单的。我已经将Java示例更新为也使用secp256r1,并更新了Java生成的DER的ASN.1解析和Java生成的DER的hexdump。因此,现在问题中的所有内容都使用了secp256r1,所有转储都与基础secp256r1曲线相关。好的,将答案与更新的问题对齐。谢谢你花这么多时间把它做好。
#include <stdio.h>
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/random.h>
#include <wolfssl/wolfcrypt/ecc.h>

int main() {
  // Make a key object ready for use.
  ecc_key key;
  wc_ecc_init(&key);

  // Make a random number generator object ready for use
  RNG rng;
  wc_InitRng(&rng);

  // Make a "32-byte" key.  According to wolfSSL,
  // this will use the SECP256R1 curve since that's
  // what they map to a request for a 32-byte key.
  wc_ecc_make_key(&rng, 32, &key);

  byte encoded[1024];
  word32 encodedLen = 0;
  int error;

  // According to the API docs, on entry encodedLen should
  // be a number equal to or larger than what the output
  // will be.  If it is not, the function will return BUFFER_E
  // and set encodedLen to how many bytes will be needed to
  // hold the exported data.
  error = wc_ecc_export_x963(&key, encoded, &encodedLen);
  printf("Error code = %d\n", error);
  if (error == BUFFER_E) {
    error = wc_ecc_export_x963(&key, encoded, &encodedLen);
    printf("Error code again = %d\n", error);
  }

  // Print out the byte values so that I can make sure
  // that I didn't somehow corrupt the data writing it
  // out as I am very rusty at C.
  //int i;
  //for (i = 0; i < encodedLen; i++) {
  //  printf("enc[%d] = %x\n", i, encoded[i]);
  //}

  FILE *outFile;
  outFile = fopen("wolf.x963", "wb");
  fwrite(encoded, encodedLen, 1, outFile);
  fclose(outFile);

  wc_ecc_free(&key);
  wc_FreeRng(&rng);

  return 0;
}
 ./SaveEccKey
Error code = -132
Error code again = 0