如何 import/export Java EC keys to/from X9.63 encoding with Java 8
How to import/export Java EC keys to/from X9.63 encoding with Java 8
我正在考虑编写一个 Java 8 程序来与另一个使用只能 export/import ECC [=59] 的库 (wolfSSL/wolfCrypt) 的软件进行 ECDH 交换=] X9.63 格式的键(它们的 wc_ecc_export_x963()
函数)。我非常希望使用 Java.
附带的提供程序来执行此操作
因此,我需要了解如何获取 Java 以从一个的 X9.63 编码创建一个 PublicKey
对象并创建一个 [= 的 X9.63 编码字节22=]对象。
我已经写了一些测试代码来尝试找出 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 来调查 public 密钥结构:
$ 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 -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_ecc_export_x963()
中得到的 hexdump。我还将把 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 编程了):
#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
您似乎从 wc_ecc_export_x963
函数中得到的只是未压缩的点表示。
256 位曲线上的点将由具有该长度的两个坐标组成。如果将它们表示为固定长度的大端无符号整数,那么它们每个将占用 ceil(256 / 8) = 32
个字节,总共 64 个字节。这些以一个字节的未压缩点指示符 04
为前缀,为您提供 65 个字节。正如您在 Java 中所见,BIT STRING 的长度为 66 字节。但是,BIT STRING 包含一个填充指示符,通常设置为 00
,表示位数是 8 的倍数。换句话说,ASN.1 定义中的 BIT STRING 只是您从 wc_ecc_export_x963
.
所以你可以做的是把所有的东西都放在 04
之前的位置 encoded.length - 133
之前,然后使用 [=] 的 X.509 解码工具 (KeyFactory
) 25=]。然而,我会考虑黑客攻击;它与任何其他密钥大小不兼容。但是,您也可以使用我很久以前提供的 this answer。
我正在考虑编写一个 Java 8 程序来与另一个使用只能 export/import ECC [=59] 的库 (wolfSSL/wolfCrypt) 的软件进行 ECDH 交换=] X9.63 格式的键(它们的 wc_ecc_export_x963()
函数)。我非常希望使用 Java.
因此,我需要了解如何获取 Java 以从一个的 X9.63 编码创建一个 PublicKey
对象并创建一个 [= 的 X9.63 编码字节22=]对象。
我已经写了一些测试代码来尝试找出 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 来调查 public 密钥结构:
$ 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 -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_ecc_export_x963()
中得到的 hexdump。我还将把 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 编程了):
#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
您似乎从 wc_ecc_export_x963
函数中得到的只是未压缩的点表示。
256 位曲线上的点将由具有该长度的两个坐标组成。如果将它们表示为固定长度的大端无符号整数,那么它们每个将占用 ceil(256 / 8) = 32
个字节,总共 64 个字节。这些以一个字节的未压缩点指示符 04
为前缀,为您提供 65 个字节。正如您在 Java 中所见,BIT STRING 的长度为 66 字节。但是,BIT STRING 包含一个填充指示符,通常设置为 00
,表示位数是 8 的倍数。换句话说,ASN.1 定义中的 BIT STRING 只是您从 wc_ecc_export_x963
.
所以你可以做的是把所有的东西都放在 04
之前的位置 encoded.length - 133
之前,然后使用 [=] 的 X.509 解码工具 (KeyFactory
) 25=]。然而,我会考虑黑客攻击;它与任何其他密钥大小不兼容。但是,您也可以使用我很久以前提供的 this answer。