作者热门文章
- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
java.security.PublicKey#getEncoded()
返回 key 的 X509 表示,与原始 ECC 值相比,在 ECC 的情况下增加了大量开销。
我希望能够以最紧凑的表示形式(即尽可能小的字节 block )将 PublicKey 转换为字节数组(反之亦然)。
key 类型(ECC)和具体的曲线类型是预先知道的,因此不需要对它们的信息进行编码。
解决方案可以使用 Java API、BouncyCaSTLe 或任何其他自定义代码/库(只要许可证不暗示需要开源将使用它的专有代码)。
最佳答案
此功能也存在于 Bouncy CaSTLe 中,但我将展示如何仅使用 Java 完成此功能,以防有人需要它:
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Arrays;
public class Curvy {
private static final byte UNCOMPRESSED_POINT_INDICATOR = 0x04;
public static ECPublicKey fromUncompressedPoint(
final byte[] uncompressedPoint, final ECParameterSpec params)
throws Exception {
int offset = 0;
if (uncompressedPoint[offset++] != UNCOMPRESSED_POINT_INDICATOR) {
throw new IllegalArgumentException(
"Invalid uncompressedPoint encoding, no uncompressed point indicator");
}
int keySizeBytes = (params.getOrder().bitLength() + Byte.SIZE - 1)
/ Byte.SIZE;
if (uncompressedPoint.length != 1 + 2 * keySizeBytes) {
throw new IllegalArgumentException(
"Invalid uncompressedPoint encoding, not the correct size");
}
final BigInteger x = new BigInteger(1, Arrays.copyOfRange(
uncompressedPoint, offset, offset + keySizeBytes));
offset += keySizeBytes;
final BigInteger y = new BigInteger(1, Arrays.copyOfRange(
uncompressedPoint, offset, offset + keySizeBytes));
final ECPoint w = new ECPoint(x, y);
final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(w, params);
final KeyFactory keyFactory = KeyFactory.getInstance("EC");
return (ECPublicKey) keyFactory.generatePublic(ecPublicKeySpec);
}
public static byte[] toUncompressedPoint(final ECPublicKey publicKey) {
int keySizeBytes = (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1)
/ Byte.SIZE;
final byte[] uncompressedPoint = new byte[1 + 2 * keySizeBytes];
int offset = 0;
uncompressedPoint[offset++] = 0x04;
final byte[] x = publicKey.getW().getAffineX().toByteArray();
if (x.length <= keySizeBytes) {
System.arraycopy(x, 0, uncompressedPoint, offset + keySizeBytes
- x.length, x.length);
} else if (x.length == keySizeBytes + 1 && x[0] == 0) {
System.arraycopy(x, 1, uncompressedPoint, offset, keySizeBytes);
} else {
throw new IllegalStateException("x value is too large");
}
offset += keySizeBytes;
final byte[] y = publicKey.getW().getAffineY().toByteArray();
if (y.length <= keySizeBytes) {
System.arraycopy(y, 0, uncompressedPoint, offset + keySizeBytes
- y.length, y.length);
} else if (y.length == keySizeBytes + 1 && y[0] == 0) {
System.arraycopy(y, 1, uncompressedPoint, offset, keySizeBytes);
} else {
throw new IllegalStateException("y value is too large");
}
return uncompressedPoint;
}
public static void main(final String[] args) throws Exception {
// just for testing
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(163);
for (int i = 0; i < 1_000; i++) {
final KeyPair ecKeyPair = kpg.generateKeyPair();
final ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic();
final ECPublicKey retrievedEcPublicKey = fromUncompressedPoint(
toUncompressedPoint(ecPublicKey), ecPublicKey.getParams());
if (!Arrays.equals(retrievedEcPublicKey.getEncoded(),
ecPublicKey.getEncoded())) {
throw new IllegalArgumentException("Whoops");
}
}
}
}
关于ECC 公钥的 Java 紧凑表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28172710/
我是一名优秀的程序员,十分优秀!