Загрузка raw 64-байтового открытого ключа ECDSA в Java

У меня есть открытый ключ raw (r,s) формата ECDSA NIST P-256. Кажется, что нет простого способа загрузить его в объект, который реализует java.безопасность.межфазные границы.Экпублики.

каков самый чистый способ загрузки 64-байтового открытого ключа, чтобы его можно было использовать для проверки подписей?

4 ответов


этот ответ будет жестким, если мы делаем это с помощью ECPublicKeySpec. Так что давайте немного обманем:

private static byte[] P256_HEAD = Base64.getDecoder().decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");

public static ECPublicKey convertP256Key(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 = convertP256Key(w);
System.out.println(key);

я сгенерировал голову, используя:

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));

идея заключается в том, чтобы создать кодированный ключ X509, который счастливо заканчивается публичной точкой w в конце (байты перед этим содержат ASN.1 DER кодирование OID именованной кривой и структурных накладных расходов, заканчивающихся байтом 04 указание несжатой точки). Затем мы заменяем "случайную" точку w в конце с w и мы расшифровываем его снова.

Java 7 требуется для функциональности EC и Java 8 для базового 64 кодера / декодера, никаких дополнительных библиотек и прочего. Обратите внимание, что это фактически отобразит открытый ключ как названы кривой при распечатке то, что другие решения не будут делать.


Java делает криптографию очень длинной.

процедура создания открытого ключа из заданной точки ЕС:

  1. построить ECPoint объект ваших координат.
  2. построить ECParameterSpec объект из информации вашей кривой.
  3. построить ECPublicKeySpec объект из своего ECPoint и свой ECParameterSpec "объект".
  4. Invoke KeyFactory.generatePublic() С ECPublicKeySpec объект для извлечения PublicKey объект.
  5. бросил PublicKey на ECPublicKey по мере необходимости.

пример:

// Setup for P-256 curve params

BigInteger p256_p = new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16);

BigInteger p256_a = new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16);
BigInteger p256_b = new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16);
byte[] p256_seed = {
                        (byte) 0xc4, (byte) 0x9d, (byte) 0x36, (byte) 0x08, 
                        (byte) 0x86, (byte) 0xe7, (byte) 0x04, (byte) 0x93, 
                        (byte) 0x6a, (byte) 0x66, (byte) 0x78, (byte) 0xe1, 
                        (byte) 0x13, (byte) 0x9d, (byte) 0x26, (byte) 0xb7, 
                        (byte) 0x81, (byte) 0x9f, (byte) 0x7e, (byte) 0x90
                    };

BigInteger p256_xg = new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16);
BigInteger p256_yg = new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16);

BigInteger p256_n = new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16);

// Construct prime field
ECFieldFp p256_field = new ECFieldFp(p256_p);

// Construct curve from parameters
EllipticCurve p256 = new EllipticCurve(p256_field, p256_a, p256_b, p256_seed);

// Construct base point for curve
ECPoint p256_base = new ECPoint(p256_xg, p256_yg);

// Construct curve parameter specifications object
ECParameterSpec p256spec = new ECParameterSpec(p256, p256_base, p256_n, 1); // Co-factor 1 for prime curves

// ------------------------------------------------------------- //

// Construct EC point from "raw" public key
ECPoint point = new ECPoint(r, s); // r, s is of type BigInteger

// Create a EC public key specification object from point and curve
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, p256spec);

// Retrieve EC KeyFactory
KeyFactory ECFactory = KeyFactory.getInstance("EC");

// Generate public key via KeyFactory
PublicKey pubKey = ECFactory.generatePublic(pubKeySpec);
ECPublicKey ECPubKey = (ECPublicKey) pubKey;

может быть полезно создать ECParameterSpec один раз (возможно, в статическом блоке инициализатора) по соображениям производительности.

Примечание: существует, вероятно, гораздо более простой способ создания объекта ECParameterSpec (например, через именованные кривые), но до сих пор я нашел только это ECGenParameterSpec имеет эту функцию. Дайте мне знать в комментариях, если есть менее болезненный подход.


чтобы избавить себя от боли делать выше, Закодируйте свой ключ EC под X. 509, который полностью опишет ключ и сделать загрузку намного проще.

в java, с ECPublicKey, все, что вам нужно сделать, это позвонить ECPublicKey.getEncoded() и передайте / сохраните массив байтов туда, где вам нужен ключ. Затем кодированный ключ X. 509 можно восстановить с помощью:

// Retrieve EC KeyFactory
KeyFactory ECFactory = KeyFactory.getInstance("EC");

// Generate public key via KeyFactory
PublicKey pubKey = ECFactory.generatePublic(new X509EncodedKeySpec(data));
ECPublicKey ECPubKey = (ECPublicKey) pubKey;

где "данные" - это закодированный байтовый матрица.


открытый ключ EC-это точка, состоящая из координат x и Y. Я написал следующий сегмент кода один раз, чтобы преобразовать EC x, Y в publicKey "объект". Надеюсь, это поможет вам. Для вашего сведения:

rawPubKey = 04 + X координата + Y координата (шестнадцатеричная строка)

curveName = P-256 (строка)

пример точки открытого ключа EC для P-256:

rawPubKey = 04 6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296 4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5

БК поставщик: вам нужен поставщик надувной замок. Я использовал bcprov-jdk15on-149.Джар, но вы можете скачать последнюю версию здесь.

/**
 * This method converts the uncompressed raw EC public key into java.security.interfaces.ECPublicKey
 * @param rawPubKey 
 * @param curveName
 * @return java.security.interfaces.ECPublicKey
 */
public ECPublicKey ucPublicKeyToPublicKey(String rawPubKey, String curveName) {
    byte[] rawPublicKey = Helper.toByte(rawPubKey); 
    ECPublicKey ecPublicKey = null;
    KeyFactory kf = null;

    ECNamedCurveParameterSpec ecNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec(curveName);
    ECCurve curve = ecNamedCurveParameterSpec.getCurve();
    EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, ecNamedCurveParameterSpec.getSeed());
    java.security.spec.ECPoint ecPoint = ECPointUtil.decodePoint(ellipticCurve, rawPublicKey);
    ECParameterSpec ecParameterSpec = EC5Util.convertSpec(ellipticCurve, ecNamedCurveParameterSpec);
    java.security.spec.ECPublicKeySpec publicKeySpec = new java.security.spec.ECPublicKeySpec(ecPoint, ecParameterSpec);

    kf = java.security.KeyFactory.getInstance("EC");

    try {
        ecPublicKey = (ECPublicKey) kf.generatePublic(publicKeySpec);
    } catch (Exception e) {
        System.out.println("Caught Exception public key: " + e.toString());
    }

    return ecPublicKey;
}

EDIT: Вот toByte() способ:

public static byte[] toByte(String hex) {
        if (hex == null)
            return null;
        hex = hex.replaceAll("\s", "");
        byte[] buffer = null;
        if (hex.length() % 2 != 0) {
            hex = "0" + hex;
        }
        int len = hex.length() / 2;
        buffer = new byte[len];
        for (int i = 0; i < len; i++) {
            buffer[i] = (byte) Integer.parseInt(
                    hex.substring(i * 2, i * 2 + 2), 16);
        }
        return buffer;
    }

но вы можете использовать свою собственную реализацию. Вот еще один:

import javax.xml.bind.DatatypeConverter;
public static byte[] toByte(String hex) {{
    return DatatypeConverter.parseHexBinary(hex);
}

это сработало для меня с помощью Bouncycastle:

ECParameterSpec ecParameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
ECNamedCurveSpec params = new ECNamedCurveSpec("secp256r1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint publicPoint =  ECPointUtil.decodePoint(params.getCurve(), publicKeyByteArray);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(publicPoint, params);
PublicKey publicKey =  keyFactory.generatePublic(pubKeySpec);