我有一个原始 (r,s) 格式的 ECDSA NIST P-256 公钥。似乎没有简单的方法可以将其加载到实现 java.security.interfaces.ECPublicKey 的对象中。
加载 64 字节公钥以用于检查签名的最简洁方法是什么?
最佳答案
EC 功能需要 Java 7,Base 64 编码器/解码器需要 Java 8,无需额外的库 - 只需简单的 Java。请注意,这实际上会在打印时将公钥显示为命名曲线,这是大多数其他解决方案无法做到的。 如果您有最新的运行时,this other answer更干净。
如果我们使用 ECPublicKeySpec
来做这个,这个答案会很困难。所以让我们作弊并使用 X509EncodedKeySpec
代替:
private static byte[] P256_HEAD = Base64.getDecoder().decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");
/**
* Converts an uncompressed secp256r1 / P-256 public point to the EC public key it is representing.
* @param w a 64 byte uncompressed EC point consisting of just a 256-bit X and Y
* @return an <code>ECPublicKey</code> that the point represents
*/
public static ECPublicKey generateP256PublicKeyFromFlatW(byte[] w) throws InvalidKeySpecException {
byte[] encodedKey = new byte[P256_HEAD.length + w.length];
System.arraycopy(P256_HEAD, 0, encodedKey, 0, P256_HEAD.length);
System.arraycopy(w, 0, encodedKey, P256_HEAD.length, w.length);
KeyFactory eckf;
try {
eckf = KeyFactory.getInstance("EC");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("EC key factory not present in runtime");
}
X509EncodedKeySpec ecpks = new X509EncodedKeySpec(encodedKey);
return (ECPublicKey) eckf.generatePublic(ecpks);
}
用法:
ECPublicKey key = generateP256PublicKeyFromFlatW(w);
System.out.println(key);
这背后的想法是创建一个临时的 X509 编码 key ,它以公共(public)点 w
结束。之前的字节包含命名曲线的 OID 和结构开销的 ASN.1 DER 编码,以字节 04
结尾,指示未压缩点。 Here is an example结构是什么样的,对 32 字节的 X 和 Y 使用值 1 和 2。
为创建 header 而删除的未压缩点值的 32 字节 X 和 Y 值。这只有效,因为该点是静态大小的 - 它在末端的位置仅由曲线的大小决定。
现在函数 generateP256PublicKeyFromFlatW
所需要做的就是将接收到的公共(public)点 w
添加到 header 中,并通过为 X509EncodedKeySpec< 实现的解码器运行它
.
上面的代码使用原始的、未压缩的公共(public) EC 点——只有 32 字节的 X 和 Y——没有值为 04
的未压缩点指示符。当然支持65字节的压缩点也很容易:
/**
* Converts an uncompressed secp256r1 / P-256 public point to the EC public key it is representing.
* @param w a 64 byte uncompressed EC point starting with <code>04</code>
* @return an <code>ECPublicKey</code> that the point represents
*/
public static ECPublicKey generateP256PublicKeyFromUncompressedW(byte[] w) throws InvalidKeySpecException {
if (w[0] != 0x04) {
throw new InvalidKeySpecException("w is not an uncompressed key");
}
return generateP256PublicKeyFromFlatW(Arrays.copyOfRange(w, 1, w.length));
}
最后,我使用 base 64 生成常量 P256_HEAD
head 值:
private static byte[] createHeadForNamedCurve(String name, int size)
throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException, IOException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec m = new ECGenParameterSpec(name);
kpg.initialize(m);
KeyPair kp = kpg.generateKeyPair();
byte[] encoded = kp.getPublic().getEncoded();
return Arrays.copyOf(encoded, encoded.length - 2 * (size / Byte.SIZE));
}
调用者:
String name = "NIST P-256";
int size = 256;
byte[] head = createHeadForNamedCurve(name, size);
System.out.println(Base64.getEncoder().encodeToString(head));
关于java - 在 Java 中加载原始的 64 字节长 ECDSA 公钥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30445997/