ECC PublicKey的Java紧凑表示

Dai*_*mon 4 java encoding cryptography elliptic-curve public-key

java.security.PublicKey#getEncoded() 返回键的X509表示,在ECC的情况下,与原始ECC值相比增加了很多开销.

我希望能够在大多数紧凑的表示中将PublicKey转换为字节数组(反之亦然)(即尽可能小的字节块).

KeyType(ECC)和具体曲线类型是预先已知的,因此不需要对它们的信息进行编码.

解决方案可以使用Java API,BouncyCastle或任何其他自定义代码/库(只要许可证并不意味着需要使用它的开源专有代码).

Maa*_*wes 8

这个功能也存在于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");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)