2015-05-25 3 views
10

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

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

+0

Каков формат вашего EC Public Key? Сжатый или несжатый? –

+1

64 байта? Очевидно, не сжимается. Это было бы 33 байта, если бы оно было сжато. –

+0

Исправить. Он несжатый. – user1094206

ответ

7

Этот ответ будет жестким, если мы делаем это с помощью 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, который с радостью заканчивает wi го публичного пункта w в конце (байты до этого содержат кодировку DER ASN.1 DER для идентификационной кривой названной кривой и структурные издержки, заканчивающиеся байтом 04 с указанием несжатой точки). Затем мы заменяем «случайную» точку w в конце вашим w, и мы снова декодируем его.

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

+0

Спасибо, что это очень полезно! – user1094206

+0

Мне пришлось изменить 'createHeadForNamedCurve', чтобы вернуть один меньше байта для головы, т. Е.' (Encoded.length - 2 * (size/Byte.SIZE)) -1) 'чтобы заставить это работать, когда оно используется для декодирования возвращаемого ключа с удаленного сервера SSH в соглашении ключа ECDH. –

4

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

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

  1. Построить ECPoint объект из заданных координат.
  2. Построить объект ECParameterSpec с информацией о вашей кривой.
  3. Постройте объект ECPublicKeySpec от вашего ECPoint и вашего ECParameterSpec объекта.
  4. Вызов 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() и передать/сохранить массив байтов туда, где вам нужен следующий ключ. Х.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; 

, где «данные» является закодированной массив байт.

+0

Вид смешной, вы уже были на полпути, чтобы добраться до ярлыка, который я вложил в мой ответ :) –

+0

@MaartenBodewes Ну, это заняло больше времени, чем я ожидал, чтобы на самом деле получить все константы, используемые на кривой, захватив их с двух или трех различные документы. Я все еще удивлен, что java не имеет прямого способа получить объект ECParameterSpec непосредственно из именованных кривых, кажется очень полезной функцией, если она существует. – initramfs

+1

Ты определенно прав. Счастливый, чтобы поддержать запрос функции :) –

3

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

rawPubKey = 04 + х координатная + у координатная (Hex String)

curveName = P-256 (String)

Пример EC Public Key Точка для P-256:

rawPubKey = 04 6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296 4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5

BC Поставщик: Вам нужен поставщик Bouncy Castle. Я использовал bcprov-jdk15on-149.jar, но вы можете скачать последнюю версию с here.

/** 
* 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); 
} 
+0

Пожалуйста, по крайней мере, сообщите людям, какие библиотеки вы используете, и какие вспомогательные функции, такие как 'Helper.toByte', делают (и почему они нужны в первую очередь). –

+0

@MaartenBodewes Я отредактировал свой ответ. Ty. Я думал, что OP может использовать свой собственный путь (вспомогательную или встроенную библиотеку) для преобразования «шестнадцатеричной строки» в «байтовый массив». :) –

+0

ОК, но это не хорошо названный метод - я не мог видеть, что это гексагон, хотя у меня были подозрения. 'ECNamedCurveTable', похоже, является определяющим классом Bouncy Calstle, правильно? –

2

Это работает для меня:

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

 Смежные вопросы

  • Нет связанных вопросов^_^